From d9233caa4265652a5feb0c5237a4b7419847f1d4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 29 Jan 2025 00:56:37 +0100 Subject: [PATCH 001/392] Fixed issue #1981 --- src/db/db/dbGenericShapeIterator.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/db/db/dbGenericShapeIterator.h b/src/db/db/dbGenericShapeIterator.h index 570cb58e1..4e44681b6 100644 --- a/src/db/db/dbGenericShapeIterator.h +++ b/src/db/db/dbGenericShapeIterator.h @@ -495,7 +495,11 @@ private: void set () { - m_object = db::object_with_properties (*m_basic, m_basic.prop_id ()); + if (! at_end ()) { + m_object = db::object_with_properties (*m_basic, m_basic.prop_id ()); + } else { + m_object = db::object_with_properties (); + } } }; From dfc7d6bef4b5a29776c719900af2d2f98e141541 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 29 Jan 2025 00:59:28 +0100 Subject: [PATCH 002/392] Enhancing Python/Ruby API for better support with properties --- src/db/db/dbAsIfFlatRegion.cc | 3 + src/db/db/dbPropertiesRepository.h | 12 ++++ src/db/db/gsiDeclDbBox.cc | 16 +++++ src/db/db/gsiDeclDbEdge.cc | 16 +++++ src/db/db/gsiDeclDbEdgePair.cc | 16 +++++ src/db/db/gsiDeclDbPath.cc | 16 +++++ src/db/db/gsiDeclDbPolygon.cc | 32 +++++++++ src/db/db/gsiDeclDbRegion.cc | 100 +++++++++++++++++++++++++++++ src/db/db/gsiDeclDbText.cc | 16 +++++ testdata/ruby/dbBoxTest.rb | 6 ++ testdata/ruby/dbEdgePairTest.rb | 6 ++ testdata/ruby/dbEdgeTest.rb | 6 ++ testdata/ruby/dbPathTest.rb | 6 ++ testdata/ruby/dbPolygonTest.rb | 12 ++++ testdata/ruby/dbRegionTest.rb | 44 +++++++++++-- testdata/ruby/dbTextTest.rb | 6 ++ 16 files changed, 309 insertions(+), 4 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index f74273f5a..c4cc80695 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -134,6 +134,9 @@ AsIfFlatRegion::to_string (size_t nmax) const } first = false; os << p->to_string (); + if (p.prop_id () != 0) { + os << db::properties (p.prop_id ()).to_dict_var ().to_string (); + } } if (! p.at_end ()) { os << "..."; diff --git a/src/db/db/dbPropertiesRepository.h b/src/db/db/dbPropertiesRepository.h index ed6848676..a5bbf7a9f 100644 --- a/src/db/db/dbPropertiesRepository.h +++ b/src/db/db/dbPropertiesRepository.h @@ -110,6 +110,18 @@ public: */ PropertiesSet (const PropertiesSet &&other); + /** + * @brief Constructor from tl::Variant pair iterator + */ + template + PropertiesSet (const Iter &from, const Iter &to) + : m_map (), m_hash (0) + { + for (auto i = from; i != to; ++i) { + insert (i->first, i->second); + } + } + /** * @brief Assignment */ diff --git a/src/db/db/gsiDeclDbBox.cc b/src/db/db/gsiDeclDbBox.cc index 1e70cc10b..a1493570a 100644 --- a/src/db/db/gsiDeclDbBox.cc +++ b/src/db/db/gsiDeclDbBox.cc @@ -596,10 +596,18 @@ static db::BoxWithProperties *new_box_with_properties (const db::Box &box, db::p return new db::BoxWithProperties (box, pid); } +static db::BoxWithProperties *new_box_with_properties2 (const db::Box &path, const std::map &properties) +{ + return new db::BoxWithProperties (path, db::properties_id (db::PropertiesSet (properties.begin (), properties.end ()))); +} + Class decl_BoxWithProperties (decl_Box, "db", "BoxWithProperties", gsi::properties_support_methods () + constructor ("new", &new_box_with_properties, gsi::arg ("box"), gsi::arg ("properties_id", db::properties_id_type (0)), "@brief Creates a new object from a property-less object and a properties ID." + ) + + constructor ("new", &new_box_with_properties2, gsi::arg ("box"), gsi::arg ("properties"), + "@brief Creates a new object from a property-less object and a properties hash." ) , "@brief A Box object with properties attached.\n" @@ -670,10 +678,18 @@ static db::DBoxWithProperties *new_dbox_with_properties (const db::DBox &box, db return new db::DBoxWithProperties (box, pid); } +static db::DBoxWithProperties *new_dbox_with_properties2 (const db::DBox &path, const std::map &properties) +{ + return new db::DBoxWithProperties (path, db::properties_id (db::PropertiesSet (properties.begin (), properties.end ()))); +} + Class decl_DBoxWithProperties (decl_DBox, "db", "DBoxWithProperties", gsi::properties_support_methods () + constructor ("new", &new_dbox_with_properties, gsi::arg ("box"), gsi::arg ("properties_id", db::properties_id_type (0)), "@brief Creates a new object from a property-less object and a properties ID." + ) + + constructor ("new", &new_dbox_with_properties2, gsi::arg ("box"), gsi::arg ("properties"), + "@brief Creates a new object from a property-less object and a properties hash." ) , "@brief A DBox object with properties attached.\n" diff --git a/src/db/db/gsiDeclDbEdge.cc b/src/db/db/gsiDeclDbEdge.cc index e981c351d..6bef103c3 100644 --- a/src/db/db/gsiDeclDbEdge.cc +++ b/src/db/db/gsiDeclDbEdge.cc @@ -700,10 +700,18 @@ static db::EdgeWithProperties *new_edge_with_properties (const db::Edge &edge, d return new db::EdgeWithProperties (edge, pid); } +static db::EdgeWithProperties *new_edge_with_properties2 (const db::Edge &edge, const std::map &properties) +{ + return new db::EdgeWithProperties (edge, db::properties_id (db::PropertiesSet (properties.begin (), properties.end ()))); +} + Class decl_EdgeWithProperties (decl_Edge, "db", "EdgeWithProperties", gsi::properties_support_methods () + constructor ("new", &new_edge_with_properties, gsi::arg ("edge"), gsi::arg ("properties_id", db::properties_id_type (0)), "@brief Creates a new object from a property-less object and a properties ID." + ) + + constructor ("new", &new_edge_with_properties2, gsi::arg ("edge"), gsi::arg ("properties"), + "@brief Creates a new object from a property-less object and a properties hash." ) , "@brief A Edge object with properties attached.\n" @@ -765,10 +773,18 @@ static db::DEdgeWithProperties *new_dedge_with_properties (const db::DEdge &edge return new db::DEdgeWithProperties (edge, pid); } +static db::DEdgeWithProperties *new_dedge_with_properties2 (const db::DEdge &edge, const std::map &properties) +{ + return new db::DEdgeWithProperties (edge, db::properties_id (db::PropertiesSet (properties.begin (), properties.end ()))); +} + Class decl_DEdgeWithProperties (decl_DEdge, "db", "DEdgeWithProperties", gsi::properties_support_methods () + constructor ("new", &new_dedge_with_properties, gsi::arg ("edge"), gsi::arg ("properties_id", db::properties_id_type (0)), "@brief Creates a new object from a property-less object and a properties ID." + ) + + constructor ("new", &new_dedge_with_properties2, gsi::arg ("edge"), gsi::arg ("properties"), + "@brief Creates a new object from a property-less object and a properties hash." ) , "@brief A DEdge object with properties attached.\n" diff --git a/src/db/db/gsiDeclDbEdgePair.cc b/src/db/db/gsiDeclDbEdgePair.cc index f3a2c6733..811c46dc5 100644 --- a/src/db/db/gsiDeclDbEdgePair.cc +++ b/src/db/db/gsiDeclDbEdgePair.cc @@ -290,10 +290,18 @@ static db::EdgePairWithProperties *new_edge_pair_with_properties (const db::Edge return new db::EdgePairWithProperties (edge_pair, pid); } +static db::EdgePairWithProperties *new_edge_pair_with_properties2 (const db::EdgePair &edge_pair, const std::map &properties) +{ + return new db::EdgePairWithProperties (edge_pair, db::properties_id (db::PropertiesSet (properties.begin (), properties.end ()))); +} + Class decl_EdgePairWithProperties (decl_EdgePair, "db", "EdgePairWithProperties", gsi::properties_support_methods () + constructor ("new", &new_edge_pair_with_properties, gsi::arg ("edge_pair"), gsi::arg ("properties_id", db::properties_id_type (0)), "@brief Creates a new object from a property-less object and a properties ID." + ) + + constructor ("new", &new_edge_pair_with_properties2, gsi::arg ("edge_pair"), gsi::arg ("properties"), + "@brief Creates a new object from a property-less object and a properties hash." ) , "@brief A EdgePair object with properties attached.\n" @@ -357,10 +365,18 @@ static db::DEdgePairWithProperties *new_dedge_pair_with_properties (const db::DE return new db::DEdgePairWithProperties (edge_pair, pid); } +static db::DEdgePairWithProperties *new_dedge_pair_with_properties2 (const db::DEdgePair &edge_pair, const std::map &properties) +{ + return new db::DEdgePairWithProperties (edge_pair, db::properties_id (db::PropertiesSet (properties.begin (), properties.end ()))); +} + Class decl_DEdgePairWithProperties (decl_EdgePair, "db", "DEdgePairWithProperties", gsi::properties_support_methods () + constructor ("new", &new_dedge_pair_with_properties, gsi::arg ("edge_pair"), gsi::arg ("properties_id", db::properties_id_type (0)), "@brief Creates a new object from a property-less object and a properties ID." + ) + + constructor ("new", &new_dedge_pair_with_properties2, gsi::arg ("edge_pair"), gsi::arg ("properties"), + "@brief Creates a new object from a property-less object and a properties hash." ) , "@brief A DEdgePair object with properties attached.\n" diff --git a/src/db/db/gsiDeclDbPath.cc b/src/db/db/gsiDeclDbPath.cc index c84e2b948..2a1f84665 100644 --- a/src/db/db/gsiDeclDbPath.cc +++ b/src/db/db/gsiDeclDbPath.cc @@ -390,10 +390,18 @@ static db::PathWithProperties *new_path_with_properties (const db::Path &path, d return new db::PathWithProperties (path, pid); } +static db::PathWithProperties *new_path_with_properties2 (const db::Path &path, const std::map &properties) +{ + return new db::PathWithProperties (path, db::properties_id (db::PropertiesSet (properties.begin (), properties.end ()))); +} + Class decl_PathWithProperties (decl_Path, "db", "PathWithProperties", gsi::properties_support_methods () + constructor ("new", &new_path_with_properties, gsi::arg ("path"), gsi::arg ("properties_id", db::properties_id_type (0)), "@brief Creates a new object from a property-less object and a properties ID." + ) + + constructor ("new", &new_path_with_properties2, gsi::arg ("path"), gsi::arg ("properties"), + "@brief Creates a new object from a property-less object and a properties hash." ) , "@brief A Path object with properties attached.\n" @@ -477,10 +485,18 @@ static db::DPathWithProperties *new_dpath_with_properties (const db::DPath &path return new db::DPathWithProperties (path, pid); } +static db::DPathWithProperties *new_dpath_with_properties2 (const db::DPath &path, const std::map &properties) +{ + return new db::DPathWithProperties (path, db::properties_id (db::PropertiesSet (properties.begin (), properties.end ()))); +} + Class decl_DPathWithProperties (decl_DPath, "db", "DPathWithProperties", gsi::properties_support_methods () + constructor ("new", &new_dpath_with_properties, gsi::arg ("path"), gsi::arg ("properties_id", db::properties_id_type (0)), "@brief Creates a new object from a property-less object and a properties ID." + ) + + constructor ("new", &new_dpath_with_properties2, gsi::arg ("path"), gsi::arg ("properties"), + "@brief Creates a new object from a property-less object and a properties hash." ) , "@brief A DPath object with properties attached.\n" diff --git a/src/db/db/gsiDeclDbPolygon.cc b/src/db/db/gsiDeclDbPolygon.cc index a5dcef5c0..636d66119 100644 --- a/src/db/db/gsiDeclDbPolygon.cc +++ b/src/db/db/gsiDeclDbPolygon.cc @@ -791,10 +791,18 @@ static db::SimplePolygonWithProperties *new_simple_polygon_with_properties (cons return new db::SimplePolygonWithProperties (poly, pid); } +static db::SimplePolygonWithProperties *new_simple_polygon_with_properties2 (const db::SimplePolygon &poly, const std::map &properties) +{ + return new db::SimplePolygonWithProperties (poly, db::properties_id (db::PropertiesSet (properties.begin (), properties.end ()))); +} + Class decl_SimplePolygonWithProperties (decl_SimplePolygon, "db", "SimplePolygonWithProperties", gsi::properties_support_methods () + constructor ("new", &new_simple_polygon_with_properties, gsi::arg ("polygon"), gsi::arg ("properties_id", db::properties_id_type (0)), "@brief Creates a new object from a property-less object and a properties ID." + ) + + constructor ("new", &new_simple_polygon_with_properties2, gsi::arg ("polygon"), gsi::arg ("properties"), + "@brief Creates a new object from a property-less object and a properties hash." ) , "@brief A SimplePolygon object with properties attached.\n" @@ -878,10 +886,18 @@ static db::DSimplePolygonWithProperties *new_dsimple_polygon_with_properties (co return new db::DSimplePolygonWithProperties (poly, pid); } +static db::DSimplePolygonWithProperties *new_dsimple_polygon_with_properties2 (const db::DSimplePolygon &poly, const std::map &properties) +{ + return new db::DSimplePolygonWithProperties (poly, db::properties_id (db::PropertiesSet (properties.begin (), properties.end ()))); +} + Class decl_DSimplePolygonWithProperties (decl_DSimplePolygon, "db", "DSimplePolygonWithProperties", gsi::properties_support_methods () + constructor ("new", &new_dsimple_polygon_with_properties, gsi::arg ("polygon"), gsi::arg ("properties_id", db::properties_id_type (0)), "@brief Creates a new object from a property-less object and a properties ID." + ) + + constructor ("new", &new_dsimple_polygon_with_properties2, gsi::arg ("polygon"), gsi::arg ("properties"), + "@brief Creates a new object from a property-less object and a properties hash." ) , "@brief A DSimplePolygon object with properties attached.\n" @@ -2070,10 +2086,18 @@ static db::PolygonWithProperties *new_polygon_with_properties (const db::Polygon return new db::PolygonWithProperties (poly, pid); } +static db::PolygonWithProperties *new_polygon_with_properties2 (const db::Polygon &poly, const std::map &properties) +{ + return new db::PolygonWithProperties (poly, db::properties_id (db::PropertiesSet (properties.begin (), properties.end ()))); +} + Class decl_PolygonWithProperties (decl_Polygon, "db", "PolygonWithProperties", gsi::properties_support_methods () + constructor ("new", &new_polygon_with_properties, gsi::arg ("polygon"), gsi::arg ("properties_id", db::properties_id_type (0)), "@brief Creates a new object from a property-less object and a properties ID." + ) + + constructor ("new", &new_polygon_with_properties2, gsi::arg ("polygon"), gsi::arg ("properties"), + "@brief Creates a new object from a property-less object and a properties hash." ) , "@brief A Polygon object with properties attached.\n" @@ -2184,10 +2208,18 @@ static db::DPolygonWithProperties *new_dpolygon_with_properties (const db::DPoly return new db::DPolygonWithProperties (poly, pid); } +static db::DPolygonWithProperties *new_dpolygon_with_properties2 (const db::DPolygon &poly, const std::map &properties) +{ + return new db::DPolygonWithProperties (poly, db::properties_id (db::PropertiesSet (properties.begin (), properties.end ()))); +} + Class decl_DPolygonWithProperties (decl_DPolygon, "db", "DPolygonWithProperties", gsi::properties_support_methods () + constructor ("new", &new_dpolygon_with_properties, gsi::arg ("polygon"), gsi::arg ("properties_id", db::properties_id_type (0)), "@brief Creates a new object from a property-less object and a properties ID." + ) + + constructor ("new", &new_dpolygon_with_properties2, gsi::arg ("polygon"), gsi::arg ("properties"), + "@brief Creates a new object from a property-less object and a properties hash." ) , "@brief A DPolygon object with properties attached.\n" diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 9294f23d4..9ea181a82 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -250,26 +250,51 @@ static db::Region *new_a (const std::vector &a) return new db::Region (a.begin (), a.end ()); } +static db::Region *new_ap (const std::vector &a) +{ + return new db::Region (a.begin (), a.end ()); +} + static db::Region *new_b (const db::Box &o) { return new db::Region (o); } +static db::Region *new_bp (const db::BoxWithProperties &o) +{ + return new db::Region (o); +} + static db::Region *new_p (const db::Polygon &o) { return new db::Region (o); } +static db::Region *new_pp (const db::PolygonWithProperties &o) +{ + return new db::Region (o); +} + static db::Region *new_ps (const db::SimplePolygon &o) { return new db::Region (o); } +static db::Region *new_psp (const db::SimplePolygonWithProperties &o) +{ + return new db::Region (o); +} + static db::Region *new_path (const db::Path &o) { return new db::Region (o); } +static db::Region *new_pathp (const db::PathWithProperties &o) +{ + return new db::Region (o); +} + static db::Region *new_texts_as_boxes1 (const db::RecursiveShapeIterator &si, const std::string &pat, bool pattern, db::Coord enl) { return new db::Region (db::Region (si).texts_as_boxes (pat, pattern, enl)); @@ -382,6 +407,13 @@ static void insert_a (db::Region *r, const std::vector &a) } } +static void insert_ap (db::Region *r, const std::vector &a) +{ + for (std::vector ::const_iterator p = a.begin (); p != a.end (); ++p) { + r->insert (*p); + } +} + static void insert_r (db::Region *r, const db::Region &a) { for (db::Region::const_iterator p = a.begin (); ! p.at_end (); ++p) { @@ -1170,26 +1202,61 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This constructor creates a region from an array of polygons.\n" ) + + constructor ("new", &new_ap, gsi::arg ("array"), + "@brief Constructor from an array of polygons with properties\n" + "\n" + "This constructor creates a region from an array of polygons with properties.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + constructor ("new", &new_b, gsi::arg ("box"), "@brief Box constructor\n" "\n" "This constructor creates a region from a box.\n" ) + + constructor ("new", &new_bp, gsi::arg ("box"), + "@brief Box constructor\n" + "\n" + "This constructor creates a region from a box with properties.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + constructor ("new", &new_p, gsi::arg ("polygon"), "@brief Polygon constructor\n" "\n" "This constructor creates a region from a polygon.\n" ) + + constructor ("new", &new_pp, gsi::arg ("polygon"), + "@brief Polygon constructor\n" + "\n" + "This constructor creates a region from a polygon with properties.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + constructor ("new", &new_ps, gsi::arg ("polygon"), "@brief Simple polygon constructor\n" "\n" "This constructor creates a region from a simple polygon.\n" ) + + constructor ("new", &new_psp, gsi::arg ("polygon"), + "@brief Simple polygon constructor\n" + "\n" + "This constructor creates a region from a simple polygon with properties.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + constructor ("new", &new_path, gsi::arg ("path"), "@brief Path constructor\n" "\n" "This constructor creates a region from a path.\n" ) + + constructor ("new", &new_pathp, gsi::arg ("path"), + "@brief Path constructor\n" + "\n" + "This constructor creates a region from a path with properties.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + constructor ("new", &new_si, gsi::arg ("shape_iterator"), "@brief Constructor from a hierarchical shape set\n" "\n" @@ -1697,21 +1764,49 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "Inserts a box into the region.\n" ) + + method ("insert", (void (db::Region::*)(const db::BoxWithProperties &)) &db::Region::insert, gsi::arg ("box"), + "@brief Inserts a box\n" + "\n" + "Inserts a box with properties into the region.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + method ("insert", (void (db::Region::*)(const db::Polygon &)) &db::Region::insert, gsi::arg ("polygon"), "@brief Inserts a polygon\n" "\n" "Inserts a polygon into the region.\n" ) + + method ("insert", (void (db::Region::*)(const db::PolygonWithProperties &)) &db::Region::insert, gsi::arg ("polygon"), + "@brief Inserts a polygon\n" + "\n" + "Inserts a polygon with properties into the region.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + method ("insert", (void (db::Region::*)(const db::SimplePolygon &)) &db::Region::insert, gsi::arg ("polygon"), "@brief Inserts a simple polygon\n" "\n" "Inserts a simple polygon into the region.\n" ) + + method ("insert", (void (db::Region::*)(const db::SimplePolygonWithProperties &)) &db::Region::insert, gsi::arg ("polygon"), + "@brief Inserts a simple polygon\n" + "\n" + "Inserts a simple polygon with properties into the region.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + method ("insert", (void (db::Region::*)(const db::Path &)) &db::Region::insert, gsi::arg ("path"), "@brief Inserts a path\n" "\n" "Inserts a path into the region.\n" ) + + method ("insert", (void (db::Region::*)(const db::PathWithProperties &)) &db::Region::insert, gsi::arg ("path"), + "@brief Inserts a path\n" + "\n" + "Inserts a path with properties into the region.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + method_ext ("insert", &insert_si, gsi::arg ("shape_iterator"), "@brief Inserts all shapes delivered by the recursive shape iterator into this region\n" "\n" @@ -1729,6 +1824,11 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", method_ext ("insert", &insert_a, gsi::arg ("array"), "@brief Inserts all polygons from the array into this region\n" ) + + method_ext ("insert", &insert_ap, gsi::arg ("array"), + "@brief Inserts all polygons with properties from the array into this region\n" + "\n" + "This variant has been introduced in version 0.30." + ) + method_ext ("insert", &insert_r, gsi::arg ("region"), "@brief Inserts all polygons from the other region into this region\n" "This method has been introduced in version 0.25." diff --git a/src/db/db/gsiDeclDbText.cc b/src/db/db/gsiDeclDbText.cc index b8a669c97..ec665c9d5 100644 --- a/src/db/db/gsiDeclDbText.cc +++ b/src/db/db/gsiDeclDbText.cc @@ -454,10 +454,18 @@ static db::TextWithProperties *new_text_with_properties (const db::Text &text, d return new db::TextWithProperties (text, pid); } +static db::TextWithProperties *new_text_with_properties2 (const db::Text &text, const std::map &properties) +{ + return new db::TextWithProperties (text, db::properties_id (db::PropertiesSet (properties.begin (), properties.end ()))); +} + Class decl_TextWithProperties (decl_Text, "db", "TextWithProperties", gsi::properties_support_methods () + constructor ("new", &new_text_with_properties, gsi::arg ("text"), gsi::arg ("properties_id", db::properties_id_type (0)), "@brief Creates a new object from a property-less object and a properties ID." + ) + + constructor ("new", &new_text_with_properties2, gsi::arg ("text"), gsi::arg ("properties"), + "@brief Creates a new object from a property-less object and a properties hash." ) , "@brief A Text object with properties attached.\n" @@ -522,10 +530,18 @@ static db::DTextWithProperties *new_dtext_with_properties (const db::DText &text return new db::DTextWithProperties (text, pid); } +static db::DTextWithProperties *new_dtext_with_properties2 (const db::DText &text, const std::map &properties) +{ + return new db::DTextWithProperties (text, db::properties_id (db::PropertiesSet (properties.begin (), properties.end ()))); +} + Class decl_DTextWithProperties (decl_DText, "db", "DTextWithProperties", gsi::properties_support_methods () + constructor ("new", &new_dtext_with_properties, gsi::arg ("text"), gsi::arg ("properties_id", db::properties_id_type (0)), "@brief Creates a new object from a property-less object and a properties ID." + ) + + constructor ("new", &new_dtext_with_properties2, gsi::arg ("text"), gsi::arg ("properties"), + "@brief Creates a new object from a property-less object and a properties hash." ) , "@brief A DText object with properties attached.\n" diff --git a/testdata/ruby/dbBoxTest.rb b/testdata/ruby/dbBoxTest.rb index e7b630d59..a631af0e3 100644 --- a/testdata/ruby/dbBoxTest.rb +++ b/testdata/ruby/dbBoxTest.rb @@ -517,6 +517,9 @@ class DBBox_TestClass < TestBase s = RBA::BoxWithProperties::new assert_equal(s.to_s, "() props={}") + s = RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" }) + assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") + pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), pid) assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") @@ -532,6 +535,9 @@ class DBBox_TestClass < TestBase s = RBA::DBoxWithProperties::new assert_equal(s.to_s, "() props={}") + s = RBA::DBoxWithProperties::new(RBA::DBox::new(0, 0, 100, 200), { 1 => "one" }) + assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") + pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::DBoxWithProperties::new(RBA::DBox::new(0, 0, 100, 200), pid) assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") diff --git a/testdata/ruby/dbEdgePairTest.rb b/testdata/ruby/dbEdgePairTest.rb index 3c5ccbdb8..0b0282c27 100644 --- a/testdata/ruby/dbEdgePairTest.rb +++ b/testdata/ruby/dbEdgePairTest.rb @@ -251,6 +251,9 @@ class DBEdgePair_TestClass < TestBase s = RBA::EdgePairWithProperties::new assert_equal(s.to_s, "(0,0;0,0)/(0,0;0,0) props={}") + s = RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 200), RBA::Edge::new(10, 10, 110, 210)), { 1 => "one" }) + assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={1=>one}") + pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 200), RBA::Edge::new(10, 10, 110, 210)), pid) assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={1=>one}") @@ -266,6 +269,9 @@ class DBEdgePair_TestClass < TestBase s = RBA::DEdgePairWithProperties::new assert_equal(s.to_s, "(0,0;0,0)/(0,0;0,0) props={}") + s = RBA::DEdgePairWithProperties::new(RBA::DEdgePair::new(RBA::DEdge::new(0, 0, 100, 200), RBA::DEdge::new(10, 10, 110, 210)), { 1 => "one" }) + assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={1=>one}") + pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::DEdgePairWithProperties::new(RBA::DEdgePair::new(RBA::DEdge::new(0, 0, 100, 200), RBA::DEdge::new(10, 10, 110, 210)), pid) assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={1=>one}") diff --git a/testdata/ruby/dbEdgeTest.rb b/testdata/ruby/dbEdgeTest.rb index 67074a073..63a1074e3 100644 --- a/testdata/ruby/dbEdgeTest.rb +++ b/testdata/ruby/dbEdgeTest.rb @@ -338,6 +338,9 @@ class DBEdge_TestClass < TestBase s = RBA::EdgeWithProperties::new assert_equal(s.to_s, "(0,0;0,0) props={}") + s = RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 200), { 1 => "one" }) + assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") + pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 200), pid) assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") @@ -368,6 +371,9 @@ class DBEdge_TestClass < TestBase s = RBA::DEdgeWithProperties::new assert_equal(s.to_s, "(0,0;0,0) props={}") + s = RBA::DEdgeWithProperties::new(RBA::DEdge::new(0, 0, 100, 200), { 1 => "one" }) + assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") + pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::DEdgeWithProperties::new(RBA::DEdge::new(0, 0, 100, 200), pid) assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") diff --git a/testdata/ruby/dbPathTest.rb b/testdata/ruby/dbPathTest.rb index 9e9924c08..ea0c2bffe 100644 --- a/testdata/ruby/dbPathTest.rb +++ b/testdata/ruby/dbPathTest.rb @@ -334,6 +334,9 @@ class DBPath_TestClass < TestBase s = RBA::PathWithProperties::new assert_equal(s.to_s, "() w=0 bx=0 ex=0 r=false props={}") + s = RBA::PathWithProperties::new(RBA::Path::new([ [0,0], [100, 0] ], 100), { 1 => "one" }) + assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={1=>one}") + pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::PathWithProperties::new(RBA::Path::new([ [0,0], [100, 0] ], 100), pid) assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={1=>one}") @@ -349,6 +352,9 @@ class DBPath_TestClass < TestBase s = RBA::DPathWithProperties::new assert_equal(s.to_s, "() w=0 bx=0 ex=0 r=false props={}") + s = RBA::DPathWithProperties::new(RBA::DPath::new([ [0,0], [100, 0] ], 100), { 1 => "one" }) + assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={1=>one}") + pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::DPathWithProperties::new(RBA::DPath::new([ [0,0], [100, 0] ], 100), pid) assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={1=>one}") diff --git a/testdata/ruby/dbPolygonTest.rb b/testdata/ruby/dbPolygonTest.rb index d9bf6f8c4..5c96489cd 100644 --- a/testdata/ruby/dbPolygonTest.rb +++ b/testdata/ruby/dbPolygonTest.rb @@ -899,6 +899,9 @@ class DBPolygon_TestClass < TestBase s = RBA::PolygonWithProperties::new assert_equal(s.to_s, "() props={}") + s = RBA::PolygonWithProperties::new(RBA::Polygon::new(RBA::Box::new(0, 0, 100, 200)), { 1 => "one" }) + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") + pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::PolygonWithProperties::new(RBA::Polygon::new(RBA::Box::new(0, 0, 100, 200)), pid) assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") @@ -914,6 +917,9 @@ class DBPolygon_TestClass < TestBase s = RBA::DPolygonWithProperties::new assert_equal(s.to_s, "() props={}") + s = RBA::DPolygonWithProperties::new(RBA::DPolygon::new(RBA::DBox::new(0, 0, 100, 200)), { 1 => "one" }) + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") + pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::DPolygonWithProperties::new(RBA::DPolygon::new(RBA::DBox::new(0, 0, 100, 200)), pid) assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") @@ -929,6 +935,9 @@ class DBPolygon_TestClass < TestBase s = RBA::SimplePolygonWithProperties::new assert_equal(s.to_s, "() props={}") + s = RBA::SimplePolygonWithProperties::new(RBA::SimplePolygon::new(RBA::Box::new(0, 0, 100, 200)), { 1 => "one" }) + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") + pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::SimplePolygonWithProperties::new(RBA::SimplePolygon::new(RBA::Box::new(0, 0, 100, 200)), pid) assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") @@ -944,6 +953,9 @@ class DBPolygon_TestClass < TestBase s = RBA::DSimplePolygonWithProperties::new assert_equal(s.to_s, "() props={}") + s = RBA::DSimplePolygonWithProperties::new(RBA::DSimplePolygon::new(RBA::DBox::new(0, 0, 100, 200)), { 1 => "one" }) + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") + pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::DSimplePolygonWithProperties::new(RBA::DSimplePolygon::new(RBA::DBox::new(0, 0, 100, 200)), pid) assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index ea7b13feb..116fb190c 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -1280,12 +1280,12 @@ class DBRegion_TestClass < TestBase assert_equal(csort((r & rr).to_s), csort("(0,0;0,100;100,100;100,0);(200,0;200,100;300,100;300,0);(400,0;400,100;500,100;500,0)")) assert_equal(csort(r.and(rr).to_s), csort("(0,0;0,100;100,100;100,0);(200,0;200,100;300,100;300,0);(400,0;400,100;500,100;500,0)")) - assert_equal(csort(r.and(rr, RBA::Region::NoPropertyConstraint).to_s), csort("(0,0;0,100;100,100;100,0);(200,0;200,100;300,100;300,0);(400,0;400,100;500,100;500,0)")) - assert_equal(csort(r.and(rr, RBA::Region::SamePropertiesConstraint).to_s), csort("(0,0;0,50;100,50;100,0);(400,50;400,100;500,100;500,50)")) - assert_equal(csort(r.and(rr, RBA::Region::DifferentPropertiesConstraint).to_s), csort("(0,50;0,100;100,100;100,50);(200,0;200,100;300,100;300,0);(400,0;400,50;500,50;500,0)")) + assert_equal(csort(r.and(rr, RBA::Region::NoPropertyConstraint).to_s), csort("(200,0;200,100;300,100;300,0){1=>42};(0,0;0,100;100,100;100,0){1=>17};(400,0;400,100;500,100;500,0)")) + assert_equal(csort(r.and(rr, RBA::Region::SamePropertiesConstraint).to_s), csort("(0,0;0,50;100,50;100,0){1=>17};(400,50;400,100;500,100;500,50)")) + assert_equal(csort(r.and(rr, RBA::Region::DifferentPropertiesConstraint).to_s), csort("(200,0;200,100;300,100;300,0){1=>42};(0,50;0,100;100,100;100,50){1=>17};(400,0;400,50;500,50;500,0)")) assert_equal(csort(r.not(rr).to_s), csort("(0,100;0,200;100,200;100,100);(200,100;200,200;300,200;300,100);(400,100;400,200;500,200;500,100)")) - assert_equal(csort(r.not(rr, RBA::Region::SamePropertiesConstraint).to_s), csort("(0,50;0,200;100,200;100,50);(400,100;400,200;500,200;500,100);(200,0;200,200;300,200;300,0);(400,0;400,50;500,50;500,0)")) + assert_equal(csort(r.not(rr, RBA::Region::SamePropertiesConstraint).to_s), csort("(200,0;200,200;300,200;300,0){1=>42};(0,50;0,200;100,200;100,50){1=>17};(400,100;400,200;500,200;500,100);(400,0;400,50;500,50;500,0)")) r.remove_properties rr.remove_properties @@ -1528,6 +1528,42 @@ class DBRegion_TestClass < TestBase end + # properties + def test_props + + r = RBA::Region::new([ RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" }) ]) + assert_equal(r.to_s, "(0,0;0,200;100,200;100,0){1=>one}") + + r = RBA::Region::new + r.insert([ RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" }) ]) + assert_equal(r.to_s, "(0,0;0,200;100,200;100,0){1=>one}") + + r = RBA::Region::new(RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" })) + assert_equal(r.to_s, "(0,0;0,200;100,200;100,0){1=>one}") + + r = RBA::Region::new + r.insert(RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" })) + assert_equal(r.to_s, "(0,0;0,200;100,200;100,0){1=>one}") + + r = RBA::Region::new(RBA::SimplePolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" })) + assert_equal(r.to_s, "(0,0;0,200;100,200;100,0){1=>one}") + + r = RBA::Region::new + r.insert(RBA::SimplePolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" })) + assert_equal(r.to_s, "(0,0;0,200;100,200;100,0){1=>one}") + + r = RBA::Region::new(RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" })) + assert_equal(r.to_s, "(0,0;0,200;100,200;100,0){1=>one}") + + r = RBA::Region::new + r.insert(RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" })) + assert_equal(r.to_s, "(0,0;0,200;100,200;100,0){1=>one}") + + r = RBA::Region::new(RBA::PathWithProperties::new(RBA::Path::new([ RBA::Point::new(0, 0), RBA::Point::new(100, 0) ], 20), { 1 => "one" })) + assert_equal(r.to_s, "(0,-10;0,10;100,10;100,-10){1=>one}") + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/dbTextTest.rb b/testdata/ruby/dbTextTest.rb index 892b99042..b3f08d06e 100644 --- a/testdata/ruby/dbTextTest.rb +++ b/testdata/ruby/dbTextTest.rb @@ -259,6 +259,9 @@ class DBText_TestClass < TestBase s = RBA::TextWithProperties::new assert_equal(s.to_s, "('',r0 0,0) props={}") + s = RBA::TextWithProperties::new(RBA::Text::new("text", RBA::Trans::R90), { 1 => "one" }) + assert_equal(s.to_s, "('text',r90 0,0) props={1=>one}") + pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::TextWithProperties::new(RBA::Text::new("text", RBA::Trans::R90), pid) assert_equal(s.to_s, "('text',r90 0,0) props={1=>one}") @@ -274,6 +277,9 @@ class DBText_TestClass < TestBase s = RBA::DTextWithProperties::new assert_equal(s.to_s, "('',r0 0,0) props={}") + s = RBA::DTextWithProperties::new(RBA::DText::new("text", RBA::Trans::R90), { 1 => "one" }) + assert_equal(s.to_s, "('text',r90 0,0) props={1=>one}") + pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::DTextWithProperties::new(RBA::DText::new("text", RBA::Trans::R90), pid) assert_equal(s.to_s, "('text',r90 0,0) props={1=>one}") From 375ac6364b7e368021fb92e4c2dfcf0b448edf45 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 29 Jan 2025 01:59:00 +0100 Subject: [PATCH 003/392] More support for properties inside Edges, Texts, EdgePairs. WIP --- src/db/db/dbAsIfFlatEdgePairs.cc | 3 + src/db/db/dbAsIfFlatEdges.cc | 3 + src/db/db/dbAsIfFlatTexts.cc | 3 + src/db/db/dbDeepEdgePairs.cc | 8 +- src/db/db/dbDeepEdgePairs.h | 2 +- src/db/db/dbDeepTexts.cc | 8 +- src/db/db/dbDeepTexts.h | 2 +- src/db/db/dbEdgePairs.cc | 1 + src/db/db/dbEdgePairs.h | 11 ++ src/db/db/dbEdges.h | 55 +++++++++ src/db/db/dbFlatEdgePairs.cc | 8 +- src/db/db/dbFlatEdgePairs.h | 2 +- src/db/db/dbFlatTexts.cc | 8 +- src/db/db/dbFlatTexts.h | 2 +- src/db/db/dbMutableEdgePairs.h | 7 +- src/db/db/dbMutableTexts.h | 7 +- src/db/db/dbTexts.cc | 1 + src/db/db/dbTexts.h | 11 ++ src/db/db/gsiDeclDbEdgePairs.cc | 29 +++++ src/db/db/gsiDeclDbEdges.cc | 174 +++++++++++++++++++++++++++- src/db/db/gsiDeclDbTexts.cc | 33 +++++- testdata/ruby/dbEdgeNeighborhood.rb | 2 +- testdata/ruby/dbEdgePairsTest.rb | 15 +++ testdata/ruby/dbRegionTest.rb | 6 +- testdata/ruby/dbTextsTest.rb | 15 +++ 25 files changed, 388 insertions(+), 28 deletions(-) diff --git a/src/db/db/dbAsIfFlatEdgePairs.cc b/src/db/db/dbAsIfFlatEdgePairs.cc index c733966b9..568ea3efb 100644 --- a/src/db/db/dbAsIfFlatEdgePairs.cc +++ b/src/db/db/dbAsIfFlatEdgePairs.cc @@ -81,6 +81,9 @@ AsIfFlatEdgePairs::to_string (size_t nmax) const } first = false; os << p->to_string (); + if (p.prop_id () != 0) { + os << db::properties (p.prop_id ()).to_dict_var ().to_string (); + } } if (! p.at_end ()) { os << "..."; diff --git a/src/db/db/dbAsIfFlatEdges.cc b/src/db/db/dbAsIfFlatEdges.cc index 3a09e4bc0..6a4b51e06 100644 --- a/src/db/db/dbAsIfFlatEdges.cc +++ b/src/db/db/dbAsIfFlatEdges.cc @@ -89,6 +89,9 @@ AsIfFlatEdges::to_string (size_t nmax) const } first = false; os << p->to_string (); + if (p.prop_id () != 0) { + os << db::properties (p.prop_id ()).to_dict_var ().to_string (); + } } if (! p.at_end ()) { os << "..."; diff --git a/src/db/db/dbAsIfFlatTexts.cc b/src/db/db/dbAsIfFlatTexts.cc index c5498fd64..56d5cc803 100644 --- a/src/db/db/dbAsIfFlatTexts.cc +++ b/src/db/db/dbAsIfFlatTexts.cc @@ -79,6 +79,9 @@ AsIfFlatTexts::to_string (size_t nmax) const } first = false; os << p->to_string (); + if (p.prop_id () != 0) { + os << db::properties (p.prop_id ()).to_dict_var ().to_string (); + } } if (! p.at_end ()) { os << "..."; diff --git a/src/db/db/dbDeepEdgePairs.cc b/src/db/db/dbDeepEdgePairs.cc index 80c4f752a..16cb80de9 100644 --- a/src/db/db/dbDeepEdgePairs.cc +++ b/src/db/db/dbDeepEdgePairs.cc @@ -161,12 +161,16 @@ EdgePairsDelegate *DeepEdgePairs::clone () const return new DeepEdgePairs (*this); } -void DeepEdgePairs::do_insert (const db::EdgePair &edge_pair) +void DeepEdgePairs::do_insert (const db::EdgePair &edge_pair, db::properties_id_type prop_id) { db::Layout &layout = deep_layer ().layout (); if (layout.begin_top_down () != layout.end_top_down ()) { db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); - top_cell.shapes (deep_layer ().layer ()).insert (edge_pair); + if (prop_id != 0) { + top_cell.shapes (deep_layer ().layer ()).insert (edge_pair); + } else { + top_cell.shapes (deep_layer ().layer ()).insert (db::EdgePairWithProperties (edge_pair, prop_id)); + } } invalidate_bbox (); diff --git a/src/db/db/dbDeepEdgePairs.h b/src/db/db/dbDeepEdgePairs.h index b8d346148..20397185a 100644 --- a/src/db/db/dbDeepEdgePairs.h +++ b/src/db/db/dbDeepEdgePairs.h @@ -50,7 +50,7 @@ public: EdgePairsDelegate *clone () const; - virtual void do_insert (const db::EdgePair &edge_pair); + virtual void do_insert (const db::EdgePair &edge_pair, db::properties_id_type prop_id); virtual void do_transform (const db::Trans &t); virtual void do_transform (const db::ICplxTrans &t); diff --git a/src/db/db/dbDeepTexts.cc b/src/db/db/dbDeepTexts.cc index 6a751cec2..b542a5e0a 100644 --- a/src/db/db/dbDeepTexts.cc +++ b/src/db/db/dbDeepTexts.cc @@ -178,12 +178,16 @@ TextsDelegate *DeepTexts::clone () const return new DeepTexts (*this); } -void DeepTexts::do_insert (const db::Text &text) +void DeepTexts::do_insert (const db::Text &text, db::properties_id_type prop_id) { db::Layout &layout = deep_layer ().layout (); if (layout.begin_top_down () != layout.end_top_down ()) { db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); - top_cell.shapes (deep_layer ().layer ()).insert (db::TextRef (text, layout.shape_repository ())); + if (prop_id != 0) { + top_cell.shapes (deep_layer ().layer ()).insert (db::TextRef (text, layout.shape_repository ())); + } else { + top_cell.shapes (deep_layer ().layer ()).insert (db::TextRefWithProperties (db::TextRef (text, layout.shape_repository ()), prop_id)); + } } invalidate_bbox (); diff --git a/src/db/db/dbDeepTexts.h b/src/db/db/dbDeepTexts.h index 5917e142f..e34a61284 100644 --- a/src/db/db/dbDeepTexts.h +++ b/src/db/db/dbDeepTexts.h @@ -51,7 +51,7 @@ public: TextsDelegate *clone () const; - virtual void do_insert (const db::Text &text); + virtual void do_insert (const db::Text &text, properties_id_type prop_id); virtual void do_transform (const db::Trans &t); virtual void do_transform (const db::ICplxTrans &t); diff --git a/src/db/db/dbEdgePairs.cc b/src/db/db/dbEdgePairs.cc index 7f388b513..68f3f64c7 100644 --- a/src/db/db/dbEdgePairs.cc +++ b/src/db/db/dbEdgePairs.cc @@ -120,6 +120,7 @@ void EdgePairs::insert (const Sh &shape) } template DB_PUBLIC void EdgePairs::insert (const db::EdgePair &); +template DB_PUBLIC void EdgePairs::insert (const db::EdgePairWithProperties &); void EdgePairs::insert (const db::Shape &shape) { diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index 5333e1b06..723885d08 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -128,6 +128,17 @@ public: insert (s); } + /** + * @brief Constructor from an object with properties + * + * Creates an edge pair set representing a single instance of that object + */ + explicit EdgePairs (const db::EdgePairWithProperties &s) + : mp_delegate (0) + { + insert (s); + } + /** * @brief Constructor from an object * diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index 649ded1c3..db2398164 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -112,6 +112,17 @@ public: insert (s); } + /** + * @brief Constructor from a box with properties + * + * Creates an edge set representing the contour of the box + */ + explicit Edges (const db::BoxWithProperties &s) + : mp_delegate (0) + { + insert (s); + } + /** * @brief Constructor from a simple polygon * @@ -123,6 +134,17 @@ public: insert (s); } + /** + * @brief Constructor from a simple polygon with properties + * + * Creates an edge set representing the contour of the polygon + */ + explicit Edges (const db::SimplePolygonWithProperties &s) + : mp_delegate (0) + { + insert (s); + } + /** * @brief Constructor from a polygon * @@ -134,6 +156,17 @@ public: insert (s); } + /** + * @brief Constructor from a polygon with properties + * + * Creates an edge set representing the contour of the polygon + */ + explicit Edges (const db::PolygonWithProperties &s) + : mp_delegate (0) + { + insert (s); + } + /** * @brief Constructor from a path * @@ -145,6 +178,17 @@ public: insert (s); } + /** + * @brief Constructor from a path with properties + * + * Creates an edge set representing the contour of the path + */ + explicit Edges (const db::PathWithProperties &s) + : mp_delegate (0) + { + insert (s); + } + /** * @brief Constructor from an edge * @@ -156,6 +200,17 @@ public: insert (s); } + /** + * @brief Constructor from an edge with properties + * + * Creates an edge set representing the single edge + */ + explicit Edges (const db::EdgeWithProperties &s) + : mp_delegate (0) + { + insert (s); + } + /** * @brief Sequence constructor * diff --git a/src/db/db/dbFlatEdgePairs.cc b/src/db/db/dbFlatEdgePairs.cc index f19463faa..e6b9808ac 100644 --- a/src/db/db/dbFlatEdgePairs.cc +++ b/src/db/db/dbFlatEdgePairs.cc @@ -221,9 +221,13 @@ FlatEdgePairs::insert_into (Layout *layout, db::cell_index_type into_cell, unsig } void -FlatEdgePairs::do_insert (const db::EdgePair &ep) +FlatEdgePairs::do_insert (const db::EdgePair &ep, db::properties_id_type prop_id) { - mp_edge_pairs->insert (ep); + if (prop_id != 0) { + mp_edge_pairs->insert (db::EdgePairWithProperties (ep, prop_id)); + } else { + mp_edge_pairs->insert (ep); + } invalidate_cache (); } diff --git a/src/db/db/dbFlatEdgePairs.h b/src/db/db/dbFlatEdgePairs.h index 793e59b90..dc38dc088 100644 --- a/src/db/db/dbFlatEdgePairs.h +++ b/src/db/db/dbFlatEdgePairs.h @@ -83,7 +83,7 @@ public: 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; - virtual void do_insert (const db::EdgePair &edge_pair); + virtual void do_insert (const db::EdgePair &edge_pair, db::properties_id_type prop_id); virtual void do_transform (const db::Trans &t) { diff --git a/src/db/db/dbFlatTexts.cc b/src/db/db/dbFlatTexts.cc index 6b9297e20..4028812b0 100644 --- a/src/db/db/dbFlatTexts.cc +++ b/src/db/db/dbFlatTexts.cc @@ -219,9 +219,13 @@ FlatTexts::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned } void -FlatTexts::do_insert (const db::Text &t) +FlatTexts::do_insert (const db::Text &t, db::properties_id_type prop_id) { - mp_texts->insert (t); + if (prop_id != 0) { + mp_texts->insert (db::TextWithProperties (t, prop_id)); + } else { + mp_texts->insert (t); + } invalidate_cache (); } diff --git a/src/db/db/dbFlatTexts.h b/src/db/db/dbFlatTexts.h index 0fc3a5e92..f7e5ff16c 100644 --- a/src/db/db/dbFlatTexts.h +++ b/src/db/db/dbFlatTexts.h @@ -86,7 +86,7 @@ public: virtual void flatten () { } - void do_insert (const db::Text &text); + void do_insert (const db::Text &text, properties_id_type prop_id); virtual void do_transform (const db::Trans &t) { diff --git a/src/db/db/dbMutableEdgePairs.h b/src/db/db/dbMutableEdgePairs.h index 447767941..33703cbe0 100644 --- a/src/db/db/dbMutableEdgePairs.h +++ b/src/db/db/dbMutableEdgePairs.h @@ -45,7 +45,7 @@ public: MutableEdgePairs (const MutableEdgePairs &other); virtual ~MutableEdgePairs (); - virtual void do_insert (const db::EdgePair &edge_pair) = 0; + virtual void do_insert (const db::EdgePair &edge_pair, db::properties_id_type prop_id) = 0; virtual void do_transform (const db::Trans &t) = 0; virtual void do_transform (const db::ICplxTrans &t) = 0; @@ -63,7 +63,8 @@ public: void transform (const db::IMatrix2d &t) { do_transform (t); } void transform (const db::IMatrix3d &t) { do_transform (t); } - void insert (const db::EdgePair &edge_pair) { do_insert (edge_pair); } + void insert (const db::EdgePair &edge_pair) { do_insert (edge_pair, 0); } + void insert (const db::EdgePairWithProperties &edge_pair) { do_insert (edge_pair, edge_pair.properties_id ()); } void insert (const db::Shape &shape); template @@ -72,7 +73,7 @@ public: if (shape.is_edge_pair ()) { db::EdgePair ep = shape.edge_pair (); ep.transform (trans); - insert (ep); + do_insert (ep, shape.prop_id ()); } } diff --git a/src/db/db/dbMutableTexts.h b/src/db/db/dbMutableTexts.h index ced0caba5..6d850f3ef 100644 --- a/src/db/db/dbMutableTexts.h +++ b/src/db/db/dbMutableTexts.h @@ -45,7 +45,7 @@ public: MutableTexts (const MutableTexts &other); virtual ~MutableTexts (); - virtual void do_insert (const db::Text &text) = 0; + virtual void do_insert (const db::Text &text, db::properties_id_type prop_id) = 0; virtual void do_transform (const db::Trans &t) = 0; virtual void do_transform (const db::ICplxTrans &t) = 0; @@ -63,7 +63,8 @@ public: void transform (const db::IMatrix3d &t) { do_transform (t); } void transform (const db::IMatrix2d &t) { do_transform (t); } - void insert (const db::Text &text) { do_insert (text); } + void insert (const db::Text &text) { do_insert (text, 0); } + void insert (const db::TextWithProperties &text) { do_insert (text, text.properties_id ()); } void insert (const db::Shape &shape); template @@ -72,7 +73,7 @@ public: if (shape.is_text ()) { db::Text text = shape.text (); text.transform (trans); - insert (text); + do_insert (text, shape.prop_id ()); } } diff --git a/src/db/db/dbTexts.cc b/src/db/db/dbTexts.cc index f420c7f90..98a86513a 100644 --- a/src/db/db/dbTexts.cc +++ b/src/db/db/dbTexts.cc @@ -116,6 +116,7 @@ void Texts::insert (const Sh &shape) } template DB_PUBLIC void Texts::insert (const db::Text &); +template DB_PUBLIC void Texts::insert (const db::TextWithProperties &); void Texts::insert (const db::Shape &shape) { diff --git a/src/db/db/dbTexts.h b/src/db/db/dbTexts.h index 86b2a7e24..d69fe8a40 100644 --- a/src/db/db/dbTexts.h +++ b/src/db/db/dbTexts.h @@ -124,6 +124,17 @@ public: insert (s); } + /** + * @brief Constructor from an object with properties + * + * Creates an text set representing a single instance of that object + */ + explicit Texts (const db::TextWithProperties &s) + : mp_delegate (0) + { + insert (s); + } + /** * @brief Constructor from an object * diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 99f289880..20f1817dc 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -230,11 +230,21 @@ static db::EdgePairs *new_a (const std::vector &pairs) return new db::EdgePairs (pairs.begin (), pairs.end ()); } +static db::EdgePairs *new_ap (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_epp (const db::EdgePairWithProperties &pair) +{ + return new db::EdgePairs (pair); +} + static db::EdgePairs *new_shapes (const db::Shapes &s) { db::EdgePairs *r = new db::EdgePairs (); @@ -582,6 +592,13 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This constructor has been introduced in version 0.26." ) + + constructor ("new", &new_ap, gsi::arg ("array"), + "@brief Constructor from an array of edge pairs with properties\n" + "\n" + "This constructor creates an edge pair collection from an array of \\EdgePairWithProperties objects.\n" + "\n" + "This constructor has been introduced in version 0.30." + ) + constructor ("new", &new_ep, gsi::arg ("edge_pair"), "@brief Constructor from a single edge pair object\n" "\n" @@ -589,6 +606,13 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This constructor has been introduced in version 0.26." ) + + constructor ("new", &new_epp, gsi::arg ("edge_pair"), + "@brief Constructor from a single edge pair object with properties\n" + "\n" + "This constructor creates an edge pair collection with a single edge pair.\n" + "\n" + "This constructor has been introduced in version 0.30." + ) + constructor ("new", &new_shapes, gsi::arg ("shapes"), "@brief Shapes constructor\n" "\n" @@ -701,6 +725,11 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", method ("insert", (void (db::EdgePairs::*) (const db::EdgePair &)) &db::EdgePairs::insert, gsi::arg ("edge_pair"), "@brief Inserts an edge pair into the collection\n" ) + + method ("insert", (void (db::EdgePairs::*) (const db::EdgePairWithProperties &)) &db::EdgePairs::insert, gsi::arg ("edge_pair"), + "@brief Inserts an edge pair with properties into the collection\n" + "\n" + "This variant has been introduced in version 0.30." + ) + method_ext ("is_deep?", &is_deep, "@brief Returns true if the edge pair collection is a deep (hierarchical) one\n" "\n" diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 76181040e..31d66458c 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -258,36 +258,73 @@ static db::Edges *new_e (const db::Edge &e) return ee; } +static db::Edges *new_ep (const db::EdgeWithProperties &e) +{ + db::Edges *ee = new db::Edges (); + ee->insert (e); + return ee; +} + static db::Edges *new_a1 (const std::vector &a) { return new db::Edges (a.begin (), a.end ()); } +static db::Edges *new_a1p (const std::vector &a) +{ + return new db::Edges (a.begin (), a.end ()); +} + static db::Edges *new_a2 (const std::vector &a) { return new db::Edges (a.begin (), a.end ()); } +static db::Edges *new_a2p (const std::vector &a) +{ + return new db::Edges (a.begin (), a.end ()); +} + static db::Edges *new_b (const db::Box &o) { return new db::Edges (o); } +static db::Edges *new_bp (const db::BoxWithProperties &o) +{ + return new db::Edges (o); +} + static db::Edges *new_p (const db::Polygon &o) { return new db::Edges (o); } +static db::Edges *new_pp (const db::PolygonWithProperties &o) +{ + return new db::Edges (o); +} + static db::Edges *new_ps (const db::SimplePolygon &o) { return new db::Edges (o); } +static db::Edges *new_psp (const db::SimplePolygonWithProperties &o) +{ + return new db::Edges (o); +} + static db::Edges *new_path (const db::Path &o) { return new db::Edges (o); } +static db::Edges *new_pathp (const db::PathWithProperties &o) +{ + return new db::Edges (o); +} + static db::Edges *new_shapes (const db::Shapes &s, bool as_edges) { db::Edges *r = new db::Edges (); @@ -334,6 +371,13 @@ static void insert_a1 (db::Edges *r, const std::vector &a) } } +static void insert_a1p (db::Edges *r, const std::vector &a) +{ + for (std::vector ::const_iterator p = a.begin (); p != a.end (); ++p) { + r->insert (*p); + } +} + static void insert_a2 (db::Edges *r, const std::vector &a) { for (std::vector ::const_iterator p = a.begin (); p != a.end (); ++p) { @@ -341,6 +385,13 @@ static void insert_a2 (db::Edges *r, const std::vector &a) } } +static void insert_a2p (db::Edges *r, const std::vector &a) +{ + for (std::vector ::const_iterator p = a.begin (); p != a.end (); ++p) { + r->insert (*p); + } +} + static void insert_si (db::Edges *r, db::RecursiveShapeIterator si) { while (! si.at_end ()) { @@ -582,14 +633,22 @@ static db::Region extents0 (const db::Edges *r) static void insert_r (db::Edges *e, const db::Region &a) { for (db::Region::const_iterator p = a.begin (); ! p.at_end (); ++p) { - e->insert (*p); + if (p.prop_id () != 0) { + e->insert (db::PolygonWithProperties (*p, p.prop_id ())); + } else { + e->insert (*p); + } } } static void insert_e (db::Edges *e, const db::Edges &a) { for (db::Edges::const_iterator p = a.begin (); ! p.at_end (); ++p) { - e->insert (*p); + if (p.prop_id () != 0) { + e->insert (db::EdgeWithProperties (*p, p.prop_id ())); + } else { + e->insert (*p); + } } } @@ -599,12 +658,20 @@ static void insert_st (db::Edges *e, const db::Shapes &a, const Trans &t) for (db::Shapes::shape_iterator p = a.begin (db::ShapeIterator::Polygons | db::ShapeIterator::Boxes | db::ShapeIterator::Paths); !p.at_end (); ++p) { db::Polygon poly; p->polygon (poly); - e->insert (poly.transformed (t)); + if (p->prop_id () != 0) { + e->insert (db::PolygonWithProperties (poly.transformed (t), p->prop_id ())); + } else { + e->insert (poly.transformed (t)); + } } for (db::Shapes::shape_iterator p = a.begin (db::ShapeIterator::Edges); !p.at_end (); ++p) { db::Edge edge; p->edge (edge); - e->insert (edge.transformed (t)); + if (p->prop_id () != 0) { + e->insert (db::EdgeWithProperties (edge.transformed (t), p->prop_id ())); + } else { + e->insert (edge.transformed (t)); + } } } @@ -694,41 +761,95 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "This constructor creates an edge collection with a single edge.\n" ) + + constructor ("new", &new_ep, gsi::arg ("edge"), + "@brief Constructor from a single edge with properties\n" + "\n" + "This constructor creates an edge collection with a single edge.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + constructor ("new", &new_a1, gsi::arg ("array"), "@brief Constructor from a polygon array\n" "\n" "This constructor creates an edge collection from an array of polygons.\n" "The edges form the contours of the polygons.\n" ) + + constructor ("new", &new_a1p, gsi::arg ("array"), + "@brief Constructor from a polygon array\n" + "\n" + "This constructor creates an edge collection from an array of polygons with properties.\n" + "The edges form the contours of the polygons.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + constructor ("new", &new_a2, gsi::arg ("array"), "@brief Constructor from an edge array\n" "\n" "This constructor creates an edge collection from an array of edges.\n" ) + + constructor ("new", &new_a2p, gsi::arg ("array"), + "@brief Constructor from an edge array\n" + "\n" + "This constructor creates an edge collection from an array of edges with properties.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + constructor ("new", &new_b, gsi::arg ("box"), "@brief Box constructor\n" "\n" "This constructor creates an edge collection from a box.\n" "The edges form the contour of the box.\n" ) + + constructor ("new", &new_bp, gsi::arg ("box"), + "@brief Box constructor\n" + "\n" + "This constructor creates an edge collection from a box with properties.\n" + "The edges form the contour of the box.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + constructor ("new", &new_p, gsi::arg ("polygon"), "@brief Polygon constructor\n" "\n" "This constructor creates an edge collection from a polygon.\n" "The edges form the contour of the polygon.\n" ) + + constructor ("new", &new_pp, gsi::arg ("polygon"), + "@brief Polygon constructor\n" + "\n" + "This constructor creates an edge collection from a polygon with properties.\n" + "The edges form the contour of the polygon.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + constructor ("new", &new_ps, gsi::arg ("polygon"), "@brief Simple polygon constructor\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_psp, gsi::arg ("polygon"), + "@brief Simple polygon constructor\n" + "\n" + "This constructor creates an edge collection from a simple polygon with properties.\n" + "The edges form the contour of the polygon.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + constructor ("new", &new_path, gsi::arg ("path"), "@brief Path constructor\n" "\n" "This constructor creates an edge collection from a path.\n" "The edges form the contour of the path.\n" ) + + constructor ("new", &new_pathp, gsi::arg ("path"), + "@brief Path constructor\n" + "\n" + "This constructor creates an edge collection from a path with properties.\n" + "The edges form the contour of the path.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + 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" @@ -969,26 +1090,61 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "Inserts the edge into the edge collection.\n" ) + + method ("insert", (void (db::Edges::*)(const db::EdgeWithProperties &)) &db::Edges::insert, gsi::arg ("edge"), + "@brief Inserts an edge\n" + "\n" + "Inserts the edge with properties into the edge collection.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + method ("insert", (void (db::Edges::*)(const db::Box &)) &db::Edges::insert, gsi::arg ("box"), "@brief Inserts a 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::BoxWithProperties &)) &db::Edges::insert, gsi::arg ("box"), + "@brief Inserts a box\n" + "\n" + "Inserts the edges that form the contour of the box into the edge collection with the boxes properties.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + method ("insert", (void (db::Edges::*)(const db::Polygon &)) &db::Edges::insert, gsi::arg ("polygon"), "@brief Inserts a 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::PolygonWithProperties &)) &db::Edges::insert, gsi::arg ("polygon"), + "@brief Inserts a polygon\n" + "\n" + "Inserts the edges that form the contour of the polygon into the edge collection with the properties of the polygon.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + method ("insert", (void (db::Edges::*)(const db::SimplePolygon &)) &db::Edges::insert, gsi::arg ("polygon"), "@brief Inserts a simple 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::SimplePolygonWithProperties &)) &db::Edges::insert, gsi::arg ("polygon"), + "@brief Inserts a simple polygon\n" + "\n" + "Inserts the edges that form the contour of the simple polygon into the edge collection with the properties of the polygon.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + method ("insert", (void (db::Edges::*)(const db::Path &)) &db::Edges::insert, gsi::arg ("path"), "@brief Inserts a path\n" "\n" "Inserts the edges that form the contour of the path into the edge collection.\n" ) + + method ("insert", (void (db::Edges::*)(const db::PathWithProperties &)) &db::Edges::insert, gsi::arg ("path"), + "@brief Inserts a path\n" + "\n" + "Inserts the edges that form the contour of the path into the edge collection with the properties of the path.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + method_ext ("insert", &insert_e, gsi::arg ("edges"), "@brief Inserts all edges from the other edge collection into this one\n" "This method has been introduced in version 0.25." @@ -1040,9 +1196,19 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", method_ext ("insert", &insert_a1, gsi::arg ("polygons"), "@brief Inserts all polygons from the array into this edge collection\n" ) + + method_ext ("insert", &insert_a1p, gsi::arg ("polygons"), + "@brief Inserts all polygons from the array into this edge collection\n" + "\n" + "This variant accepting polygons with properties has been introduced in version 0.30." + ) + method_ext ("insert", &insert_a2, gsi::arg ("edges"), "@brief Inserts all edges from the array into this edge collection\n" ) + + method_ext ("insert", &insert_a2p, gsi::arg ("edges"), + "@brief Inserts all edges from the array into this edge collection\n" + "\n" + "This variant accepting edges with properties has been introduced in version 0.30." + ) + method ("merge", (db::Edges &(db::Edges::*) ()) &db::Edges::merge, "@brief Merge the edges\n" "\n" diff --git a/src/db/db/gsiDeclDbTexts.cc b/src/db/db/gsiDeclDbTexts.cc index 241e734c4..a683ec7b4 100644 --- a/src/db/db/gsiDeclDbTexts.cc +++ b/src/db/db/gsiDeclDbTexts.cc @@ -197,11 +197,21 @@ static db::Texts *new_a (const std::vector &t) return new db::Texts (t.begin (), t.end ()); } +static db::Texts *new_ap (const std::vector &t) +{ + return new db::Texts (t.begin (), t.end ()); +} + static db::Texts *new_text (const db::Text &t) { return new db::Texts (t); } +static db::Texts *new_textp (const db::TextWithProperties &t) +{ + return new db::Texts (t); +} + static db::Texts *new_shapes (const db::Shapes &s) { db::Texts *r = new db::Texts (); @@ -361,15 +371,29 @@ Class decl_Texts (decl_dbShapeCollection, "db", "Texts", "This constructor creates an empty text collection.\n" ) + constructor ("new", &new_a, gsi::arg ("array"), - "@brief Constructor from an text array\n" + "@brief Constructor from a text array\n" "\n" "This constructor creates an text collection from an array of \\Text objects.\n" ) + + constructor ("new", &new_ap, gsi::arg ("array"), + "@brief Constructor from an array with texts with properties\n" + "\n" + "This constructor creates an text collection from an array of \\TextWithProperties objects.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + constructor ("new", &new_text, gsi::arg ("text"), - "@brief Constructor from a single edge pair object\n" + "@brief Constructor from a single text object\n" "\n" "This constructor creates an text collection with a single text.\n" ) + + constructor ("new", &new_textp, gsi::arg ("text"), + "@brief Constructor from a single text object\n" + "\n" + "This constructor creates an text collection with a single text with properties.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + constructor ("new", &new_shapes, gsi::arg ("shapes"), "@brief Shapes constructor\n" "\n" @@ -457,6 +481,11 @@ Class decl_Texts (decl_dbShapeCollection, "db", "Texts", method ("insert", (void (db::Texts::*) (const db::Text &)) &db::Texts::insert, gsi::arg ("text"), "@brief Inserts a text into the collection\n" ) + + method ("insert", (void (db::Texts::*) (const db::TextWithProperties &)) &db::Texts::insert, gsi::arg ("text"), + "@brief Inserts a text into the collection\n" + "\n" + "This variant accepting a text with properties has been introduced in version 0.30." + ) + method_ext ("is_deep?", &is_deep, "@brief Returns true if the edge pair collection is a deep (hierarchical) one\n" ) + diff --git a/testdata/ruby/dbEdgeNeighborhood.rb b/testdata/ruby/dbEdgeNeighborhood.rb index 529931251..7ed108704 100644 --- a/testdata/ruby/dbEdgeNeighborhood.rb +++ b/testdata/ruby/dbEdgeNeighborhood.rb @@ -135,7 +135,7 @@ class DBEdgeNeighborhood_TestClass < TestBase "/Polygon\n" ) - assert_equal(res.to_s, "(-1100,0;-1100,1000;-100,1000;-100,0);(0,0;0,1000;1000,1000;1000,0)") + assert_equal(res.to_s, "(-1100,0;-1100,1000;-100,1000;-100,0);(0,0;0,1000;1000,1000;1000,0){1=>one}") end diff --git a/testdata/ruby/dbEdgePairsTest.rb b/testdata/ruby/dbEdgePairsTest.rb index 0c46601c7..3c1036dcb 100644 --- a/testdata/ruby/dbEdgePairsTest.rb +++ b/testdata/ruby/dbEdgePairsTest.rb @@ -574,6 +574,21 @@ class DBEdgePairs_TestClass < TestBase end + # properties + def test_props + + r = RBA::EdgePairs::new([ RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500)), { 1 => "one" }) ]) + assert_equal(r.to_s, "(0,0;100,100)/(200,300;200,500){1=>one}") + + r = RBA::EdgePairs::new(RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500)), { 1 => "one" })) + assert_equal(r.to_s, "(0,0;100,100)/(200,300;200,500){1=>one}") + + r = RBA::EdgePairs::new + r.insert(RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500)), { 1 => "one" })) + assert_equal(r.to_s, "(0,0;100,100)/(200,300;200,500){1=>one}") + + end + end diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 116fb190c..85a1848a8 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -1314,9 +1314,9 @@ class DBRegion_TestClass < TestBase rr = RBA::Region::new(tc.begin_shapes_rec(l2)).enable_properties assert_equal(csort(r.separation_check(rr, 100, false, RBA::Region::Projection, nil, nil, nil, false, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false).to_s), csort("(400,200;500,200)/(500,250;400,250);(0,200;100,200)/(100,250;0,250);(200,200;300,200)/(300,250;200,250)")) - assert_equal(csort(r.separation_check(rr, 100, false, RBA::Region::Projection, nil, nil, nil, false, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false, RBA::Region::NoPropertyConstraint).to_s), csort("(400,200;500,200)/(500,250;400,250);(0,200;100,200)/(100,250;0,250);(200,200;300,200)/(300,250;200,250)")) - assert_equal(csort(r.separation_check(rr, 100, false, RBA::Region::Projection, nil, nil, nil, false, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false, RBA::Region::SamePropertiesConstraint).to_s), csort("(0,200;100,200)/(100,250;0,250)")) - assert_equal(csort(r.separation_check(rr, 100, false, RBA::Region::Projection, nil, nil, nil, false, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false, RBA::Region::DifferentPropertiesConstraint).to_s), csort("(400,200;500,200)/(500,250;400,250);(200,200;300,200)/(300,250;200,250)")) + assert_equal(csort(r.separation_check(rr, 100, false, RBA::Region::Projection, nil, nil, nil, false, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false, RBA::Region::NoPropertyConstraint).to_s), csort("(0,200;100,200)/(100,250;0,250){1=>17};(200,200;300,200)/(300,250;200,250){1=>42};(400,200;500,200)/(500,250;400,250)")) + assert_equal(csort(r.separation_check(rr, 100, false, RBA::Region::Projection, nil, nil, nil, false, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false, RBA::Region::SamePropertiesConstraint).to_s), csort("(0,200;100,200)/(100,250;0,250){1=>17}")) + assert_equal(csort(r.separation_check(rr, 100, false, RBA::Region::Projection, nil, nil, nil, false, RBA::Region::NoOppositeFilter, RBA::Region::NoRectFilter, false, RBA::Region::DifferentPropertiesConstraint).to_s), csort("(200,200;300,200)/(300,250;200,250){1=>42};(400,200;500,200)/(500,250;400,250)")) r.remove_properties rr.remove_properties diff --git a/testdata/ruby/dbTextsTest.rb b/testdata/ruby/dbTextsTest.rb index 813fe29ad..f2c78403f 100644 --- a/testdata/ruby/dbTextsTest.rb +++ b/testdata/ruby/dbTextsTest.rb @@ -443,6 +443,21 @@ class DBTexts_TestClass < TestBase end + # properties + def test_props + + r = RBA::Texts::new([ RBA::TextWithProperties::new(RBA::Text::new("abc", RBA::Trans::new), { 1 => "one" }) ]) + assert_equal(r.to_s, "('abc',r0 0,0){1=>one}") + + r = RBA::Texts::new(RBA::TextWithProperties::new(RBA::Text::new("abc", RBA::Trans::new), { 1 => "one" })) + assert_equal(r.to_s, "('abc',r0 0,0){1=>one}") + + r = RBA::Texts::new + r.insert(RBA::TextWithProperties::new(RBA::Text::new("abc", RBA::Trans::new), { 1 => "one" })) + assert_equal(r.to_s, "('abc',r0 0,0){1=>one}") + + end + end From 4fda87204821a74b0d0b268644043e65333e0107 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 29 Jan 2025 02:29:13 +0100 Subject: [PATCH 004/392] Fixed unit tests --- .../dbNetlistDeviceExtractorTests.cc | 116 +++++++++--------- src/db/unit_tests/dbRegionTests.cc | 8 +- 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc index 35229465d..06853b9ce 100644 --- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -206,9 +206,9 @@ TEST(10_MOS3DeviceExtractorTest) " device MOS3 $1 (S=(null),G=(null),D=(null)) (L=0.3,W=0.8,AS=0.4,AD=0.16,PS=2.6,PD=2);\n" "end;\n" ); - EXPECT_EQ (o1.to_string (), "(-600,-200;-600,600;-100,600;-100,-200)"); - EXPECT_EQ (o2.to_string (), "(200,-200;200,600;400,600;400,-200)"); - EXPECT_EQ (o3.to_string (), "(-100,-200;-100,600;200,600;200,-200)"); + EXPECT_EQ (o1.to_string (), "(-600,-200;-600,600;-100,600;-100,-200){TERMINAL_ID=>0}"); + EXPECT_EQ (o2.to_string (), "(200,-200;200,600;400,600;400,-200){TERMINAL_ID=>2}"); + EXPECT_EQ (o3.to_string (), "(-100,-200;-100,600;200,600;200,-200){TERMINAL_ID=>1}"); } TEST(11_MOS3DeviceExtractorTestNotRectangularGate) @@ -261,9 +261,9 @@ TEST(11_MOS3DeviceExtractorTestNotRectangularGate) " device MOS3 $1 (S=(null),G=(null),D=(null)) (L=0.3,W=1,AS=0.32,AD=0.18,PS=2.6,PD=2.4);\n" "end;\n" ); - EXPECT_EQ (o1.to_string (), "(-600,-200;-600,600;-300,600;-300,200;-100,200;-100,-200)"); - EXPECT_EQ (o2.to_string (), "(200,-200;200,500;0,500;0,600;400,600;400,-200)"); - EXPECT_EQ (o3.to_string (), "(-100,-200;-100,200;-300,200;-300,600;0,600;0,500;200,500;200,-200)"); + EXPECT_EQ (o1.to_string (), "(-600,-200;-600,600;-300,600;-300,200;-100,200;-100,-200){TERMINAL_ID=>0}"); + EXPECT_EQ (o2.to_string (), "(200,-200;200,500;0,500;0,600;400,600;400,-200){TERMINAL_ID=>2}"); + EXPECT_EQ (o3.to_string (), "(-100,-200;-100,200;-300,200;-300,600;0,600;0,500;200,500;200,-200){TERMINAL_ID=>1}"); } TEST(12_MOS3DeviceExtractorTestCircular) @@ -316,9 +316,9 @@ TEST(12_MOS3DeviceExtractorTestCircular) " device MOS3 $1 (S=(null),G=(null),D=(null)) (L=0.3,W=3.8,AS=0.4,AD=4.18,PS=2.6,PD=14.6);\n" "end;\n" ); - EXPECT_EQ (o1.to_string (), "(200,-200;200,600;700,600;700,-200)"); - EXPECT_EQ (o2.to_string (), "(-600,-1200;-600,1400;1600,1400;1600,-1200/-100,-500;1000,-500;1000,900;-100,900)"); - EXPECT_EQ (o3.to_string (), "(-100,-500;-100,900;1000,900;1000,-500/200,-200;700,-200;700,600;200,600)"); + EXPECT_EQ (o1.to_string (), "(200,-200;200,600;700,600;700,-200){TERMINAL_ID=>0}"); + EXPECT_EQ (o2.to_string (), "(-600,-1200;-600,1400;1600,1400;1600,-1200/-100,-500;1000,-500;1000,900;-100,900){TERMINAL_ID=>2}"); + EXPECT_EQ (o3.to_string (), "(-100,-500;-100,900;1000,900;1000,-500/200,-200;700,-200;700,600;200,600){TERMINAL_ID=>1}"); } TEST(20_MOS4DeviceExtractorTest) @@ -375,10 +375,10 @@ TEST(20_MOS4DeviceExtractorTest) " device MOS4 $1 (S=(null),G=(null),D=(null),B=(null)) (L=0.3,W=0.8,AS=0.4,AD=0.16,PS=2.6,PD=2);\n" "end;\n" ); - EXPECT_EQ (o1.to_string (), "(-600,-200;-600,600;-100,600;-100,-200)"); - EXPECT_EQ (o2.to_string (), "(200,-200;200,600;400,600;400,-200)"); - EXPECT_EQ (o3.to_string (), "(-100,-200;-100,600;200,600;200,-200)"); - EXPECT_EQ (o4.to_string (), "(-100,-200;-100,600;200,600;200,-200)"); + EXPECT_EQ (o1.to_string (), "(-600,-200;-600,600;-100,600;-100,-200){TERMINAL_ID=>0}"); + EXPECT_EQ (o2.to_string (), "(200,-200;200,600;400,600;400,-200){TERMINAL_ID=>2}"); + EXPECT_EQ (o3.to_string (), "(-100,-200;-100,600;200,600;200,-200){TERMINAL_ID=>1}"); + EXPECT_EQ (o4.to_string (), "(-100,-200;-100,600;200,600;200,-200){TERMINAL_ID=>3}"); } TEST(21_MOS4DeviceExtractorTestNotRectangularGate) @@ -435,10 +435,10 @@ TEST(21_MOS4DeviceExtractorTestNotRectangularGate) " device MOS4 $1 (S=(null),G=(null),D=(null),B=(null)) (L=0.3,W=1,AS=0.32,AD=0.18,PS=2.6,PD=2.4);\n" "end;\n" ); - EXPECT_EQ (o1.to_string (), "(-600,-200;-600,600;-300,600;-300,200;-100,200;-100,-200)"); - EXPECT_EQ (o2.to_string (), "(200,-200;200,500;0,500;0,600;400,600;400,-200)"); - EXPECT_EQ (o3.to_string (), "(-100,-200;-100,200;-300,200;-300,600;0,600;0,500;200,500;200,-200)"); - EXPECT_EQ (o4.to_string (), "(-100,-200;-100,200;-300,200;-300,600;0,600;0,500;200,500;200,-200)"); + EXPECT_EQ (o1.to_string (), "(-600,-200;-600,600;-300,600;-300,200;-100,200;-100,-200){TERMINAL_ID=>0}"); + EXPECT_EQ (o2.to_string (), "(200,-200;200,500;0,500;0,600;400,600;400,-200){TERMINAL_ID=>2}"); + EXPECT_EQ (o3.to_string (), "(-100,-200;-100,200;-300,200;-300,600;0,600;0,500;200,500;200,-200){TERMINAL_ID=>1}"); + EXPECT_EQ (o4.to_string (), "(-100,-200;-100,200;-300,200;-300,600;0,600;0,500;200,500;200,-200){TERMINAL_ID=>3}"); } TEST(22_MOS4DeviceExtractorTestCircular) @@ -495,10 +495,10 @@ TEST(22_MOS4DeviceExtractorTestCircular) " device MOS4 $1 (S=(null),G=(null),D=(null),B=(null)) (L=0.3,W=3.8,AS=0.4,AD=4.18,PS=2.6,PD=14.6);\n" "end;\n" ); - EXPECT_EQ (o1.to_string (), "(200,-200;200,600;700,600;700,-200)"); - EXPECT_EQ (o2.to_string (), "(-600,-1200;-600,1400;1600,1400;1600,-1200/-100,-500;1000,-500;1000,900;-100,900)"); - EXPECT_EQ (o3.to_string (), "(-100,-500;-100,900;1000,900;1000,-500/200,-200;700,-200;700,600;200,600)"); - EXPECT_EQ (o4.to_string (), "(-100,-500;-100,900;1000,900;1000,-500/200,-200;700,-200;700,600;200,600)"); + EXPECT_EQ (o1.to_string (), "(200,-200;200,600;700,600;700,-200){TERMINAL_ID=>0}"); + EXPECT_EQ (o2.to_string (), "(-600,-1200;-600,1400;1600,1400;1600,-1200/-100,-500;1000,-500;1000,900;-100,900){TERMINAL_ID=>2}"); + EXPECT_EQ (o3.to_string (), "(-100,-500;-100,900;1000,900;1000,-500/200,-200;700,-200;700,600;200,600){TERMINAL_ID=>1}"); + EXPECT_EQ (o4.to_string (), "(-100,-500;-100,900;1000,900;1000,-500/200,-200;700,-200;700,600;200,600){TERMINAL_ID=>3}"); } TEST(30_DMOS3DeviceExtractorTest) @@ -553,9 +553,9 @@ TEST(30_DMOS3DeviceExtractorTest) " device DMOS3 $1 (S=(null),G=(null),D=(null)) (L=0.3,W=0.8,AS=0.4,AD=0.16,PS=2.6,PD=2);\n" "end;\n" ); - EXPECT_EQ (o1.to_string (), "(-600,-200;-600,600;-100,600;-100,-200)"); - EXPECT_EQ (o2.to_string (), "(200,-200;200,600;400,600;400,-200)"); - EXPECT_EQ (o3.to_string (), "(-100,-200;-100,600;200,600;200,-200)"); + EXPECT_EQ (o1.to_string (), "(-600,-200;-600,600;-100,600;-100,-200){TERMINAL_ID=>0}"); + EXPECT_EQ (o2.to_string (), "(200,-200;200,600;400,600;400,-200){TERMINAL_ID=>2}"); + EXPECT_EQ (o3.to_string (), "(-100,-200;-100,600;200,600;200,-200){TERMINAL_ID=>1}"); } TEST(31_DMOS3DeviceExtractorTestNotRectangularGate) @@ -610,9 +610,9 @@ TEST(31_DMOS3DeviceExtractorTestNotRectangularGate) " device DMOS3 $1 (S=(null),G=(null),D=(null)) (L=0.3,W=1,AS=0.32,AD=0.18,PS=2.6,PD=2.4);\n" "end;\n" ); - EXPECT_EQ (o1.to_string (), "(-600,-200;-600,600;-300,600;-300,200;-100,200;-100,-200)"); - EXPECT_EQ (o2.to_string (), "(200,-200;200,500;0,500;0,600;400,600;400,-200)"); - EXPECT_EQ (o3.to_string (), "(-100,-200;-100,200;-300,200;-300,600;0,600;0,500;200,500;200,-200)"); + EXPECT_EQ (o1.to_string (), "(-600,-200;-600,600;-300,600;-300,200;-100,200;-100,-200){TERMINAL_ID=>0}"); + EXPECT_EQ (o2.to_string (), "(200,-200;200,500;0,500;0,600;400,600;400,-200){TERMINAL_ID=>2}"); + EXPECT_EQ (o3.to_string (), "(-100,-200;-100,200;-300,200;-300,600;0,600;0,500;200,500;200,-200){TERMINAL_ID=>1}"); } TEST(32_DMOS3DeviceExtractorTestCircular) @@ -667,9 +667,9 @@ TEST(32_DMOS3DeviceExtractorTestCircular) " device DMOS3 $1 (S=(null),G=(null),D=(null)) (L=0.3,W=3.8,AS=0.4,AD=4.18,PS=2.6,PD=14.6);\n" "end;\n" ); - EXPECT_EQ (o1.to_string (), "(200,-200;200,600;700,600;700,-200)"); - EXPECT_EQ (o2.to_string (), "(-600,-1200;-600,1400;1600,1400;1600,-1200/-100,-500;1000,-500;1000,900;-100,900)"); - EXPECT_EQ (o3.to_string (), "(-100,-500;-100,900;1000,900;1000,-500/200,-200;700,-200;700,600;200,600)"); + EXPECT_EQ (o1.to_string (), "(200,-200;200,600;700,600;700,-200){TERMINAL_ID=>0}"); + EXPECT_EQ (o2.to_string (), "(-600,-1200;-600,1400;1600,1400;1600,-1200/-100,-500;1000,-500;1000,900;-100,900){TERMINAL_ID=>2}"); + EXPECT_EQ (o3.to_string (), "(-100,-500;-100,900;1000,900;1000,-500/200,-200;700,-200;700,600;200,600){TERMINAL_ID=>1}"); } TEST(40_DMOS4DeviceExtractorTest) @@ -728,10 +728,10 @@ TEST(40_DMOS4DeviceExtractorTest) " device DMOS4 $1 (S=(null),G=(null),D=(null),B=(null)) (L=0.3,W=0.8,AS=0.4,AD=0.16,PS=2.6,PD=2);\n" "end;\n" ); - EXPECT_EQ (o1.to_string (), "(-600,-200;-600,600;-100,600;-100,-200)"); - EXPECT_EQ (o2.to_string (), "(200,-200;200,600;400,600;400,-200)"); - EXPECT_EQ (o3.to_string (), "(-100,-200;-100,600;200,600;200,-200)"); - EXPECT_EQ (o4.to_string (), "(-100,-200;-100,600;200,600;200,-200)"); + EXPECT_EQ (o1.to_string (), "(-600,-200;-600,600;-100,600;-100,-200){TERMINAL_ID=>0}"); + EXPECT_EQ (o2.to_string (), "(200,-200;200,600;400,600;400,-200){TERMINAL_ID=>2}"); + EXPECT_EQ (o3.to_string (), "(-100,-200;-100,600;200,600;200,-200){TERMINAL_ID=>1}"); + EXPECT_EQ (o4.to_string (), "(-100,-200;-100,600;200,600;200,-200){TERMINAL_ID=>3}"); } TEST(41_DMOS4DeviceExtractorTestNotRectangularGate) @@ -790,10 +790,10 @@ TEST(41_DMOS4DeviceExtractorTestNotRectangularGate) " device DMOS4 $1 (S=(null),G=(null),D=(null),B=(null)) (L=0.3,W=1,AS=0.32,AD=0.18,PS=2.6,PD=2.4);\n" "end;\n" ); - EXPECT_EQ (o1.to_string (), "(-600,-200;-600,600;-300,600;-300,200;-100,200;-100,-200)"); - EXPECT_EQ (o2.to_string (), "(200,-200;200,500;0,500;0,600;400,600;400,-200)"); - EXPECT_EQ (o3.to_string (), "(-100,-200;-100,200;-300,200;-300,600;0,600;0,500;200,500;200,-200)"); - EXPECT_EQ (o4.to_string (), "(-100,-200;-100,200;-300,200;-300,600;0,600;0,500;200,500;200,-200)"); + EXPECT_EQ (o1.to_string (), "(-600,-200;-600,600;-300,600;-300,200;-100,200;-100,-200){TERMINAL_ID=>0}"); + EXPECT_EQ (o2.to_string (), "(200,-200;200,500;0,500;0,600;400,600;400,-200){TERMINAL_ID=>2}"); + EXPECT_EQ (o3.to_string (), "(-100,-200;-100,200;-300,200;-300,600;0,600;0,500;200,500;200,-200){TERMINAL_ID=>1}"); + EXPECT_EQ (o4.to_string (), "(-100,-200;-100,200;-300,200;-300,600;0,600;0,500;200,500;200,-200){TERMINAL_ID=>3}"); } TEST(42_DMOS4DeviceExtractorTestCircular) @@ -852,10 +852,10 @@ TEST(42_DMOS4DeviceExtractorTestCircular) " device DMOS4 $1 (S=(null),G=(null),D=(null),B=(null)) (L=0.3,W=3.8,AS=0.4,AD=4.18,PS=2.6,PD=14.6);\n" "end;\n" ); - EXPECT_EQ (o1.to_string (), "(200,-200;200,600;700,600;700,-200)"); - EXPECT_EQ (o2.to_string (), "(-600,-1200;-600,1400;1600,1400;1600,-1200/-100,-500;1000,-500;1000,900;-100,900)"); - EXPECT_EQ (o3.to_string (), "(-100,-500;-100,900;1000,900;1000,-500/200,-200;700,-200;700,600;200,600)"); - EXPECT_EQ (o4.to_string (), "(-100,-500;-100,900;1000,900;1000,-500/200,-200;700,-200;700,600;200,600)"); + EXPECT_EQ (o1.to_string (), "(200,-200;200,600;700,600;700,-200){TERMINAL_ID=>0}"); + EXPECT_EQ (o2.to_string (), "(-600,-1200;-600,1400;1600,1400;1600,-1200/-100,-500;1000,-500;1000,900;-100,900){TERMINAL_ID=>2}"); + EXPECT_EQ (o3.to_string (), "(-100,-500;-100,900;1000,900;1000,-500/200,-200;700,-200;700,600;200,600){TERMINAL_ID=>1}"); + EXPECT_EQ (o4.to_string (), "(-100,-500;-100,900;1000,900;1000,-500/200,-200;700,-200;700,600;200,600){TERMINAL_ID=>3}"); } TEST(50_BJT3DeviceExtractorTest) @@ -910,9 +910,9 @@ TEST(50_BJT3DeviceExtractorTest) " device BJT3 $1 (C=(null),B=(null),E=(null)) (AE=0.81,PE=3.6,AB=5,PB=9,AC=5,PC=9,NE=1);\n" "end;\n" ); - EXPECT_EQ (o1.to_string (), "(700,400;700,1300;1600,1300;1600,400)"); - EXPECT_EQ (o2.to_string (), "(0,0;0,2000;2500,2000;2500,0/700,400;1600,400;1600,1300;700,1300)"); - EXPECT_EQ (o3.to_string (), "(0,0;0,2000;2500,2000;2500,0/700,400;1600,400;1600,1300;700,1300)"); + EXPECT_EQ (o1.to_string (), "(700,400;700,1300;1600,1300;1600,400){TERMINAL_ID=>2}"); + EXPECT_EQ (o2.to_string (), "(0,0;0,2000;2500,2000;2500,0/700,400;1600,400;1600,1300;700,1300){TERMINAL_ID=>1}"); + EXPECT_EQ (o3.to_string (), "(0,0;0,2000;2500,2000;2500,0/700,400;1600,400;1600,1300;700,1300){TERMINAL_ID=>0}"); } TEST(51_BJT3DeviceExtractorTest) @@ -967,9 +967,9 @@ TEST(51_BJT3DeviceExtractorTest) " device BJT3 $1 (C=(null),B=(null),E=(null)) (AE=0.81,PE=3.6,AB=5,PB=9,AC=5,PC=9,NE=1);\n" "end;\n" ); - EXPECT_EQ (o1.to_string (), "(700,400;700,1300;1600,1300;1600,400)"); - EXPECT_EQ (o2.to_string (), "(0,0;0,2000;2500,2000;2500,0/700,400;1600,400;1600,1300;700,1300)"); - EXPECT_EQ (o3.to_string (), "(-1000,-500;-1000,2500;3000,2500;3000,-500/0,0;2500,0;2500,2000;0,2000)"); + EXPECT_EQ (o1.to_string (), "(700,400;700,1300;1600,1300;1600,400){TERMINAL_ID=>2}"); + EXPECT_EQ (o2.to_string (), "(0,0;0,2000;2500,2000;2500,0/700,400;1600,400;1600,1300;700,1300){TERMINAL_ID=>1}"); + EXPECT_EQ (o3.to_string (), "(-1000,-500;-1000,2500;3000,2500;3000,-500/0,0;2500,0;2500,2000;0,2000){TERMINAL_ID=>0}"); } TEST(52_BJT3DeviceExtractorTestLateral) @@ -1024,9 +1024,9 @@ TEST(52_BJT3DeviceExtractorTestLateral) " device BJT3 $1 (C=(null),B=(null),E=(null)) (AE=0.81,PE=3.6,AB=5,PB=9,AC=0.8,PC=4.8,NE=1);\n" "end;\n" ); - EXPECT_EQ (o1.to_string (), "(700,400;700,1300;1600,1300;1600,400)"); - EXPECT_EQ (o2.to_string (), "(0,0;0,2000;2100,2000;2100,0/700,400;1600,400;1600,1300;700,1300)"); - EXPECT_EQ (o3.to_string (), "(2100,0;2100,2000;2500,2000;2500,0)"); + EXPECT_EQ (o1.to_string (), "(700,400;700,1300;1600,1300;1600,400){TERMINAL_ID=>2}"); + EXPECT_EQ (o2.to_string (), "(0,0;0,2000;2100,2000;2100,0/700,400;1600,400;1600,1300;700,1300){TERMINAL_ID=>1}"); + EXPECT_EQ (o3.to_string (), "(2100,0;2100,2000;2500,2000;2500,0){TERMINAL_ID=>0}"); } TEST(53_BJT3DeviceExtractorTestMultEmitter) @@ -1082,9 +1082,9 @@ TEST(53_BJT3DeviceExtractorTestMultEmitter) " device BJT3 $2 (C=(null),B=(null),E=(null)) (AE=0.5,PE=3,AB=10,PB=14,AC=10,PC=14,NE=1);\n" "end;\n" ); - EXPECT_EQ (o1.to_string (), "(1000,500;1000,1500;1500,1500;1500,500);(3500,500;3500,1500;4000,1500;4000,500)"); - EXPECT_EQ (o2.to_string (), "(0,0;0,2000;5000,2000;5000,0/1000,500;1500,500;1500,1500;1000,1500/3500,500;4000,500;4000,1500;3500,1500);(0,0;0,2000;5000,2000;5000,0/1000,500;1500,500;1500,1500;1000,1500/3500,500;4000,500;4000,1500;3500,1500)"); - EXPECT_EQ (o3.to_string (), "(-500,-500;-500,2500;5500,2500;5500,-500/0,0;5000,0;5000,2000;0,2000);(-500,-500;-500,2500;5500,2500;5500,-500/0,0;5000,0;5000,2000;0,2000)"); + EXPECT_EQ (o1.to_string (), "(1000,500;1000,1500;1500,1500;1500,500){TERMINAL_ID=>2};(3500,500;3500,1500;4000,1500;4000,500){TERMINAL_ID=>2}"); + EXPECT_EQ (o2.to_string (), "(0,0;0,2000;5000,2000;5000,0/1000,500;1500,500;1500,1500;1000,1500/3500,500;4000,500;4000,1500;3500,1500){TERMINAL_ID=>1};(0,0;0,2000;5000,2000;5000,0/1000,500;1500,500;1500,1500;1000,1500/3500,500;4000,500;4000,1500;3500,1500){TERMINAL_ID=>1}"); + EXPECT_EQ (o3.to_string (), "(-500,-500;-500,2500;5500,2500;5500,-500/0,0;5000,0;5000,2000;0,2000){TERMINAL_ID=>0};(-500,-500;-500,2500;5500,2500;5500,-500/0,0;5000,0;5000,2000;0,2000){TERMINAL_ID=>0}"); } TEST(54_BJT4DeviceExtractorTest) @@ -1143,9 +1143,9 @@ TEST(54_BJT4DeviceExtractorTest) " device BJT4 $1 (C=(null),B=(null),E=(null),S=(null)) (AE=0.81,PE=3.6,AB=5,PB=9,AC=5,PC=9,NE=1);\n" "end;\n" ); - EXPECT_EQ (o1.to_string (), "(700,400;700,1300;1600,1300;1600,400)"); - EXPECT_EQ (o2.to_string (), "(0,0;0,2000;2500,2000;2500,0/700,400;1600,400;1600,1300;700,1300)"); - EXPECT_EQ (o3.to_string (), "(-1000,-500;-1000,2500;3000,2500;3000,-500/0,0;2500,0;2500,2000;0,2000)"); - EXPECT_EQ (o4.to_string (), "(0,0;0,2000;2500,2000;2500,0)"); + EXPECT_EQ (o1.to_string (), "(700,400;700,1300;1600,1300;1600,400){TERMINAL_ID=>2}"); + EXPECT_EQ (o2.to_string (), "(0,0;0,2000;2500,2000;2500,0/700,400;1600,400;1600,1300;700,1300){TERMINAL_ID=>1}"); + EXPECT_EQ (o3.to_string (), "(-1000,-500;-1000,2500;3000,2500;3000,-500/0,0;2500,0;2500,2000;0,2000){TERMINAL_ID=>0}"); + EXPECT_EQ (o4.to_string (), "(0,0;0,2000;2500,2000;2500,0){TERMINAL_ID=>3}"); } diff --git a/src/db/unit_tests/dbRegionTests.cc b/src/db/unit_tests/dbRegionTests.cc index 2beb36455..3bb98718a 100644 --- a/src/db/unit_tests/dbRegionTests.cc +++ b/src/db/unit_tests/dbRegionTests.cc @@ -2090,13 +2090,13 @@ TEST(50b_PropertiesFlat) rr.insert (db::Box (0, 0, 100, 200)); rr.insert (db::BoxWithProperties (db::Box (1, 2, 101, 202), pid1)); - EXPECT_EQ ((db::Region () + rr).to_string (), "(0,0;0,200;100,200;100,0);(1,2;1,202;101,202;101,2)"); - EXPECT_EQ ((rr + db::Region ()).to_string (), "(0,0;0,200;100,200;100,0);(1,2;1,202;101,202;101,2)"); - EXPECT_EQ ((r + rr).to_string (), "(0,0;0,20;10,20;10,0);(0,0;0,200;100,200;100,0);(1,2;1,202;101,202;101,2)"); + EXPECT_EQ ((db::Region () + rr).to_string (), "(0,0;0,200;100,200;100,0);(1,2;1,202;101,202;101,2){id=>1}"); + EXPECT_EQ ((rr + db::Region ()).to_string (), "(0,0;0,200;100,200;100,0);(1,2;1,202;101,202;101,2){id=>1}"); + EXPECT_EQ ((r + rr).to_string (), "(0,0;0,20;10,20;10,0);(0,0;0,200;100,200;100,0);(1,2;1,202;101,202;101,2){id=>1}"); r += rr; - EXPECT_EQ (r.to_string (), "(0,0;0,20;10,20;10,0);(0,0;0,200;100,200;100,0);(1,2;1,202;101,202;101,2)"); + EXPECT_EQ (r.to_string (), "(0,0;0,20;10,20;10,0);(0,0;0,200;100,200;100,0);(1,2;1,202;101,202;101,2){id=>1}"); } TEST(51_PropertiesFlatFromLayout) From a282a5d66595474bde6312c80b3ea3c92430cea2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 29 Jan 2025 02:42:16 +0100 Subject: [PATCH 005/392] Some more tests for properties inside Edges, Regions --- src/db/db/gsiDeclDbRegion.cc | 6 +++- testdata/ruby/dbEdgesTest.rb | 63 +++++++++++++++++++++++++++++++++++ testdata/ruby/dbRegionTest.rb | 6 ++++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 9ea181a82..2cf35d992 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -427,7 +427,11 @@ static void insert_st (db::Region *r, const db::Shapes &a, const Trans &t) for (db::Shapes::shape_iterator p = a.begin (db::ShapeIterator::Polygons | db::ShapeIterator::Boxes | db::ShapeIterator::Paths); !p.at_end (); ++p) { db::Polygon poly; p->polygon (poly); - r->insert (poly.transformed (t)); + if (p->prop_id () != 0) { + r->insert (db::PolygonWithProperties (poly.transformed (t), p->prop_id ())); + } else { + r->insert (poly.transformed (t)); + } } } diff --git a/testdata/ruby/dbEdgesTest.rb b/testdata/ruby/dbEdgesTest.rb index 87bc8674a..d125a22e7 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -955,6 +955,69 @@ class DBEdges_TestClass < TestBase end + # properties + def test_props + + r = RBA::Edges::new([ RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 100), { 1 => "one" }) ]) + assert_equal(r.to_s, "(0,0;100,100){1=>one}") + + r = RBA::Edges::new + r.insert([ RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 100), { 1 => "one" }) ]) + assert_equal(r.to_s, "(0,0;100,100){1=>one}") + + r = RBA::Edges::new(RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 100), { 1 => "one" })) + assert_equal(r.to_s, "(0,0;100,100){1=>one}") + + r = RBA::Edges::new + r.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 100), { 1 => "one" })) + assert_equal(r.to_s, "(0,0;100,100){1=>one}") + + r = RBA::Edges::new([ RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" }) ]) + assert_equal(r.to_s, "(0,0;0,200){1=>one};(0,200;100,200){1=>one};(100,200;100,0){1=>one};(100,0;0,0){1=>one}") + + r = RBA::Edges::new + r.insert([ RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" }) ]) + assert_equal(r.to_s, "(0,0;0,200){1=>one};(0,200;100,200){1=>one};(100,200;100,0){1=>one};(100,0;0,0){1=>one}") + + r = RBA::Edges::new(RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" })) + assert_equal(r.to_s, "(0,0;0,200){1=>one};(0,200;100,200){1=>one};(100,200;100,0){1=>one};(100,0;0,0){1=>one}") + + r = RBA::Edges::new + r.insert(RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" })) + assert_equal(r.to_s, "(0,0;0,200){1=>one};(0,200;100,200){1=>one};(100,200;100,0){1=>one};(100,0;0,0){1=>one}") + + r = RBA::Edges::new(RBA::SimplePolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" })) + assert_equal(r.to_s, "(0,0;0,200){1=>one};(0,200;100,200){1=>one};(100,200;100,0){1=>one};(100,0;0,0){1=>one}") + + r = RBA::Edges::new + r.insert(RBA::SimplePolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" })) + assert_equal(r.to_s, "(0,0;0,200){1=>one};(0,200;100,200){1=>one};(100,200;100,0){1=>one};(100,0;0,0){1=>one}") + + r = RBA::Edges::new(RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" })) + assert_equal(r.to_s, "(0,0;0,200){1=>one};(0,200;100,200){1=>one};(100,200;100,0){1=>one};(100,0;0,0){1=>one}") + + r = RBA::Edges::new + r.insert(RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" })) + assert_equal(r.to_s, "(0,0;0,200){1=>one};(0,200;100,200){1=>one};(100,200;100,0){1=>one};(100,0;0,0){1=>one}") + + r = RBA::Edges::new + r.insert(RBA::Region::new(RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" }))) + assert_equal(r.to_s, "(0,0;0,200){1=>one};(0,200;100,200){1=>one};(100,200;100,0){1=>one};(100,0;0,0){1=>one}") + + r = RBA::Edges::new + s = RBA::Shapes::new + s.insert(RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" })) + r.insert(s) + assert_equal(r.to_s, "(0,0;0,200){1=>one};(0,200;100,200){1=>one};(100,200;100,0){1=>one};(100,0;0,0){1=>one}") + + r = RBA::Edges::new + s = RBA::Shapes::new + s.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 100), { 1 => "one" })) + r.insert(s) + assert_equal(r.to_s, "(0,0;100,100){1=>one}") + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 85a1848a8..09a556eea 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -1562,6 +1562,12 @@ class DBRegion_TestClass < TestBase r = RBA::Region::new(RBA::PathWithProperties::new(RBA::Path::new([ RBA::Point::new(0, 0), RBA::Point::new(100, 0) ], 20), { 1 => "one" })) assert_equal(r.to_s, "(0,-10;0,10;100,10;100,-10){1=>one}") + r = RBA::Region::new + s = RBA::Shapes::new + s.insert(RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" })) + r.insert(s) + assert_equal(r.to_s, "(0,0;0,200;100,200;100,0){1=>one}") + end end From 16351d2a887f40f7bc8395ca26d1dac8f7f7970f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Feb 2025 21:54:57 +0000 Subject: [PATCH 006/392] Bump pypa/gh-action-pypi-publish from 1.12.3 to 1.12.4 Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.12.3 to 1.12.4. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.12.3...v1.12.4) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index be3b7623e..049b57901 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -103,7 +103,7 @@ jobs: name: artifact path: dist - - uses: pypa/gh-action-pypi-publish@v1.12.3 + - uses: pypa/gh-action-pypi-publish@v1.12.4 continue-on-error: true # might fail if we don't bump the version with: user: __token__ @@ -120,7 +120,7 @@ jobs: name: artifact path: dist - - uses: pypa/gh-action-pypi-publish@v1.12.3 + - uses: pypa/gh-action-pypi-publish@v1.12.4 with: user: __token__ password: ${{ secrets.pypi_password }} From 1bd1bb06ce5d06fd9c2fccabfa8b0de06983328b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 2 Feb 2025 17:00:31 +0100 Subject: [PATCH 007/392] More support for objects with properties: RDB integration. --- src/db/db/dbObjectWithProperties.cc | 93 ++++++++++++++++-- src/db/db/dbObjectWithProperties.h | 52 ++++++++++ src/db/db/gsiDeclDbPropertiesSupport.h | 15 +++ .../unit_tests/dbObjectWithPropertiesTests.cc | 53 ++++++++++ src/db/unit_tests/unit_tests.pro | 1 + src/rdb/rdb/gsiDeclRdb.cc | 51 ++++++++++ src/rdb/rdb/rdb.h | 10 ++ src/rdb/rdb/rdbUtils.cc | 28 +++--- src/rdb/rdb/rdbUtils.h | 25 +++++ testdata/ruby/dbPolygonTest.rb | 16 +++ testdata/ruby/rdbTest.rb | 97 +++++++++++++++++++ 11 files changed, 420 insertions(+), 21 deletions(-) create mode 100644 src/db/unit_tests/dbObjectWithPropertiesTests.cc diff --git a/src/db/db/dbObjectWithProperties.cc b/src/db/db/dbObjectWithProperties.cc index fd2d68749..e30373fa2 100644 --- a/src/db/db/dbObjectWithProperties.cc +++ b/src/db/db/dbObjectWithProperties.cc @@ -21,14 +21,6 @@ */ #include "dbObjectWithProperties.h" -#include "dbUserObject.h" -#include "dbText.h" -#include "dbPolygon.h" -#include "dbBox.h" -#include "dbPath.h" -#include "dbPoint.h" -#include "dbEdge.h" -#include "dbEdgePair.h" namespace db { @@ -56,3 +48,88 @@ template class db::object_with_properties; template class db::object_with_properties; } + +namespace tl +{ + +template bool _test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p) +{ + if (! tl::test_extractor_impl (ex, (T &) p)) { + return false; + } + + if (ex.test ("props")) { + + if (! ex.test ("=")) { + return false; + } + + tl::Variant v; + if (! tl::test_extractor_impl (ex, v)) { + return false; + } + if (! v.is_array ()) { + return false; + } + + db::PropertiesSet props; + for (auto i = v.begin_array (); i != v.end_array (); ++i) { + props.insert (i->first, i->second); + } + + p.properties_id (db::properties_id (props)); + + } + + return true; +} + +template void _extractor_impl (tl::Extractor &ex, db::object_with_properties &p) +{ + if (! test_extractor_impl (ex, p)) { + ex.error (tl::to_string (tr ("Expected a shape specification with properties"))); + } +} + +template <> bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { return _test_extractor_impl (ex, p); } +template <> bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { return _test_extractor_impl (ex, p); } +template <> bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { return _test_extractor_impl (ex, p); } +template <> bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { return _test_extractor_impl (ex, p); } +template <> bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { return _test_extractor_impl (ex, p); } +template <> bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { return _test_extractor_impl (ex, p); } +template <> bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { return _test_extractor_impl (ex, p); } +template <> bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { return _test_extractor_impl (ex, p); } +template <> bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { return _test_extractor_impl (ex, p); } + +template <> bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { return _test_extractor_impl (ex, p); } +template <> bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { return _test_extractor_impl (ex, p); } +template <> bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { return _test_extractor_impl (ex, p); } +template <> bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { return _test_extractor_impl (ex, p); } +template <> bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { return _test_extractor_impl (ex, p); } +template <> bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { return _test_extractor_impl (ex, p); } +template <> bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { return _test_extractor_impl (ex, p); } +template <> bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { return _test_extractor_impl (ex, p); } +template <> bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { return _test_extractor_impl (ex, p); } + +template <> void extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { _extractor_impl (ex, p); } +template <> void extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { _extractor_impl (ex, p); } +template <> void extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { _extractor_impl (ex, p); } +template <> void extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { _extractor_impl (ex, p); } +template <> void extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { _extractor_impl (ex, p); } +template <> void extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { _extractor_impl (ex, p); } +template <> void extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { _extractor_impl (ex, p); } +template <> void extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { _extractor_impl (ex, p); } +template <> void extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { _extractor_impl (ex, p); } + +template <> void extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { _extractor_impl (ex, p); } +template <> void extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { _extractor_impl (ex, p); } +template <> void extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { _extractor_impl (ex, p); } +template <> void extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { _extractor_impl (ex, p); } +template <> void extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { _extractor_impl (ex, p); } +template <> void extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { _extractor_impl (ex, p); } +template <> void extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { _extractor_impl (ex, p); } +template <> void extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { _extractor_impl (ex, p); } +template <> void extractor_impl (tl::Extractor &ex, db::object_with_properties &p) { _extractor_impl (ex, p); } + +} + diff --git a/src/db/db/dbObjectWithProperties.h b/src/db/db/dbObjectWithProperties.h index 03242ac61..9ab320b23 100644 --- a/src/db/db/dbObjectWithProperties.h +++ b/src/db/db/dbObjectWithProperties.h @@ -26,7 +26,9 @@ #include "tlException.h" #include "tlTypeTraits.h" +#include "tlString.h" #include "dbTypes.h" +#include "dbUserObject.h" #include "dbPolygon.h" #include "dbPath.h" #include "dbEdge.h" @@ -340,5 +342,55 @@ operator<< (std::ostream &os, const object_with_properties &p) } // namespace db + +namespace tl +{ + +/** + * @brief Special extractors + */ + +template <> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p); + +template <> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC bool test_extractor_impl (tl::Extractor &ex, db::object_with_properties &p); + +template <> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::object_with_properties &p); + +template <> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::object_with_properties &p); +template <> DB_PUBLIC void extractor_impl (tl::Extractor &ex, db::object_with_properties &p); + +} // namespace tl + #endif diff --git a/src/db/db/gsiDeclDbPropertiesSupport.h b/src/db/db/gsiDeclDbPropertiesSupport.h index a97b687bc..f1780458b 100644 --- a/src/db/db/gsiDeclDbPropertiesSupport.h +++ b/src/db/db/gsiDeclDbPropertiesSupport.h @@ -132,10 +132,25 @@ static T moved_xy_meth_impl (const T *s, typename T::coord_type dx, typename T:: return s->moved (typename T::vector_type (dx, dy)); } +template +struct downcast_impl_helper; + +template +struct downcast_impl_helper > +{ + static T impl (const db::object_with_properties *obj) + { + return *obj; + } +}; + template static gsi::Methods properties_support_methods () { return + gsi::method_ext ("downcast", &downcast_impl_helper::impl, + "@brief Gets the corresponding object without the properties\n" + ) + gsi::method ("prop_id", (db::properties_id_type (T::*) () const) &T::properties_id, "@brief Gets the properties ID associated with the object\n" ) + diff --git a/src/db/unit_tests/dbObjectWithPropertiesTests.cc b/src/db/unit_tests/dbObjectWithPropertiesTests.cc new file mode 100644 index 000000000..c06a78800 --- /dev/null +++ b/src/db/unit_tests/dbObjectWithPropertiesTests.cc @@ -0,0 +1,53 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbObjectWithProperties.h" +#include "tlUnitTest.h" + +namespace { + +TEST(1) +{ + db::PropertiesSet ps; + ps.insert (tl::Variant (1), tl::Variant ("one")); + ps.insert (tl::Variant ("key"), tl::Variant (42.0)); + + db::PolygonWithProperties pwp (db::Polygon (db::Box (0, 0, 100, 200)), db::properties_id (ps)); + + EXPECT_EQ (pwp.to_string (), "(0,0;0,200;100,200;100,0) props={1=>one,key=>42}"); + + db::PolygonWithProperties pwp2; + + std::string s; + tl::Extractor ex (s.c_str ()); + + EXPECT_EQ (ex.try_read (pwp2), false); + + s = " (0,0;0,200;100,200;100,0) props= {1 => \"one\", key => 42} "; + ex = tl::Extractor (s.c_str ()); + + EXPECT_EQ (ex.try_read (pwp2), true); + EXPECT_EQ (pwp2.to_string (), "(0,0;0,200;100,200;100,0) props={1=>one,key=>42}"); +} + +} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 67e131894..7363746df 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -11,6 +11,7 @@ SOURCES = \ dbEdgeNeighborhoodTests.cc \ dbFillToolTests.cc \ dbLogTests.cc \ + dbObjectWithPropertiesTests.cc \ dbRecursiveInstanceIteratorTests.cc \ dbRegionCheckUtilsTests.cc \ dbTriangleTests.cc \ diff --git a/src/rdb/rdb/gsiDeclRdb.cc b/src/rdb/rdb/gsiDeclRdb.cc index 78dc7a243..10307e96f 100644 --- a/src/rdb/rdb/gsiDeclRdb.cc +++ b/src/rdb/rdb/gsiDeclRdb.cc @@ -1211,6 +1211,21 @@ void create_items_from_edge_pair_array (rdb::Database *db, rdb::id_type cell_id, rdb::create_items_from_sequence (db, cell_id, cat_id, trans, collection.begin (), collection.end ()); } +void create_items_from_polygon_array_with_properties (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const std::vector &collection, bool with_properties) +{ + rdb::create_items_from_sequence_with_properties (db, cell_id, cat_id, trans, collection.begin (), collection.end (), with_properties); +} + +void create_items_from_edge_array_with_properties (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const std::vector &collection, bool with_properties) +{ + rdb::create_items_from_sequence_with_properties (db, cell_id, cat_id, trans, collection.begin (), collection.end (), with_properties); +} + +void create_items_from_edge_pair_array_with_properties (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const db::CplxTrans &trans, const std::vector &collection, bool with_properties) +{ + rdb::create_items_from_sequence_with_properties (db, cell_id, cat_id, trans, collection.begin (), collection.end (), with_properties); +} + static rdb::Item *create_item (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id) { if (! db->cell_by_id (cell_id)) { @@ -1548,6 +1563,18 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "@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_with_properties, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("array"), gsi::arg ("with_properties", true), + "@brief Creates new polygon items for the given cell/category combination\n" + "This version takes \\PolygonWithProperties objects. If \\with_properties is true (the default), the\n" + "properties are added to the item as tagged values.\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 (with properties) for which the items are created\n" + "@param with_properties If true, the properties are transferred into the item as well" + "\n" + "This variant has been introduced in version 0.30." + ) + 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 " @@ -1562,6 +1589,18 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "@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_with_properties, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("array"), gsi::arg ("with_properties", true), + "@brief Creates new edge items for the given cell/category combination\n" + "This version takes \\EdgeWithProperties objects. If \\with_properties is true (the default), the\n" + "properties are added to the item as tagged values.\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 edges (with properties) for which the items are created\n" + "@param with_properties If true, the properties are transferred into the item as well" + "\n" + "This variant has been introduced in version 0.30." + ) + 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 " @@ -1576,6 +1615,18 @@ Class decl_ReportDatabase ("rdb", "ReportDatabase", "@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_with_properties, gsi::arg ("cell_id"), gsi::arg ("category_id"), gsi::arg ("trans"), gsi::arg ("array"), gsi::arg ("with_properties", true), + "@brief Creates new edge pair items for the given cell/category combination\n" + "This version takes \\EdgePairWithProperties objects. If \\with_properties is true (the default), the\n" + "properties are added to the item as tagged values.\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 edge pairs (with properties) for which the items are created\n" + "@param with_properties If true, the properties are transferred into the item as well" + "\n" + "This variant has been introduced in version 0.30." + ) + 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 " diff --git a/src/rdb/rdb/rdb.h b/src/rdb/rdb/rdb.h index 48d6e01db..b5c4e98a8 100644 --- a/src/rdb/rdb/rdb.h +++ b/src/rdb/rdb/rdb.h @@ -27,6 +27,7 @@ #include "rdbCommon.h" #include "dbTrans.h" +#include "dbObjectWithProperties.h" #include "gsi.h" #include "tlObject.h" #include "tlObjectCollection.h" @@ -532,6 +533,15 @@ RDB_PUBLIC_TEMPLATE ValueBase *make_value (const T &value) return new Value (value); } +/** + * @brief Type bindings + */ +template +RDB_PUBLIC_TEMPLATE ValueBase *make_value (const db::object_with_properties &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 7478b897d..6d0181162 100644 --- a/src/rdb/rdb/rdbUtils.cc +++ b/src/rdb/rdb/rdbUtils.cc @@ -290,6 +290,19 @@ void create_items_from_shapes (rdb::Database *db, rdb::id_type cell_id, rdb::id_ } } +void add_properties_to_item (rdb::Item *item, db::properties_id_type prop_id) +{ + if (! item->database ()) { + return; + } + + auto ps = db::properties (prop_id); + for (auto i = ps.begin (); i != ps.end (); ++i) { + id_type tag_id = item->database ()->tags ().tag (db::property_name (i->first).to_string (), true /*user tag*/).id (); + add_item_value (item, db::property_value (i->second), db::CplxTrans (), tag_id); + } +} + 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, bool with_properties) { std::unique_ptr value (rdb::ValueBase::create_from_shape (shape, trans)); @@ -301,19 +314,8 @@ void create_item_from_shape (rdb::Database *db, rdb::id_type cell_id, rdb::id_ty item->values ().add (value.release ()); // translate properties - if (with_properties && shape.has_prop_id () && shape.shapes () && shape.shapes ()->cell ()) { - - const db::Layout *layout = shape.shapes ()->cell ()->layout (); - if (layout) { - - auto ps = db::properties (shape.prop_id ()); - for (auto i = ps.begin (); i != ps.end (); ++i) { - id_type tag_id = db->tags ().tag (db::property_name (i->first).to_string (), true /*user tag*/).id (); - add_item_value (item, db::property_value (i->second), trans, tag_id); - } - - } - + if (with_properties && shape.has_prop_id ()) { + add_properties_to_item (item, shape.prop_id ()); } } diff --git a/src/rdb/rdb/rdbUtils.h b/src/rdb/rdb/rdbUtils.h index 158553ed5..b65243b9f 100644 --- a/src/rdb/rdb/rdbUtils.h +++ b/src/rdb/rdb/rdbUtils.h @@ -109,6 +109,13 @@ RDB_PUBLIC void create_items_from_shapes (rdb::Database *db, rdb::id_type cell_i */ 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, bool with_properties = true); +/** + * @brief Adds properties to a RDB item + * + * The properties will be added as values with "tags" + */ +RDB_PUBLIC void add_properties_to_item (rdb::Item *item, db::properties_id_type prop_id); + /** * @brief Creates RDB items from a region * @@ -154,6 +161,24 @@ RDB_PUBLIC_TEMPLATE void create_items_from_sequence (rdb::Database *db, rdb::id_ } } +/** + * @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_TEMPLATE void create_items_from_sequence_with_properties (rdb::Database *db, rdb::id_type cell_id, rdb::id_type cat_id, const Trans &trans, Iter begin, Iter end, bool with_properties = true) +{ + 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))); + if (with_properties && o->properties_id () != 0) { + add_properties_to_item (item, o->properties_id ()); + } + } +} + /** * @brief Creates a value from a tl::Variant * diff --git a/testdata/ruby/dbPolygonTest.rb b/testdata/ruby/dbPolygonTest.rb index 5c96489cd..114faf54d 100644 --- a/testdata/ruby/dbPolygonTest.rb +++ b/testdata/ruby/dbPolygonTest.rb @@ -914,6 +914,10 @@ class DBPolygon_TestClass < TestBase assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={}") assert_equal(s.property(1), nil) + # Test downcast + assert_equal(s.class.to_s, "RBA::PolygonWithProperties") + assert_equal(s.downcast.class.to_s, "RBA::Polygon") + s = RBA::DPolygonWithProperties::new assert_equal(s.to_s, "() props={}") @@ -932,6 +936,10 @@ class DBPolygon_TestClass < TestBase assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={}") assert_equal(s.property(1), nil) + # Test downcast + assert_equal(s.class.to_s, "RBA::DPolygonWithProperties") + assert_equal(s.downcast.class.to_s, "RBA::DPolygon") + s = RBA::SimplePolygonWithProperties::new assert_equal(s.to_s, "() props={}") @@ -950,6 +958,10 @@ class DBPolygon_TestClass < TestBase assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={}") assert_equal(s.property(1), nil) + # Test downcast + assert_equal(s.class.to_s, "RBA::SimplePolygonWithProperties") + assert_equal(s.downcast.class.to_s, "RBA::SimplePolygon") + s = RBA::DSimplePolygonWithProperties::new assert_equal(s.to_s, "() props={}") @@ -968,6 +980,10 @@ class DBPolygon_TestClass < TestBase assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={}") assert_equal(s.property(1), nil) + # Test downcast + assert_equal(s.class.to_s, "RBA::DSimplePolygonWithProperties") + assert_equal(s.downcast.class.to_s, "RBA::DSimplePolygon") + end end diff --git a/testdata/ruby/rdbTest.rb b/testdata/ruby/rdbTest.rb index be37effa0..392e0cfa4 100644 --- a/testdata/ruby/rdbTest.rb +++ b/testdata/ruby/rdbTest.rb @@ -943,6 +943,103 @@ class RDB_TestClass < TestBase rdb.each_cell { |c| cn << c.to_s_items } assert_equal(cn.join(";"), "c1[label: ('Hello, world!',r0 0,0),edge: (0.01,0.021;0.031,0.051),polygon: (0.021,0.042;0.021,0.073;0.043,0.073;0.043,0.042)]") + rdb = RBA::ReportDatabase.new("neu") + cat1 = rdb.create_category("l1") + cell1 = rdb.create_cell("c1") + rdb.create_items(cell1.rdb_id, cat1.rdb_id, RBA::CplxTrans::new(ly.dbu), [ RBA::Polygon::new(RBA::Box::new(0, 0, 100, 200)), RBA::Polygon::new(RBA::Box::new(100, 200, 110, 210)) ]) + assert_equal(cat1.num_items, 2) + cn = [] + rdb.each_cell { |c| cn << c.to_s_items } + assert_equal(cn.join(";"), "c1[polygon: (0,0;0,0.2;0.1,0.2;0.1,0),polygon: (0.1,0.2;0.1,0.21;0.11,0.21;0.11,0.2)]") + + rdb = RBA::ReportDatabase.new("neu") + cat1 = rdb.create_category("l1") + cell1 = rdb.create_cell("c1") + rdb.create_items(cell1.rdb_id, cat1.rdb_id, RBA::CplxTrans::new(ly.dbu), [ RBA::Edge::new(0, 0, 100, 200), RBA::Edge::new(100, 200, 110, 210) ]) + assert_equal(cat1.num_items, 2) + cn = [] + rdb.each_cell { |c| cn << c.to_s_items } + assert_equal(cn.join(";"), "c1[edge: (0,0;0.1,0.2),edge: (0.1,0.2;0.11,0.21)]") + + rdb = RBA::ReportDatabase.new("neu") + cat1 = rdb.create_category("l1") + cell1 = rdb.create_cell("c1") + rdb.create_items(cell1.rdb_id, cat1.rdb_id, RBA::CplxTrans::new(ly.dbu), [ RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 200), RBA::Edge::new(100, 200, 110, 210)) ]) + assert_equal(cat1.num_items, 1) + cn = [] + rdb.each_cell { |c| cn << c.to_s_items } + assert_equal(cn.join(";"), "c1[edge-pair: (0,0;0.1,0.2)/(0.1,0.2;0.11,0.21)]") + + rdb = RBA::ReportDatabase.new("neu") + cat1 = rdb.create_category("l1") + cell1 = rdb.create_cell("c1") + rdb.create_items(cell1.rdb_id, cat1.rdb_id, RBA::CplxTrans::new(ly.dbu), [ + RBA::PolygonWithProperties::new(RBA::Polygon::new(RBA::Box::new(0, 0, 100, 200)), { 1 => "one" }), + RBA::PolygonWithProperties::new(RBA::Polygon::new(RBA::Box::new(100, 200, 110, 210)), { 42 => 17 }) + ]) + assert_equal(cat1.num_items, 2) + cn = [] + rdb.each_cell { |c| cn << c.to_s_items } + assert_equal(cn.join(";"), "c1[polygon: (0,0;0,0.2;0.1,0.2;0.1,0)/text: one,polygon: (0.1,0.2;0.1,0.21;0.11,0.21;0.11,0.2)/float: 17]") + + rdb = RBA::ReportDatabase.new("neu") + cat1 = rdb.create_category("l1") + cell1 = rdb.create_cell("c1") + rdb.create_items(cell1.rdb_id, cat1.rdb_id, RBA::CplxTrans::new(ly.dbu), [ + RBA::PolygonWithProperties::new(RBA::Polygon::new(RBA::Box::new(0, 0, 100, 200)), { 1 => "one" }), + RBA::PolygonWithProperties::new(RBA::Polygon::new(RBA::Box::new(100, 200, 110, 210)), { 42 => 17 }) + ], false) + assert_equal(cat1.num_items, 2) + cn = [] + rdb.each_cell { |c| cn << c.to_s_items } + assert_equal(cn.join(";"), "c1[polygon: (0,0;0,0.2;0.1,0.2;0.1,0),polygon: (0.1,0.2;0.1,0.21;0.11,0.21;0.11,0.2)]") + + rdb = RBA::ReportDatabase.new("neu") + cat1 = rdb.create_category("l1") + cell1 = rdb.create_cell("c1") + rdb.create_items(cell1.rdb_id, cat1.rdb_id, RBA::CplxTrans::new(ly.dbu), [ + RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 200), { 1 => "one" }), + RBA::EdgeWithProperties::new(RBA::Edge::new(100, 200, 110, 210), { 42 => 17 }) + ]) + assert_equal(cat1.num_items, 2) + cn = [] + rdb.each_cell { |c| cn << c.to_s_items } + assert_equal(cn.join(";"), "c1[edge: (0,0;0.1,0.2)/text: one,edge: (0.1,0.2;0.11,0.21)/float: 17]") + + rdb = RBA::ReportDatabase.new("neu") + cat1 = rdb.create_category("l1") + cell1 = rdb.create_cell("c1") + rdb.create_items(cell1.rdb_id, cat1.rdb_id, RBA::CplxTrans::new(ly.dbu), [ + RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 200), { 1 => "one" }), + RBA::EdgeWithProperties::new(RBA::Edge::new(100, 200, 110, 210), { 42 => 17 }) + ], false) + assert_equal(cat1.num_items, 2) + cn = [] + rdb.each_cell { |c| cn << c.to_s_items } + assert_equal(cn.join(";"), "c1[edge: (0,0;0.1,0.2),edge: (0.1,0.2;0.11,0.21)]") + + rdb = RBA::ReportDatabase.new("neu") + cat1 = rdb.create_category("l1") + cell1 = rdb.create_cell("c1") + rdb.create_items(cell1.rdb_id, cat1.rdb_id, RBA::CplxTrans::new(ly.dbu), [ + RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 200), RBA::Edge::new(100, 200, 110, 210)), { 1 => "one" }) + ]) + assert_equal(cat1.num_items, 1) + cn = [] + rdb.each_cell { |c| cn << c.to_s_items } + assert_equal(cn.join(";"), "c1[edge-pair: (0,0;0.1,0.2)/(0.1,0.2;0.11,0.21)/text: one]") + + rdb = RBA::ReportDatabase.new("neu") + cat1 = rdb.create_category("l1") + cell1 = rdb.create_cell("c1") + rdb.create_items(cell1.rdb_id, cat1.rdb_id, RBA::CplxTrans::new(ly.dbu), [ + RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 200), RBA::Edge::new(100, 200, 110, 210)), { 1 => "one" }) + ], false) + assert_equal(cat1.num_items, 1) + cn = [] + rdb.each_cell { |c| cn << c.to_s_items } + assert_equal(cn.join(";"), "c1[edge-pair: (0,0;0.1,0.2)/(0.1,0.2;0.11,0.21)]") + end def test_13 From 08dbe3aba571f3f604ba35382aba484343ce7346 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 2 Feb 2025 19:41:54 +0100 Subject: [PATCH 008/392] Fixing problem with sorting order of tl::Variant as key in std::map --- src/tl/tl/tlVariant.cc | 32 ++++++++++++---- src/tl/unit_tests/tlVariantTests.cc | 58 +++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/src/tl/tl/tlVariant.cc b/src/tl/tl/tlVariant.cc index 82192cca0..38b5bf14b 100644 --- a/src/tl/tl/tlVariant.cc +++ b/src/tl/tl/tlVariant.cc @@ -1309,22 +1309,40 @@ Variant::less_core (const tl::Variant &d, type t) const bool Variant::operator== (const tl::Variant &d) const { - std::pair tt = normalized_type (m_type, d.m_type); - if (! tt.first) { + type type1 = normalized_type (m_type); + type type2 = normalized_type (d.m_type); + + if (is_integer_type (type1)) { + type1 = Variant::t_double; + } + if (is_integer_type (type2)) { + type2 = Variant::t_double; + } + + if (type1 != type2) { return false; } else { - return equal_core (d, tt.second); + return equal_core (d, type1); } } bool Variant::operator< (const tl::Variant &d) const { - std::pair tt = normalized_type (m_type, d.m_type); - if (! tt.first) { - return normalized_type (m_type) < normalized_type (d.m_type); + type type1 = normalized_type (m_type); + type type2 = normalized_type (d.m_type); + + if (is_integer_type (type1)) { + type1 = Variant::t_double; + } + if (is_integer_type (type2)) { + type2 = Variant::t_double; + } + + if (type1 != type2) { + return type1 < type2; } else { - return less_core (d, tt.second); + return less_core (d, type1); } } diff --git a/src/tl/unit_tests/tlVariantTests.cc b/src/tl/unit_tests/tlVariantTests.cc index 2f01572fc..fbe9695d5 100644 --- a/src/tl/unit_tests/tlVariantTests.cc +++ b/src/tl/unit_tests/tlVariantTests.cc @@ -1377,4 +1377,62 @@ TEST(12) EXPECT_EQ (std::string (v.to_parsable_string ()), "('Aber_',##2.5,(#5,'x'),())"); } +// tl::Variant sorting with different numericals +TEST(13) +{ + tl::Variant v1 ((long) 2); + tl::Variant v2 ((int) 2); + tl::Variant v3 ((unsigned int) 2); + tl::Variant v4 (2.0); + tl::Variant v5 ("2"); + tl::Variant v6 (2, true); // ID + + EXPECT_EQ (v1 == v2, true); + EXPECT_EQ (v2 == v1, true); + EXPECT_EQ (v1 < v2, false); + EXPECT_EQ (v2 < v1, false); + + EXPECT_EQ (v1 == v3, true); // signed compares to unsigned + EXPECT_EQ (v3 == v1, true); + EXPECT_EQ (v3 < v1, false); + EXPECT_EQ (v1 < v3, false); + + EXPECT_EQ (v1 == v4, true); + EXPECT_EQ (v2 == v1, true); + EXPECT_EQ (v1 < v2, false); + EXPECT_EQ (v2 < v1, false); + + EXPECT_EQ (v1 == v5, false); // string != value + EXPECT_EQ (v5 == v1, false); + EXPECT_EQ (v1 < v5, true); + EXPECT_EQ (v5 < v1, false); + + // IDs are treated differently + EXPECT_EQ (v1 == v6, false); + EXPECT_EQ (v6 == v1, false); + EXPECT_EQ (v1 < v6, false); + EXPECT_EQ (v6 < v1, true); + + // Use of tl::Variant as map keys + std::map vm; + + vm.insert (std::make_pair (tl::Variant (2), 1)); + EXPECT_EQ (vm[tl::Variant (2)], 1); + + vm.insert (std::make_pair (tl::Variant (2.0), 2)); + EXPECT_EQ (vm[tl::Variant (2)], 1); + EXPECT_EQ (vm[tl::Variant (2.0)], 1); + + vm.insert (std::make_pair (tl::Variant (2, true), 3)); + EXPECT_EQ (vm[tl::Variant (2)], 1); + EXPECT_EQ (vm[tl::Variant (2.0)], 1); + EXPECT_EQ (vm[tl::Variant (2, true)], 3); + + vm.insert (std::make_pair (tl::Variant ("2"), 4)); + EXPECT_EQ (vm[tl::Variant (2)], 1); + EXPECT_EQ (vm[tl::Variant (2.0)], 1); + EXPECT_EQ (vm[tl::Variant (2, true)], 3); + EXPECT_EQ (vm[tl::Variant ("2")], 4); +} + } From aaae05016e70534c0fef41292798a52431503233 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 2 Feb 2025 21:57:21 +0100 Subject: [PATCH 009/392] Fixed unit tests --- src/tl/unit_tests/tlVariantTests.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tl/unit_tests/tlVariantTests.cc b/src/tl/unit_tests/tlVariantTests.cc index fbe9695d5..a3fdc5f1f 100644 --- a/src/tl/unit_tests/tlVariantTests.cc +++ b/src/tl/unit_tests/tlVariantTests.cc @@ -1040,7 +1040,7 @@ TEST(5) EXPECT_EQ (m [1.0], 17); // non-members of that category EXPECT_EQ (m [1.25], 0); - EXPECT_EQ (m [(unsigned int) 1], 0); + EXPECT_EQ (m [(unsigned int) 1], 17); EXPECT_EQ (m ["1"], 0); // unsigned int category @@ -1051,7 +1051,7 @@ TEST(5) EXPECT_EQ (m [2.0], 42); // non-members of that category EXPECT_EQ (m [2.25], 0); - EXPECT_EQ (m [2], 0); + EXPECT_EQ (m [2], 42); EXPECT_EQ (m ["2"], 0); // float category @@ -1333,7 +1333,7 @@ TEST(11) // compare without type EXPECT_EQ (tl::Variant (1l) == tl::Variant (1.0), true); EXPECT_EQ (tl::Variant (1l) == tl::Variant (1), true); - EXPECT_EQ (tl::Variant (1l) == tl::Variant (1u), false); + EXPECT_EQ (tl::Variant (1l) == tl::Variant (1u), true); EXPECT_EQ (tl::Variant (1l) == tl::Variant (1.5), false); EXPECT_EQ (tl::Variant (1l) < tl::Variant (1.0), false); EXPECT_EQ (tl::Variant (1.0) < tl::Variant (1l), false); From aa674484015b9ea795ba00a2fc60a62f20cd9c9c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 2 Feb 2025 23:47:02 +0100 Subject: [PATCH 010/392] WIP: polygon neighborhood --- src/db/db/db.pro | 3 + src/db/db/dbEdgeNeighborhood.h | 2 +- src/db/db/dbPolygonNeighborhood.cc | 209 ++++++++++++++++++++++ src/db/db/dbPolygonNeighborhood.h | 173 ++++++++++++++++++ src/db/db/gsiDeclDbEdgeNeighborhood.cc | 2 +- src/db/db/gsiDeclDbPolygonNeighborhood.cc | 144 +++++++++++++++ 6 files changed, 531 insertions(+), 2 deletions(-) create mode 100644 src/db/db/dbPolygonNeighborhood.cc create mode 100644 src/db/db/dbPolygonNeighborhood.h create mode 100644 src/db/db/gsiDeclDbPolygonNeighborhood.cc diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 98e7ef5da..1b0c205b2 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -76,6 +76,7 @@ SOURCES = \ dbPCellVariant.cc \ dbPoint.cc \ dbPolygon.cc \ + dbPolygonNeighborhood.cc \ dbPolygonTools.cc \ dbPolygonGenerators.cc \ dbPropertiesRepository.cc \ @@ -139,6 +140,7 @@ SOURCES = \ gsiDeclDbPath.cc \ gsiDeclDbPoint.cc \ gsiDeclDbPolygon.cc \ + gsiDeclDbPolygonNeighborhood.cc \ gsiDeclDbReader.cc \ gsiDeclDbRecursiveInstanceIterator.cc \ gsiDeclDbRecursiveShapeIterator.cc \ @@ -310,6 +312,7 @@ HEADERS = \ dbPCellVariant.h \ dbPoint.h \ dbPolygon.h \ + dbPolygonNeighborhood.h \ dbPolygonTools.h \ dbPolygonGenerators.h \ dbPropertiesRepository.h \ diff --git a/src/db/db/dbEdgeNeighborhood.h b/src/db/db/dbEdgeNeighborhood.h index c4c53fc04..e5e95adc4 100644 --- a/src/db/db/dbEdgeNeighborhood.h +++ b/src/db/db/dbEdgeNeighborhood.h @@ -158,7 +158,7 @@ class DB_PUBLIC EdgeNeighborhoodCompoundOperationNode : public CompoundRegionMultiInputOperationNode { public: - EdgeNeighborhoodCompoundOperationNode (const std::vector &children, EdgeNeighborhoodVisitor *visitor, const db::Coord bext, db::Coord eext, db::Coord din, db::Coord dout); + EdgeNeighborhoodCompoundOperationNode (const std::vector &children, EdgeNeighborhoodVisitor *visitor, db::Coord bext, db::Coord eext, db::Coord din, db::Coord dout); virtual ResultType result_type () const { diff --git a/src/db/db/dbPolygonNeighborhood.cc b/src/db/db/dbPolygonNeighborhood.cc new file mode 100644 index 000000000..21a7a9696 --- /dev/null +++ b/src/db/db/dbPolygonNeighborhood.cc @@ -0,0 +1,209 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbPolygonNeighborhood.h" +#include "dbBoxScanner.h" +#include "dbClip.h" + +namespace db +{ + +PolygonNeighborhoodVisitor::PolygonNeighborhoodVisitor () + : m_result_type (db::CompoundRegionOperationNode::ResultType::Edges) +{ + disconnect_outputs (); +} + +void +PolygonNeighborhoodVisitor::connect_output (Layout * /*layout*/, std::unordered_set *polygons) const +{ + disconnect_outputs (); + mp_polygons = polygons; +} + +void +PolygonNeighborhoodVisitor::connect_output (db::Layout *layout, std::unordered_set *polygons) const +{ + disconnect_outputs (); + mp_layout = layout; + mp_polygon_refs = polygons; +} + +void +PolygonNeighborhoodVisitor::connect_output (db::Layout * /*layout*/, std::unordered_set *edges) const +{ + disconnect_outputs (); + mp_edges = edges; +} + +void +PolygonNeighborhoodVisitor::connect_output (Layout * /*layout*/, std::unordered_set *edge_pairs) const +{ + disconnect_outputs (); + mp_edge_pairs = edge_pairs; +} + +void +PolygonNeighborhoodVisitor::disconnect_outputs () const +{ + mp_layout = 0; + mp_polygons = 0; + mp_polygon_refs = 0; + mp_edges = 0; + mp_edge_pairs = 0; +} + +void +PolygonNeighborhoodVisitor::output_polygon (const db::PolygonWithProperties &poly) +{ + if (mp_polygons) { + mp_polygons->insert (poly); + } else if (mp_polygon_refs) { + tl_assert (mp_layout != 0); + mp_polygon_refs->insert (db::PolygonRefWithProperties (db::PolygonRef (poly, mp_layout->shape_repository ()), poly.properties_id ())); + } else { + throw tl::Exception (tl::to_string (tr ("PolygonNeighborhoodVisitor is not configured for edge output (use 'result_type=Edges')"))); + } +} + +void +PolygonNeighborhoodVisitor::output_edge (const db::EdgeWithProperties &edge) +{ + if (mp_edges == 0) { + throw tl::Exception (tl::to_string (tr ("PolygonNeighborhoodVisitor is not configured for edge output (use 'result_type=Edges')"))); + } + mp_edges->insert (edge); +} + +void +PolygonNeighborhoodVisitor::output_edge_pair (const db::EdgePairWithProperties &edge_pair) +{ + if (mp_edge_pairs == 0) { + throw tl::Exception (tl::to_string (tr ("PolygonNeighborhoodVisitor is not configured for edge pair output (use 'result_type=EdgePairs')"))); + } + mp_edge_pairs->insert (edge_pair); +} + +// -------------------------------------------------------------------------------------------------- + +PolygonNeighborhoodCompoundOperationNode::PolygonNeighborhoodCompoundOperationNode (const std::vector &children, PolygonNeighborhoodVisitor *visitor, db::Coord dist) + : CompoundRegionMultiInputOperationNode (children), m_dist (dist), mp_visitor (visitor) +{ + tl_assert (visitor != 0); + visitor->keep (); +} + +db::Coord +PolygonNeighborhoodCompoundOperationNode::computed_dist () const +{ + return m_dist; +} + +std::string +PolygonNeighborhoodCompoundOperationNode::generated_description () const +{ + return tl::to_string (tr ("Polygon neighborhood collector")); +} + +void +PolygonNeighborhoodCompoundOperationNode::do_compute_local (CompoundRegionOperationCache *cache, db::Layout *layout, db::Cell *cell, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase *proc) const +{ + compute_local_impl (cache, layout, cell, interactions, results, proc); +} + +void +PolygonNeighborhoodCompoundOperationNode::do_compute_local (CompoundRegionOperationCache *cache, db::Layout *layout, db::Cell *cell, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase *proc) const +{ + compute_local_impl (cache, layout, cell, interactions, results, proc); +} + +void +PolygonNeighborhoodCompoundOperationNode::do_compute_local (CompoundRegionOperationCache *cache, db::Layout *layout, db::Cell *cell, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase *proc) const +{ + compute_local_impl (cache, layout, cell, interactions, results, proc); +} + +void +PolygonNeighborhoodCompoundOperationNode::do_compute_local (CompoundRegionOperationCache *cache, db::Layout *layout, db::Cell *cell, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase *proc) const +{ + compute_local_impl (cache, layout, cell, interactions, results, proc); +} + +void +PolygonNeighborhoodCompoundOperationNode::do_compute_local (CompoundRegionOperationCache *cache, db::Layout *layout, db::Cell *cell, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase *proc) const +{ + compute_local_impl (cache, layout, cell, interactions, results, proc); +} + +void +PolygonNeighborhoodCompoundOperationNode::do_compute_local (CompoundRegionOperationCache *cache, db::Layout *layout, db::Cell *cell, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase *proc) const +{ + compute_local_impl (cache, layout, cell, interactions, results, proc); +} + +template +void +PolygonNeighborhoodCompoundOperationNode::compute_local_impl (CompoundRegionOperationCache *cache, db::Layout *layout, db::Cell *cell, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase *proc) const +{ + if (! mp_visitor) { + return; + } + tl_assert (interactions.num_subjects () == 1); + tl_assert (! results.empty ()); + + try { + + mp_visitor->connect_output (layout, &results.front ()); + + const T &pr = interactions.begin_subjects ()->second; + db::PolygonWithProperties subject (pr.instantiate (), pr.properties_id ()); + + PolygonNeighborhoodVisitor::neighbors_type neighbors; + + for (unsigned int i = 0; i < children (); ++i) { + + std::vector &n = neighbors [i]; + + std::vector > others; + others.push_back (std::unordered_set ()); + + shape_interactions computed_interactions; + child (i)->compute_local (cache, layout, cell, interactions_for_child (interactions, i, computed_interactions), others, proc); + + for (auto p = others.front ().begin (); p != others.front ().end (); ++p) { + n.push_back (db::PolygonWithProperties (p->instantiate (), p->properties_id ())); + } + + } + + const_cast (mp_visitor.get ())->neighbors (layout, cell, subject, neighbors); + + mp_visitor->disconnect_outputs (); + + } catch (...) { + mp_visitor->disconnect_outputs (); + throw; + } +} + +} + diff --git a/src/db/db/dbPolygonNeighborhood.h b/src/db/db/dbPolygonNeighborhood.h new file mode 100644 index 000000000..17bd13178 --- /dev/null +++ b/src/db/db/dbPolygonNeighborhood.h @@ -0,0 +1,173 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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_dbPolygonNeighborhood +#define HDR_dbPolygonNeighborhood + +#include "dbCommon.h" +#include "dbCompoundOperation.h" +#include "dbBoxScanner.h" +#include "dbMatrix.h" + +namespace db +{ + +/** + * @brief A visitor for the neighbors of an edge + */ +class DB_PUBLIC PolygonNeighborhoodVisitor + : public gsi::ObjectBase, public tl::Object +{ +public: + typedef std::pair position_interval_type; + typedef unsigned int input_key_type; + typedef std::vector neighbor_shapes_type; + typedef std::map neighbors_type; + + /** + * @brief Constructor + */ + PolygonNeighborhoodVisitor (); + + /** + * @brief Destructor + */ + virtual ~PolygonNeighborhoodVisitor () { } + + /** + * @brief Configure the polygon output + */ + void connect_output (db::Layout * /*layout*/, std::unordered_set *polygons) const; + + /** + * @brief Configure the polygon ref output + */ + void connect_output (db::Layout *layout, std::unordered_set *polygons) const; + + /** + * @brief Configure the edge output + */ + void connect_output (db::Layout * /*layout*/, std::unordered_set *edges) const; + + /** + * @brief Configure the edge pair output + */ + void connect_output (db::Layout * /*layout*/, std::unordered_set *edge_pairs) const; + + /** + * @brief Disconnects output + */ + void disconnect_outputs () const; + + /** + * @brief Event handler called when a new polygon is encountered + * This will report the central polygon and the neighbors. + */ + virtual void neighbors (const db::Layout * /*layout*/, const db::Cell * /*cell*/, const db::PolygonWithProperties & /*polygon*/, const neighbors_type & /*neighbors*/) { } + + /** + * @brief Sets the result type + */ + void set_result_type (db::CompoundRegionOperationNode::ResultType result_type) + { + m_result_type = result_type; + } + + /** + * @brief Gets the result type + */ + db::CompoundRegionOperationNode::ResultType result_type () const + { + return m_result_type; + } + + /** + * @brief Delivers a polygon + * This function is only permitted if the result type is Region. + */ + void output_polygon (const PolygonWithProperties &poly); + + /** + * @brief Delivers an edge + * This function is only permitted if the result type is Edges. + */ + void output_edge (const db::EdgeWithProperties &edge); + + /** + * @brief Delivers an edge pair object + * This function is only permitted if the result type is EdgePairs. + */ + void output_edge_pair (const db::EdgePairWithProperties &edge_pair); + +private: + db::CompoundRegionOperationNode::ResultType m_result_type; + mutable std::unordered_set *mp_polygons; + mutable std::unordered_set *mp_polygon_refs; + mutable std::unordered_set *mp_edges; + mutable std::unordered_set *mp_edge_pairs; + mutable db::Layout *mp_layout; +}; + +/** + * @brief A local operation for implementation of the neighborhood visitor + */ +class DB_PUBLIC PolygonNeighborhoodCompoundOperationNode + : public CompoundRegionMultiInputOperationNode +{ +public: + PolygonNeighborhoodCompoundOperationNode (const std::vector &children, PolygonNeighborhoodVisitor *visitor, db::Coord dist); + + virtual ResultType result_type () const + { + return mp_visitor ? mp_visitor->result_type () : CompoundRegionOperationNode::Edges; + } + + virtual bool wants_caching () const + { + return false; + } + +protected: + virtual db::Coord computed_dist () const; + virtual std::string generated_description () const; + + virtual void do_compute_local (CompoundRegionOperationCache * /*cache*/, db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions & /*interactions*/, std::vector > & /*results*/, const db::LocalProcessorBase * /*proc*/) const; + virtual void do_compute_local (CompoundRegionOperationCache * /*cache*/, db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions & /*interactions*/, std::vector > & /*results*/, const db::LocalProcessorBase * /*proc*/) const; + virtual void do_compute_local (CompoundRegionOperationCache * /*cache*/, db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions & /*interactions*/, std::vector > & /*results*/, const db::LocalProcessorBase * /*proc*/) const; + virtual void do_compute_local (CompoundRegionOperationCache * /*cache*/, db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions & /*interactions*/, std::vector > & /*results*/, const db::LocalProcessorBase * /*proc*/) const; + virtual void do_compute_local (CompoundRegionOperationCache * /*cache*/, db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions & /*interactions*/, std::vector > & /*results*/, const db::LocalProcessorBase * /*proc*/) const; + virtual void do_compute_local (CompoundRegionOperationCache * /*cache*/, db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions & /*interactions*/, std::vector > & /*results*/, const db::LocalProcessorBase * /*proc*/) const; + +private: + db::Coord m_dist; + tl::weak_ptr mp_visitor; + + template + void compute_local_impl (CompoundRegionOperationCache * /*cache*/, db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions & /*interactions*/, std::vector > & /*results*/, const db::LocalProcessorBase * /*proc*/) const; +}; + +} + +#endif + diff --git a/src/db/db/gsiDeclDbEdgeNeighborhood.cc b/src/db/db/gsiDeclDbEdgeNeighborhood.cc index 526b54487..94b0a3961 100644 --- a/src/db/db/gsiDeclDbEdgeNeighborhood.cc +++ b/src/db/db/gsiDeclDbEdgeNeighborhood.cc @@ -224,7 +224,7 @@ Class decl_EdgeNeighborhoodVisitorImpl (decl_E ); // --------------------------------------------------------------------------------- -// EdgeProcessor binding +// PolygonNeighborhoodCompoundOperationNode binding static db::CompoundRegionOperationNode *new_edge_neighborhood (const std::vector &children, db::EdgeNeighborhoodVisitor *visitor, const db::Coord bext, db::Coord eext, db::Coord din, db::Coord dout) { diff --git a/src/db/db/gsiDeclDbPolygonNeighborhood.cc b/src/db/db/gsiDeclDbPolygonNeighborhood.cc new file mode 100644 index 000000000..bf5d373ca --- /dev/null +++ b/src/db/db/gsiDeclDbPolygonNeighborhood.cc @@ -0,0 +1,144 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "gsiEnums.h" + +#include "tlThreads.h" +#include "dbPolygonNeighborhood.h" + +namespace gsi +{ + +// --------------------------------------------------------------------------------- +// EdgeFilter binding + +class PolygonNeighborhoodVisitorImpl + : public db::PolygonNeighborhoodVisitor +{ +public: + PolygonNeighborhoodVisitorImpl () { } + + void issue_neighbors (const db::Layout *, const db::Cell *, const db::PolygonWithProperties &, const db::PolygonNeighborhoodVisitor::neighbors_type &) + { + // just for signature + } + + void neighbors (const db::Layout *layout, const db::Cell *cell, const db::PolygonWithProperties &polygon, const db::PolygonNeighborhoodVisitor::neighbors_type &neighbors) + { + if (f_neighbors.can_issue ()) { + + // NOTE: as scripts are potentially thread unsafe, we lock here + tl::MutexLocker locker (&m_lock); + return f_neighbors.issue (&PolygonNeighborhoodVisitorImpl::issue_neighbors, layout, cell, polygon, neighbors); + + } + } + + gsi::Callback f_neighbors; + +private: + // No copying + PolygonNeighborhoodVisitorImpl &operator= (const PolygonNeighborhoodVisitorImpl &); + PolygonNeighborhoodVisitorImpl (const PolygonNeighborhoodVisitorImpl &); + + tl::Mutex m_lock; +}; + +Class decl_PolygonNeighborhoodVisitor ("db", "PolygonNeighborhoodVisitorBase", + "@hide" +); + +Class decl_PolygonNeighborhoodVisitorImpl (decl_PolygonNeighborhoodVisitor, "db", "PolygonNeighborhoodVisitor", + gsi::callback ("neighbors", &PolygonNeighborhoodVisitorImpl::issue_neighbors, &PolygonNeighborhoodVisitorImpl::f_neighbors, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("polygon"), gsi::arg ("neighborhood"), + "@brief Is called for each polygon with the neighbors\n" + "This method is called for every (merged) polygon on the input region. It delivers the polygon and the neighborhood. " + "The neighborhood is a collection of polygons (with properties) vs. input index.\n" + "It contains all polygons 'close to' the current polygon given by 'polygon'. 'Close to' does not necessarily refer to " + "being exactly in the vicinity, but may include other polygons just entering the bounding box of the current polygon." + ) + + gsi::method ("output", &PolygonNeighborhoodVisitorImpl::output_polygon, gsi::arg ("polygon"), + "@brief Outputs a polygon\n" + "Use this method from one of the callbacks (\\on_edge, \\begin_polygon, \\end_polygon) to deliver a polygon. " + "Note that you have to configure the result type as 'Region' on construction of the visitor before being able to do so.\n" + "\n" + "'output' expects an object in original space - i.e. of the input edge. \\to_original_trans gives you a suitable " + "transformation to bring objects from 'edge is horizontal' space into the original space." + ) + + gsi::method ("output", &PolygonNeighborhoodVisitorImpl::output_edge, gsi::arg ("edge"), + "@brief Outputs an edge\n" + "Use this method from one of the callbacks (\\on_edge, \\begin_polygon, \\end_polygon) to deliver a polygon. " + "Note that you have to configure the result type as 'Edges' on construction of the visitor before being able to do so." + "\n" + "'output' expects an object in original space - i.e. of the input edge. \\to_original_trans gives you a suitable " + "transformation to bring objects from 'edge is horizontal' space into the original space." + ) + + gsi::method ("output", &PolygonNeighborhoodVisitorImpl::output_edge_pair, gsi::arg ("edge_pair"), + "@brief Outputs an edge pair\n" + "Use this method from one of the callbacks (\\on_edge, \\begin_polygon, \\end_polygon) to deliver a polygon. " + "Note that you have to configure the result type as 'EdgePairs' on construction of the visitor before being able to do so." + "\n" + "'output' expects an object in original space - i.e. of the input edge. \\to_original_trans gives you a suitable " + "transformation to bring objects from 'edge is horizontal' space into the original space." + ) + + gsi::method ("result_type=", &PolygonNeighborhoodVisitorImpl::set_result_type, gsi::arg ("result_type"), + "@brief Configures the result type\n" + "Use this method to indicate what type of result you want to deliver. You can use the corresponding 'output' method then to " + "deliver result shapes from one the callbacks (\\on_edge, \\begin_polygon, \\end_polygon). Set this attribute when you create " + "the visitor object. This attribute does not need to be set if no output is indended to be delivered." + ) + + gsi::method ("result_type", &PolygonNeighborhoodVisitorImpl::result_type, + "@brief Gets the result type\n" + ), + "@brief A visitor for the neighborhood of polygons in the input\n" + "\n" + "Objects of this class are passed to \\PolygonNeighborhoodCompoundOperationNode constructor to handle " + "events on each edge of the primary input along with the neighborhood taken from the additional inputs.\n" + "\n" + "See \\neighbors for the description of the events delivered." + "\n" + "This class has been introduced in version 0.30.0.\n" +); + +// --------------------------------------------------------------------------------- +// PolygonNeighborhoodCompoundOperationNode binding + +static db::CompoundRegionOperationNode *new_edge_neighborhood (const std::vector &children, db::PolygonNeighborhoodVisitor *visitor, const db::Coord dist) +{ + return new db::PolygonNeighborhoodCompoundOperationNode (children, visitor, dist); +} + +gsi::ClassExt decl_CompoundRegionOperationNode_ext ( + gsi::constructor ("new_polygon_neighborhood", &new_edge_neighborhood, gsi::arg ("children"), gsi::arg ("visitor"), gsi::arg ("dist", 0), + "@brief Creates a new edge neighborhood collector\n" + "\n" + "@param children The inputs to use. The inputs are enumrated by base zero indexes in the visitor callback.\n" + "@param visitor The visitor object (see \\PolygonNeighborhoodVisitor) receiving the edge events.\n" + "@param dist The search distance in which to look up neighbors.\n" + "\n" + "This constructor has been introduced in version 0.30.0.\n" + ) +); + +} + From 1f44a951f492c258a8a2a6018a5d2fc11ce05cf7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 3 Feb 2025 20:47:31 +0100 Subject: [PATCH 011/392] Polygon neighborhood compound operation --- src/db/db/gsiDeclDbEdgeNeighborhood.cc | 4 +- src/db/db/gsiDeclDbPolygonNeighborhood.cc | 12 +- .../unit_tests/dbPolygonNeighborhoodTests.cc | 161 ++++++++++++++++++ src/db/unit_tests/unit_tests.pro | 1 + src/rba/unit_tests/rbaTests.cc | 1 + testdata/algo/polygon_neighborhood.gds | Bin 0 -> 1286 bytes testdata/algo/polygon_neighborhood_au1.gds | Bin 0 -> 1622 bytes testdata/algo/polygon_neighborhood_au2.gds | Bin 0 -> 1556 bytes testdata/algo/polygon_neighborhood_au3.gds | Bin 0 -> 1826 bytes testdata/algo/polygon_neighborhood_au4.gds | Bin 0 -> 1502 bytes testdata/algo/polygon_neighborhood_au5.gds | Bin 0 -> 1772 bytes testdata/ruby/dbPolygonNeighborhood.rb | 142 +++++++++++++++ 12 files changed, 313 insertions(+), 8 deletions(-) create mode 100644 src/db/unit_tests/dbPolygonNeighborhoodTests.cc create mode 100644 testdata/algo/polygon_neighborhood.gds create mode 100644 testdata/algo/polygon_neighborhood_au1.gds create mode 100644 testdata/algo/polygon_neighborhood_au2.gds create mode 100644 testdata/algo/polygon_neighborhood_au3.gds create mode 100644 testdata/algo/polygon_neighborhood_au4.gds create mode 100644 testdata/algo/polygon_neighborhood_au5.gds create mode 100644 testdata/ruby/dbPolygonNeighborhood.rb diff --git a/src/db/db/gsiDeclDbEdgeNeighborhood.cc b/src/db/db/gsiDeclDbEdgeNeighborhood.cc index 94b0a3961..9711d2897 100644 --- a/src/db/db/gsiDeclDbEdgeNeighborhood.cc +++ b/src/db/db/gsiDeclDbEdgeNeighborhood.cc @@ -231,11 +231,11 @@ static db::CompoundRegionOperationNode *new_edge_neighborhood (const std::vector return new db::EdgeNeighborhoodCompoundOperationNode (children, visitor, bext, eext, din, dout); } -gsi::ClassExt decl_CompoundRegionOperationNode_ext ( +gsi::ClassExt decl_CompoundRegionOperationNode_ext_EdgeNeighborhood ( gsi::constructor ("new_edge_neighborhood", &new_edge_neighborhood, gsi::arg ("children"), gsi::arg ("visitor"), gsi::arg ("bext", 0), gsi::arg ("eext", 0), gsi::arg ("din", 0), gsi::arg ("dout", 0), "@brief Creates a new edge neighborhood collector\n" "\n" - "@param children The inputs to use. The inputs are enumrated by base zero indexes in the visitor callback.\n" + "@param children The inputs to use. The inputs are enumerated by base zero indexes in the visitor callback.\n" "@param visitor The visitor object (see \\EdgeNeighborhoodVisitor) receiving the edge events.\n" "@param bext The search window extension to use at the edge beginning.\n" "@param eext The search window extension to use at the edge end.\n" diff --git a/src/db/db/gsiDeclDbPolygonNeighborhood.cc b/src/db/db/gsiDeclDbPolygonNeighborhood.cc index bf5d373ca..37c0ffded 100644 --- a/src/db/db/gsiDeclDbPolygonNeighborhood.cc +++ b/src/db/db/gsiDeclDbPolygonNeighborhood.cc @@ -123,17 +123,17 @@ Class decl_PolygonNeighborhoodVisitorImpl ( // --------------------------------------------------------------------------------- // PolygonNeighborhoodCompoundOperationNode binding -static db::CompoundRegionOperationNode *new_edge_neighborhood (const std::vector &children, db::PolygonNeighborhoodVisitor *visitor, const db::Coord dist) +static db::CompoundRegionOperationNode *new_polygon_neighborhood (const std::vector &children, db::PolygonNeighborhoodVisitor *visitor, const db::Coord dist) { return new db::PolygonNeighborhoodCompoundOperationNode (children, visitor, dist); } -gsi::ClassExt decl_CompoundRegionOperationNode_ext ( - gsi::constructor ("new_polygon_neighborhood", &new_edge_neighborhood, gsi::arg ("children"), gsi::arg ("visitor"), gsi::arg ("dist", 0), - "@brief Creates a new edge neighborhood collector\n" +gsi::ClassExt decl_CompoundRegionOperationNode_ext_PolygonNeighborhood ( + gsi::constructor ("new_polygon_neighborhood", &new_polygon_neighborhood, gsi::arg ("children"), gsi::arg ("visitor"), gsi::arg ("dist", 0), + "@brief Creates a new polygon neighborhood collector\n" "\n" - "@param children The inputs to use. The inputs are enumrated by base zero indexes in the visitor callback.\n" - "@param visitor The visitor object (see \\PolygonNeighborhoodVisitor) receiving the edge events.\n" + "@param children The inputs to use. The inputs are enumerated by base zero indexes in the visitor callback.\n" + "@param visitor The visitor object (see \\PolygonNeighborhoodVisitor) receiving the polygon events.\n" "@param dist The search distance in which to look up neighbors.\n" "\n" "This constructor has been introduced in version 0.30.0.\n" diff --git a/src/db/unit_tests/dbPolygonNeighborhoodTests.cc b/src/db/unit_tests/dbPolygonNeighborhoodTests.cc new file mode 100644 index 000000000..966523a42 --- /dev/null +++ b/src/db/unit_tests/dbPolygonNeighborhoodTests.cc @@ -0,0 +1,161 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbRegion.h" +#include "dbPolygonNeighborhood.h" +#include "dbReader.h" +#include "dbTestSupport.h" + +#include "tlStream.h" + +#include + +namespace +{ + +class PNPrimaryCopyVisitor + : public db::PolygonNeighborhoodVisitor +{ +public: + PNPrimaryCopyVisitor () + { + set_result_type (db::CompoundRegionOperationNode::ResultType::Region); + } + + void neighbors (const db::Layout *, const db::Cell *, const db::PolygonWithProperties &polygon, const neighbors_type &) + { + output_polygon (polygon); + } +}; + +class PNPrimaryCopyIntruderVisitor + : public db::PolygonNeighborhoodVisitor +{ +public: + PNPrimaryCopyIntruderVisitor (unsigned int input) + { + set_result_type (db::CompoundRegionOperationNode::ResultType::Edges); + m_input = input; + } + + void neighbors (const db::Layout *, const db::Cell *, const db::PolygonWithProperties &polygon, const neighbors_type &neighbors) + { + for (auto n = neighbors.begin (); n != neighbors.end (); ++n) { + if (n->first == m_input) { + for (auto p = n->second.begin (); p != n->second.end (); ++p) { + output_edge (db::Edge (polygon.box ().center (), p->box ().center ())); + } + } + } + } + +private: + unsigned int m_input; +}; + +} + +static void prep_layer (db::Layout &ly, int gds_layer, db::Region &r, db::DeepShapeStore &dss, bool deep) +{ + unsigned int li = ly.get_layer (db::LayerProperties (gds_layer, 0)); + if (deep) { + r = db::Region (db::RecursiveShapeIterator (ly, ly.cell (*ly.begin_top_down ()), li), dss); + } else { + r = db::Region (db::RecursiveShapeIterator (ly, ly.cell (*ly.begin_top_down ()), li)); + } +} + +static void run_test (tl::TestBase *_this, db::PolygonNeighborhoodVisitor &visitor, const std::string &au_name, bool deep = true, db::Coord dist = 0) +{ + db::Layout ly; + { + std::string fn (tl::testdata ()); + fn += "/algo/polygon_neighborhood.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + db::DeepShapeStore dss; + + db::Region r1, r2, r3; + prep_layer (ly, 1, r1, dss, deep); + prep_layer (ly, 2, r2, dss, deep); + prep_layer (ly, 3, r3, dss, deep); + + std::vector children; + children.push_back (new db::CompoundRegionOperationPrimaryNode ()); + children.push_back (new db::CompoundRegionOperationForeignNode ()); + children.push_back (new db::CompoundRegionOperationSecondaryNode (&r2)); + children.push_back (new db::CompoundRegionOperationSecondaryNode (&r3)); + + db::PolygonNeighborhoodCompoundOperationNode en_node (children, &visitor, dist); + + unsigned int l100 = ly.get_layer (db::LayerProperties (100, 0)); + + if (en_node.result_type () == db::CompoundRegionOperationNode::ResultType::Region) { + auto res = r1.cop_to_region (en_node); + res.insert_into (&ly, *ly.begin_top_down (), l100); + } else if (en_node.result_type () == db::CompoundRegionOperationNode::ResultType::Edges) { + auto res = r1.cop_to_edges (en_node); + res.insert_into (&ly, *ly.begin_top_down (), l100); + } else if (en_node.result_type () == db::CompoundRegionOperationNode::ResultType::EdgePairs) { + auto res = r1.cop_to_edge_pairs (en_node); + res.insert_into (&ly, *ly.begin_top_down (), l100); + } + + db::compare_layouts (_this, ly, tl::testdata () + au_name); +} + +TEST(1) +{ + PNPrimaryCopyVisitor visitor; + run_test (_this, visitor, "/algo/polygon_neighborhood_au1.gds"); +} + + +TEST(2) +{ + PNPrimaryCopyIntruderVisitor visitor (0); + run_test (_this, visitor, "/algo/polygon_neighborhood_au2.gds", true, 2000); +} + +TEST(3) +{ + PNPrimaryCopyIntruderVisitor visitor (1); + run_test (_this, visitor, "/algo/polygon_neighborhood_au3.gds", true, 2000); +} + +TEST(4) +{ + PNPrimaryCopyIntruderVisitor visitor (2); + run_test (_this, visitor, "/algo/polygon_neighborhood_au4.gds", true, 2000); +} + +TEST(5) +{ + PNPrimaryCopyIntruderVisitor visitor (3); + run_test (_this, visitor, "/algo/polygon_neighborhood_au5.gds", true, 2000); +} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 7363746df..5183520f5 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -12,6 +12,7 @@ SOURCES = \ dbFillToolTests.cc \ dbLogTests.cc \ dbObjectWithPropertiesTests.cc \ + dbPolygonNeighborhoodTests.cc \ dbRecursiveInstanceIteratorTests.cc \ dbRegionCheckUtilsTests.cc \ dbTriangleTests.cc \ diff --git a/src/rba/unit_tests/rbaTests.cc b/src/rba/unit_tests/rbaTests.cc index 01e91bda6..7b23e3940 100644 --- a/src/rba/unit_tests/rbaTests.cc +++ b/src/rba/unit_tests/rbaTests.cc @@ -102,6 +102,7 @@ RUBYTEST (dbEdgePairTest, "dbEdgePairTest.rb") RUBYTEST (dbEdgesTest, "dbEdgesTest.rb") RUBYTEST (dbEdgeTest, "dbEdgeTest.rb") RUBYTEST (dbEdgeNeighborhood, "dbEdgeNeighborhood.rb") +RUBYTEST (dbPolygonNeighborhood, "dbPolygonNeighborhood.rb") RUBYTEST (dbGlyphs, "dbGlyphs.rb") RUBYTEST (dbHierNetworkProcessorTests, "dbHierNetworkProcessorTests.rb") RUBYTEST (dbInstanceTest, "dbInstanceTest.rb") diff --git a/testdata/algo/polygon_neighborhood.gds b/testdata/algo/polygon_neighborhood.gds new file mode 100644 index 0000000000000000000000000000000000000000..6d21d6933633cb86344b19e99412874d425ee61b GIT binary patch literal 1286 zcmaizy-QqC5XH~FUGFYv;wP>W!9bc2f{KlrBKQf4fox$RR+g42^Cu)-%7D#w`X^Z0 zC|U?w2o@=}urOe;g~b-aIOopJ`U1J@YkrS&XWpHU^AM0_+n6Y1<-aJPgenFwlz&Gh z3tu*tfsta*=cS)>o2LiOh1H|!`u@%qCVIN$g|PLt29zp5c$Mkb!|M#d$AO~A$sSO? zG)^vxT)hRHu9q)S8vJiQQ2i!y(=ob)#+uy+?r4b=__<|6*RGaV$bUteeN2 zt~ZX;`Hy3)kG{6P^PY8GXPmg-{uR!Rd*d^?7}4=Q{~0}x`E3vD{QmVjyN;VvudTJc IR#ukq1Y<*Py8r+H literal 0 HcmV?d00001 diff --git a/testdata/algo/polygon_neighborhood_au1.gds b/testdata/algo/polygon_neighborhood_au1.gds new file mode 100644 index 0000000000000000000000000000000000000000..93c4064ca6fd87aadfce13120b32eba9253e10a5 GIT binary patch literal 1622 zcmbVMze}7!5PkQ3=iY%PDv_u}FkllTBu5BDgNVNZh6Hkjg`~1bVQJ}~kaU&-o9mK4 z!P3SM3qgy3MT#pd6fCZ=xI!5B?cCh$LH3M(&HI?$w>$IZ?Z6?+)-h4YN*BnGV+2EZ z9si@8xsUIcfRVg!e(~Fz55ImiW>@xJ)^*3 z!6r~TR-9ZAIe7{gTD)|O;_yHD!0499X@dw%Jp-njh87Q);EVhzb6v5qA=0df7+Tz) zpX48GDxY=a=RC*I;=S*`9@F}X%>U8X4zhk-Pv6fRnKLz@Zq16J#X~IQKhW0yG4*$& z1(Y$sE}ZW$Yq)W@=yj&n>Ai4nXL%%a$ElmE$*v{`xWs+l|f@)ZSaiuRvW23;~o=y-pO@Edatpe9wb^k%?Do8 zMXpvA*{fH-wP!?&dyMbTJM~jhy_;U)_x8J?#RDez+UdcXqVEj)_W(nS`|c+BM>8Vz zeG%f-s(Q@O;`RMB|9Xu1(brmc>{*L@jT86lTcK|38^6hg0B!H{&G_}eZ+Y13*X?&! z9XjW|_O9D&Us_tv>1?t$Y;W4IGb7?zp|hYXcRTOCvo7MR&+a(uBEHQM#s8c&UHrSu Jnv&PGh#Qp!=7S4?O~jx=EQAyyTUbO8*&@Xj!RtM9-`zaH+0AQyk2`Z_?#CGfWZ4p0m8|v?8FDny z!o~7G>REVr{|?a1$8OKPy#C<*+uqIjwJV)Bj~CGz8=?zg@lhM7)`4&&Lj~bz2H?`M zTx4SfsBIaiW<|c71)LtQZJ~Pnzj|O|Rb;zI0_V;H7y3?*M<(-kCj#)|1#N4~>zoE{&3|K6n4Pcr`#FZRj$Z9RQIb7ao!0sGdUae6$)V*cZU{-0w1 z!$bvjjB^S*ub4H$=`g2#X0OwGH`_p?!@cT-p?j^8GfUiicf?+i&9=z5ZSI{f_G&BX zm#plUAMcp02>I)o{X0Fb)s*K|(#I-ekG0z98THnH>^{__oy z?z#xMyI>x3dc3=y*6&R+KlGF+T+Ci_Ey+8&W+#HY{bC(d^5Tp^@WGEfB*WO zRmaJx*X|WwJ7ivaQuI1Z^M%wKc|5(zH}X+!$q5_nhEXbD`kKfeO{8GVYqW~hoqTRp cDi5R*`Tj}d_fzLVRebyGhtX6u?MfAY0Zam%761SM literal 0 HcmV?d00001 diff --git a/testdata/algo/polygon_neighborhood_au3.gds b/testdata/algo/polygon_neighborhood_au3.gds new file mode 100644 index 0000000000000000000000000000000000000000..a08972c7898194c2e54c5d136354ac0d85665c13 GIT binary patch literal 1826 zcmb7EJ!=$E6g@k$c{^fs6;~6N95v)>Jq_D8G@CRsTDcEeM zKOtqB1cM5(5K@S2VG%)OixgV~ukXA&cQXOsW_Fr0+;`91d(S;@;E*KCXjYQiFC<9O zKoeJr@5mDOXz?DM zXmS7jD1T#B`J5x&<#!A%KDhq=xXw>x|7TtuAm`WfczyQBo~Z$K>rES4Jj6o(qka9) zQGYj9L55LoVfPKYhCA=(yw21*p1aio>TTwFFAU7pNhX$=d;f@Bk*$`<_Z{ZW=eb@= z`Xwp)<>v=>D?@6sAS2G`VXGDv8EY?pC=BJ`sn_l5_=dGc|14j6|>3UnyZwANL07HxW?MC@u zZi#f(MTmO~>M=u$ch=+l{c-k3U+dgO&syAT9I3B&g}QNXd?sfD^u5nJ_psOR zu-`d#*qry;gWPM!)N4<3t=$xFNLEk7*BiVeKR%26d8U=mzSIh3S*o|!I`nlk>yy6% zbW>il6bqQTA@bJ`O>N3{XxYQo%4=B7k{v2*ST$4y5mlwEhWw{-&%fsKrH8G>G62L|Uy-Q7}V?;?%*RLx+wIjvaJ!6m-a@ ze?rGD6$=U#6gm{iAW}t2h9Vh?pZDA^U-ATR;#aOnx%c*U``XOv#pcV0bEuUD=$xB-&;W`V;0`4yz#UEioF7d^ z))#^DrsBku$j4KFq4DKS6p#E<4~#8~Y_*BN=`+CDj-l}ZBYKg~8P65l3nHDmh@tWR z{-}O^QT6O2-Qhik#t+WFKd$`~S^v?eePsW79?#DjSu#s@CcKhm53an9e3 z6_8%Gxqk7Sdoo}$o3XvXS%U|l=MrI z_siEetX72h^RfOL8t=Qw?<=PdU4|L^Zi8pMw|dCgllK_WGbhg#>Am)XdXQ**Tn}7m ziR{fPa#xq$>CTA8d(7ug`~7pGnwwtXcm0K-@c|=x-E^g?=r@CdtAL^Le!EfqhpQs3 zH4)<8ta{AQ_|{rnzdz3U=xgme?^)yhj3fE=t#EGK8^4LE06p*X&G_@6?|RtJ?{B}e z>##ZRwL4v}9Z;`5>dx9t@`YrTG~8b08~O5HWOqsA+bfYDE1F@L3Yfes^4k|u|0MG3 f#{a6=-LWS?X**!(W>_xWDLdO;>Z{7WzastsC(D#5 literal 0 HcmV?d00001 diff --git a/testdata/algo/polygon_neighborhood_au5.gds b/testdata/algo/polygon_neighborhood_au5.gds new file mode 100644 index 0000000000000000000000000000000000000000..1bae54dcee5cf677f1ee358d808b95c643e5d710 GIT binary patch literal 1772 zcmbVMJ!n%=6#nvFdK0lV6{D>fs6_`+(ISFBh_qUvqF{y$#i@fs2M0$7M+e;;1($3( zI68FfQn8>=2cd&V5Qi!vL5c(^9`AQgPVxkA()2CgHhx3KYqUBex8GhSzE9nW2@0;M{0y%&1s>LiEenR~TQuE=Utp8JxH{8 zoDZC8h-}U&a#v^G=+1~1_n57pcIT&}TANg z@6L-fmPLr0bLufei#L|z{H<~JM_=pQSO2h5tQIC^nMgI6JxxrtTv_e0Y$fvg=KkkZr zekJnl;b1Ia#)ljtQPg(|3KvTQR4P$sq3iy&RBzK$bTB$ Lot>-Tx1Gmdb%w~C literal 0 HcmV?d00001 diff --git a/testdata/ruby/dbPolygonNeighborhood.rb b/testdata/ruby/dbPolygonNeighborhood.rb new file mode 100644 index 000000000..a5755d4cc --- /dev/null +++ b/testdata/ruby/dbPolygonNeighborhood.rb @@ -0,0 +1,142 @@ +# encoding: UTF-8 + +# KLayout Layout Viewer +# Copyright (C) 2006-2025 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 MyVisitor < RBA::PolygonNeighborhoodVisitor + + def initialize + @log = {} + @current_log = nil + end + + def log + @log.keys.sort { |a,b| a < b ? -1 : (a == b ? 0 : 1) }.collect { |k| @log[k].join("") }.join("") + end + + def neighbors(layout, cell, polygon, neighborhood) + polygon = polygon.dup + output(polygon) + @log[polygon] ||= [] + @current_log = @log[polygon] + @current_log << "Polygon: #{polygon}\n" + neighborhood.each do |inp, poly| + poly_str = poly.collect { |p| p.to_s }.join("/") + @current_log << " #{inp}: #{poly_str}\n" + end + end + +end + +class MyVisitor2 < RBA::PolygonNeighborhoodVisitor + + def initialize + self.result_type = RBA::CompoundRegionOperationNode::ResultType::Edges + end + + def neighbors(layout, cell, polygon, neighborhood) + neighborhood.each do |inp, poly| + poly.each do |p| + e = RBA::Edge::new(polygon.bbox.center, p.bbox.center) + output(e) + end + end + end + +end + +class DBPolygonNeighborhood_TestClass < TestBase + + # basic events + def test_1 + + ly = RBA::Layout::new + + l1 = ly.layer(1, 0) + cell = ly.create_cell("TOP") + + pid1 = RBA::Layout::properties_id({ 1 => "one" }) + + cell.shapes(l1).insert(RBA::Box::new(0, 0, 1000, 1000), pid1) + cell.shapes(l1).insert(RBA::Box::new(-1100, 0, -100, 1000)) + + prim = RBA::Region::new(cell.begin_shapes_rec(l1)) + prim.enable_properties + + visitor = MyVisitor::new + + visitor.result_type = RBA::CompoundRegionOperationNode::ResultType::Region + assert_equal(visitor.result_type, RBA::CompoundRegionOperationNode::ResultType::Region) + + dist = 101 + + children = [ + RBA::CompoundRegionOperationNode::new_foreign + ] + + node = RBA::CompoundRegionOperationNode::new_polygon_neighborhood(children, visitor, dist) + res = prim.complex_op(node) + + assert_equal(visitor.log, + "Polygon: (-1100,0;-1100,1000;-100,1000;-100,0) props={}\n" + + " 0: (0,0;0,1000;1000,1000;1000,0) props={1=>one}\n" + + "Polygon: (0,0;0,1000;1000,1000;1000,0) props={1=>one}\n" + + " 0: (-1100,0;-1100,1000;-100,1000;-100,0) props={}\n" + ) + + assert_equal(res.to_s, "(-1100,0;-1100,1000;-100,1000;-100,0);(0,0;0,1000;1000,1000;1000,0){1=>one}") + + end + + # edge pair output, to_original_trans + def test_2 + + ly = RBA::Layout::new + + l1 = ly.layer(1, 0) + cell = ly.create_cell("TOP") + + cell.shapes(l1).insert(RBA::Box::new(0, 0, 1000, 1000)) + cell.shapes(l1).insert(RBA::Box::new(-1100, 0, -100, 1000)) + + prim = RBA::Region::new(cell.begin_shapes_rec(l1)) + + visitor = MyVisitor2::new + + dist = 101 + + children = [ + RBA::CompoundRegionOperationNode::new_foreign + ] + + node = RBA::CompoundRegionOperationNode::new_polygon_neighborhood(children, visitor, dist) + res = prim.complex_op(node) + + assert_equal(res.to_s, "(-600,500;500,500);(500,500;-600,500)") + + end + +end + +load("test_epilogue.rb") + From c09184989fcb4a104c5388ddc97ff53520c6f481 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 4 Feb 2025 23:59:04 +0100 Subject: [PATCH 012/392] Avoiding call ambiguities when creating Edges, Region, EdgePairs and Texts from arrays of objects with properties. --- src/db/db/gsiDeclDbEdgePairs.cc | 13 ++++++------ src/db/db/gsiDeclDbEdges.cc | 35 ++++++++++++++++---------------- src/db/db/gsiDeclDbRegion.cc | 13 ++++++------ src/db/db/gsiDeclDbTexts.cc | 13 ++++++------ testdata/ruby/dbEdgePairsTest.rb | 3 +++ testdata/ruby/dbEdgesTest.rb | 3 +++ testdata/ruby/dbRegionTest.rb | 3 +++ testdata/ruby/dbTextsTest.rb | 3 +++ 8 files changed, 48 insertions(+), 38 deletions(-) diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 20f1817dc..e5717d08d 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -230,7 +230,7 @@ static db::EdgePairs *new_a (const std::vector &pairs) return new db::EdgePairs (pairs.begin (), pairs.end ()); } -static db::EdgePairs *new_ap (const std::vector &pairs) +static db::EdgePairs *new_ap (const std::vector &pairs, bool) { return new db::EdgePairs (pairs.begin (), pairs.end ()); } @@ -592,12 +592,11 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This constructor has been introduced in version 0.26." ) + - constructor ("new", &new_ap, gsi::arg ("array"), - "@brief Constructor from an array of edge pairs with properties\n" - "\n" - "This constructor creates an edge pair collection from an array of \\EdgePairWithProperties objects.\n" - "\n" - "This constructor has been introduced in version 0.30." + // This is a dummy constructor that allows creating a EdgePairs collection from an array + // of EdgePairWithProperties objects too. GSI needs the dummy argument to + // differentiate between the cases when an empty array is passed. + constructor ("new", &new_ap, gsi::arg ("array"), gsi::arg ("dummy", true), + "@hide" ) + constructor ("new", &new_ep, gsi::arg ("edge_pair"), "@brief Constructor from a single edge pair object\n" diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 31d66458c..73dc5be84 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -265,12 +265,12 @@ static db::Edges *new_ep (const db::EdgeWithProperties &e) return ee; } -static db::Edges *new_a1 (const std::vector &a) +static db::Edges *new_a1 (const std::vector &a, bool) { return new db::Edges (a.begin (), a.end ()); } -static db::Edges *new_a1p (const std::vector &a) +static db::Edges *new_a1p (const std::vector &a, bool) { return new db::Edges (a.begin (), a.end ()); } @@ -280,7 +280,7 @@ static db::Edges *new_a2 (const std::vector &a) return new db::Edges (a.begin (), a.end ()); } -static db::Edges *new_a2p (const std::vector &a) +static db::Edges *new_a2p (const std::vector &a, bool) { return new db::Edges (a.begin (), a.end ()); } @@ -768,31 +768,32 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "This variant has been introduced in version 0.30." ) + - constructor ("new", &new_a1, gsi::arg ("array"), + constructor ("new", &new_a1, gsi::arg ("array"), gsi::arg ("dummy", true), "@brief Constructor from a polygon array\n" "\n" "This constructor creates an edge collection from an array of polygons.\n" "The edges form the contours of the polygons.\n" + "\n" + "The dummy argument is needed internally to differentiate the constructors " + "taking arrays of polygons and edges in case of empty arrays. Do not specify " + "this argument." ) + - constructor ("new", &new_a1p, gsi::arg ("array"), - "@brief Constructor from a polygon array\n" - "\n" - "This constructor creates an edge collection from an array of polygons with properties.\n" - "The edges form the contours of the polygons.\n" - "\n" - "This variant has been introduced in version 0.30." + // This is a dummy constructor that allows creating an Edges collection from an array + // of PolygonWithProperties objects too. GSI needs the dummy argument to + // differentiate between the cases when an empty array is passed. + constructor ("new", &new_a1p, gsi::arg ("array"), gsi::arg ("dummy", true), + "@hide" ) + constructor ("new", &new_a2, gsi::arg ("array"), "@brief Constructor from an edge array\n" "\n" "This constructor creates an edge collection from an array of edges.\n" ) + - constructor ("new", &new_a2p, gsi::arg ("array"), - "@brief Constructor from an edge array\n" - "\n" - "This constructor creates an edge collection from an array of edges with properties.\n" - "\n" - "This variant has been introduced in version 0.30." + // This is a dummy constructor that allows creating an Edges collection from an array + // of EdgeWithProperties objects too. GSI needs the dummy argument to + // differentiate between the cases when an empty array is passed. + constructor ("new", &new_a2p, gsi::arg ("array"), gsi::arg ("dummy", true), + "@hide" ) + constructor ("new", &new_b, gsi::arg ("box"), "@brief Box constructor\n" diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 2cf35d992..4b41acf78 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -250,7 +250,7 @@ static db::Region *new_a (const std::vector &a) return new db::Region (a.begin (), a.end ()); } -static db::Region *new_ap (const std::vector &a) +static db::Region *new_ap (const std::vector &a, bool) { return new db::Region (a.begin (), a.end ()); } @@ -1206,12 +1206,11 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This constructor creates a region from an array of polygons.\n" ) + - constructor ("new", &new_ap, gsi::arg ("array"), - "@brief Constructor from an array of polygons with properties\n" - "\n" - "This constructor creates a region from an array of polygons with properties.\n" - "\n" - "This variant has been introduced in version 0.30." + // This is a dummy constructor that allows creating a Region from an array + // of PolygonWithProperties objects too. GSI needs the dummy argument to + // differentiate between the cases when an empty array is passed. + constructor ("new", &new_ap, gsi::arg ("array"), gsi::arg ("dummy", true), + "@hide" ) + constructor ("new", &new_b, gsi::arg ("box"), "@brief Box constructor\n" diff --git a/src/db/db/gsiDeclDbTexts.cc b/src/db/db/gsiDeclDbTexts.cc index a683ec7b4..7cef8695e 100644 --- a/src/db/db/gsiDeclDbTexts.cc +++ b/src/db/db/gsiDeclDbTexts.cc @@ -197,7 +197,7 @@ static db::Texts *new_a (const std::vector &t) return new db::Texts (t.begin (), t.end ()); } -static db::Texts *new_ap (const std::vector &t) +static db::Texts *new_ap (const std::vector &t, bool) { return new db::Texts (t.begin (), t.end ()); } @@ -375,12 +375,11 @@ Class decl_Texts (decl_dbShapeCollection, "db", "Texts", "\n" "This constructor creates an text collection from an array of \\Text objects.\n" ) + - constructor ("new", &new_ap, gsi::arg ("array"), - "@brief Constructor from an array with texts with properties\n" - "\n" - "This constructor creates an text collection from an array of \\TextWithProperties objects.\n" - "\n" - "This variant has been introduced in version 0.30." + // This is a dummy constructor that allows creating a Texts collection from an array + // of TextWithProperties objects too. GSI needs the dummy argument to + // differentiate between the cases when an empty array is passed. + constructor ("new", &new_ap, gsi::arg ("array"), gsi::arg ("dummy", true), + "@hide" ) + constructor ("new", &new_text, gsi::arg ("text"), "@brief Constructor from a single text object\n" diff --git a/testdata/ruby/dbEdgePairsTest.rb b/testdata/ruby/dbEdgePairsTest.rb index 3c1036dcb..ada31d850 100644 --- a/testdata/ruby/dbEdgePairsTest.rb +++ b/testdata/ruby/dbEdgePairsTest.rb @@ -580,6 +580,9 @@ class DBEdgePairs_TestClass < TestBase r = RBA::EdgePairs::new([ RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500)), { 1 => "one" }) ]) assert_equal(r.to_s, "(0,0;100,100)/(200,300;200,500){1=>one}") + r = RBA::EdgePairs::new([]) + assert_equal(r.to_s, "") + r = RBA::EdgePairs::new(RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500)), { 1 => "one" })) assert_equal(r.to_s, "(0,0;100,100)/(200,300;200,500){1=>one}") diff --git a/testdata/ruby/dbEdgesTest.rb b/testdata/ruby/dbEdgesTest.rb index d125a22e7..2e17948a4 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -961,6 +961,9 @@ class DBEdges_TestClass < TestBase r = RBA::Edges::new([ RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 100), { 1 => "one" }) ]) assert_equal(r.to_s, "(0,0;100,100){1=>one}") + r = RBA::Edges::new([]) + assert_equal(r.to_s, "") + r = RBA::Edges::new r.insert([ RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 100), { 1 => "one" }) ]) assert_equal(r.to_s, "(0,0;100,100){1=>one}") diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 09a556eea..78ee4766c 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -1534,6 +1534,9 @@ class DBRegion_TestClass < TestBase r = RBA::Region::new([ RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" }) ]) assert_equal(r.to_s, "(0,0;0,200;100,200;100,0){1=>one}") + r = RBA::Region::new([]) + assert_equal(r.to_s, "") + r = RBA::Region::new r.insert([ RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" }) ]) assert_equal(r.to_s, "(0,0;0,200;100,200;100,0){1=>one}") diff --git a/testdata/ruby/dbTextsTest.rb b/testdata/ruby/dbTextsTest.rb index f2c78403f..af2061531 100644 --- a/testdata/ruby/dbTextsTest.rb +++ b/testdata/ruby/dbTextsTest.rb @@ -449,6 +449,9 @@ class DBTexts_TestClass < TestBase r = RBA::Texts::new([ RBA::TextWithProperties::new(RBA::Text::new("abc", RBA::Trans::new), { 1 => "one" }) ]) assert_equal(r.to_s, "('abc',r0 0,0){1=>one}") + r = RBA::Texts::new([]) + assert_equal(r.to_s, "") + r = RBA::Texts::new(RBA::TextWithProperties::new(RBA::Text::new("abc", RBA::Trans::new), { 1 => "one" })) assert_equal(r.to_s, "('abc',r0 0,0){1=>one}") From aad90d0c16785abb1fae6459935c7313c7b1131d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 5 Feb 2025 00:02:04 +0100 Subject: [PATCH 013/392] Fixed a name clash in PolygonNeighborhood unit tests --- testdata/ruby/dbPolygonNeighborhood.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/testdata/ruby/dbPolygonNeighborhood.rb b/testdata/ruby/dbPolygonNeighborhood.rb index a5755d4cc..e5d5aaf67 100644 --- a/testdata/ruby/dbPolygonNeighborhood.rb +++ b/testdata/ruby/dbPolygonNeighborhood.rb @@ -23,7 +23,7 @@ end load("test_prologue.rb") -class MyVisitor < RBA::PolygonNeighborhoodVisitor +class PMyVisitor < RBA::PolygonNeighborhoodVisitor def initialize @log = {} @@ -48,7 +48,7 @@ class MyVisitor < RBA::PolygonNeighborhoodVisitor end -class MyVisitor2 < RBA::PolygonNeighborhoodVisitor +class PMyVisitor2 < RBA::PolygonNeighborhoodVisitor def initialize self.result_type = RBA::CompoundRegionOperationNode::ResultType::Edges @@ -83,7 +83,7 @@ class DBPolygonNeighborhood_TestClass < TestBase prim = RBA::Region::new(cell.begin_shapes_rec(l1)) prim.enable_properties - visitor = MyVisitor::new + visitor = PMyVisitor::new visitor.result_type = RBA::CompoundRegionOperationNode::ResultType::Region assert_equal(visitor.result_type, RBA::CompoundRegionOperationNode::ResultType::Region) @@ -121,7 +121,7 @@ class DBPolygonNeighborhood_TestClass < TestBase prim = RBA::Region::new(cell.begin_shapes_rec(l1)) - visitor = MyVisitor2::new + visitor = PMyVisitor2::new dist = 101 From 502346608fbe4de89f3959d358ba747f8ab12859 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 9 Feb 2025 22:44:34 +0100 Subject: [PATCH 014/392] [consider merging] Bugfix: fixing a crash when editing PCell parameters (QR code PCell in this case) while the macro editor is open --- src/edt/edt/edtPCellParametersPage.cc | 7 +++++++ src/edt/edt/edtPCellParametersPage.h | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/src/edt/edt/edtPCellParametersPage.cc b/src/edt/edt/edtPCellParametersPage.cc index d68c0b319..afbf327f6 100644 --- a/src/edt/edt/edtPCellParametersPage.cc +++ b/src/edt/edt/edtPCellParametersPage.cc @@ -706,6 +706,13 @@ PCellParametersPage::parameter_changed () dm_parameter_changed (); } +void +PCellParametersPage::deleteLater () +{ + dm_parameter_changed.cancel (); + QFrame::deleteLater (); +} + void PCellParametersPage::do_parameter_changed () { diff --git a/src/edt/edt/edtPCellParametersPage.h b/src/edt/edt/edtPCellParametersPage.h index a6c862452..c976dc254 100644 --- a/src/edt/edt/edtPCellParametersPage.h +++ b/src/edt/edt/edtPCellParametersPage.h @@ -139,6 +139,11 @@ public: */ void set_parameters (const std::vector &values); + /** + * @brief Reimplementation of deleteLater + */ + void deleteLater (); + signals: void edited (); From e48c6f3660cefcccba5a3c4108ffaa9b3f2351dc Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 14 Feb 2025 22:11:18 +0100 Subject: [PATCH 015/392] Edges#each, Edges#each_merged, Region#each, Region#each_merged, Texts#each and EdgePairs#each now deliver objects with properties too. --- src/db/db/dbFlatEdges.cc | 2 +- src/db/db/gsiDeclDbEdgePairs.cc | 9 ++++++++- src/db/db/gsiDeclDbEdges.cc | 16 ++++++++++++++-- src/db/db/gsiDeclDbRegion.cc | 17 +++++++++++++++-- src/db/db/gsiDeclDbTexts.cc | 9 ++++++++- testdata/ruby/dbEdgePairsTest.rb | 6 ++++++ testdata/ruby/dbEdgesTest.rb | 28 ++++++++++++++++++++++------ testdata/ruby/dbRegionTest.rb | 22 ++++++++++++++++------ testdata/ruby/dbTextsTest.rb | 6 ++++++ 9 files changed, 96 insertions(+), 19 deletions(-) diff --git a/src/db/db/dbFlatEdges.cc b/src/db/db/dbFlatEdges.cc index cae04d9cf..dbd40a87a 100644 --- a/src/db/db/dbFlatEdges.cc +++ b/src/db/db/dbFlatEdges.cc @@ -127,7 +127,7 @@ FlatEdges::ensure_merged_edges_valid () const if (! need_split_props) { - EdgeBooleanClusterCollectorToShapes cluster_collector (&tmp, EdgeOr); + EdgeBooleanClusterCollectorToShapes cluster_collector (&tmp, EdgeOr, prop_id); scanner.reserve (mp_edges->size ()); diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index e5717d08d..3ac118dd5 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -577,6 +577,11 @@ static db::EdgePairs with_area2 (const db::EdgePairs *r, db::EdgePair::area_type return r->filtered (f); } +static db::generic_shape_iterator begin_edge_pairs (const db::EdgePairs *edge_pairs) +{ + return db::generic_shape_iterator (db::make_wp_iter (edge_pairs->delegate ()->begin ())); +} + extern Class decl_dbShapeCollection; Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", @@ -1499,8 +1504,10 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been introduced in version 0.27." ) + - gsi::iterator ("each", &db::EdgePairs::begin, + gsi::iterator_ext ("each", &begin_edge_pairs, "@brief Returns each edge pair of the edge pair collection\n" + "\n" + "Starting with version 0.30, the iterator delivers EdgePairWithProperties objects." ) + method ("[]", &db::EdgePairs::nth, gsi::arg ("n"), "@brief Returns the nth edge pair\n" diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 73dc5be84..0d8f307ad 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -745,6 +745,15 @@ static std::vector split_interacting_with_region (const db::Edges *r, return as_2edges_vector (r->selected_interacting_differential (other, min_count, max_count)); } +static db::generic_shape_iterator begin_edges (const db::Edges *edges) +{ + return db::generic_shape_iterator (db::make_wp_iter (edges->delegate ()->begin ())); +} + +static db::generic_shape_iterator begin_edges_merged (const db::Edges *edges) +{ + return db::generic_shape_iterator (db::make_wp_iter (edges->delegate ()->begin_merged ())); +} extern Class decl_dbShapeCollection; @@ -2266,15 +2275,18 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "This method has been introduced in version 0.27." ) + - gsi::iterator ("each", &db::Edges::begin, + gsi::iterator_ext ("each", &begin_edges, "@brief Returns each edge of the region\n" + "\n" + "Starting with version 0.30, the iterator delivers an EdgeWithProperties object." ) + - gsi::iterator ("each_merged", &db::Edges::begin_merged, + gsi::iterator_ext ("each_merged", &begin_edges_merged, "@brief Returns each edge of the region\n" "\n" "In contrast to \\each, this method delivers merged edges if merge semantics applies while \\each delivers the original edges only.\n" "\n" "This method has been introduced in version 0.25." + "Starting with version 0.30, the iterator delivers an EdgeWithProperties object." ) + method ("[]", &db::Edges::nth, gsi::arg ("n"), "@brief Returns the nth edge of the collection\n" diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 4b41acf78..b3cb7bcff 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -1168,6 +1168,16 @@ rasterize1 (const db::Region *region, const db::Point &origin, const db::Vector return rasterize2 (region, origin, pixel_size, pixel_size, nx, ny); } +static db::generic_shape_iterator begin_region (const db::Region *region) +{ + return db::generic_shape_iterator (db::make_wp_iter (region->delegate ()->begin ())); +} + +static db::generic_shape_iterator begin_region_merged (const db::Region *region) +{ + return db::generic_shape_iterator (db::make_wp_iter (region->delegate ()->begin_merged ())); +} + static tl::Variant begin_shapes_rec (const db::Region *region) { auto res = region->begin_iter (); @@ -3725,15 +3735,18 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This method has been introduced in version 0.27." ) + - iterator ("each", &db::Region::begin, + iterator_ext ("each", &begin_region, "@brief Returns each polygon of the region\n" "\n" "This returns the raw polygons (not merged polygons if merged semantics is enabled).\n" + "\n" + "Starting with version 0.30, the iterator delivers a RegionWithProperties object." ) + - iterator ("each_merged", &db::Region::begin_merged, + iterator_ext ("each_merged", &begin_region_merged, "@brief Returns each merged polygon of the region\n" "\n" "This returns the raw polygons if merged semantics is disabled or the merged ones if merged semantics is enabled.\n" + "Starting with version 0.30, the iterator delivers a RegionWithProperties object." ) + method ("[]", &db::Region::nth, gsi::arg ("n"), "@brief Returns the nth polygon of the region\n" diff --git a/src/db/db/gsiDeclDbTexts.cc b/src/db/db/gsiDeclDbTexts.cc index 7cef8695e..0de49ade6 100644 --- a/src/db/db/gsiDeclDbTexts.cc +++ b/src/db/db/gsiDeclDbTexts.cc @@ -362,6 +362,11 @@ static db::Region pull_interacting (const db::Texts *r, const db::Region &other) return out; } +static db::generic_shape_iterator begin_texts (const db::Texts *texts) +{ + return db::generic_shape_iterator (db::make_wp_iter (texts->delegate ()->begin ())); +} + extern Class decl_dbShapeCollection; Class decl_Texts (decl_dbShapeCollection, "db", "Texts", @@ -721,8 +726,10 @@ Class decl_Texts (decl_dbShapeCollection, "db", "Texts", "\n" "This method has been introduced in version 0.27." ) + - gsi::iterator ("each", &db::Texts::begin, + gsi::iterator_ext ("each", &begin_texts, "@brief Returns each text of the text collection\n" + "\n" + "Starting with version 0.30, the iterator delivers TextWithProperties objects." ) + method ("[]", &db::Texts::nth, gsi::arg ("n"), "@brief Returns the nth text\n" diff --git a/testdata/ruby/dbEdgePairsTest.rb b/testdata/ruby/dbEdgePairsTest.rb index ada31d850..829b28d95 100644 --- a/testdata/ruby/dbEdgePairsTest.rb +++ b/testdata/ruby/dbEdgePairsTest.rb @@ -590,6 +590,12 @@ class DBEdgePairs_TestClass < TestBase r.insert(RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500)), { 1 => "one" })) assert_equal(r.to_s, "(0,0;100,100)/(200,300;200,500){1=>one}") + r = RBA::EdgePairs::new + r.insert(RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500)), { 1 => "one" })) + r.insert(RBA::EdgePair::new(RBA::Edge::new(0, 10, 100, 110), RBA::Edge::new(220, 300, 220, 500))) + s = r.each.collect(&:to_s).join(";") + assert_equal(s, "(0,10;100,110)/(220,300;220,500) props={};(0,0;100,100)/(200,300;200,500) props={1=>one}") + end end diff --git a/testdata/ruby/dbEdgesTest.rb b/testdata/ruby/dbEdgesTest.rb index 2e17948a4..590c247a9 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -129,12 +129,8 @@ class DBEdges_TestClass < TestBase r.assign(RBA::Edges::new(RBA::Box::new(10, 20, 100, 200))) assert_equal(csort(r.to_s), csort("(10,20;10,200);(10,200;100,200);(100,200;100,20);(100,20;10,20)")) - s = "" - r.each do |e| - s.empty? || s += ";" - s += e.to_s - end - assert_equal(s, "(10,20;10,200);(10,200;100,200);(100,200;100,20);(100,20;10,20)") + s = r.each.collect(&:to_s).join(";") + assert_equal(s, "(10,20;10,200) props={};(10,200;100,200) props={};(100,200;100,20) props={};(100,20;10,20) props={}") assert_equal(r.is_empty?, false) assert_equal(r.count, 4) assert_equal(r.hier_count, 4) @@ -1019,6 +1015,26 @@ class DBEdges_TestClass < TestBase r.insert(s) assert_equal(r.to_s, "(0,0;100,100){1=>one}") + r = RBA::Edges::new + s = RBA::Shapes::new + s.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 100), { 1 => "one" })) + r.insert(s) + assert_equal(r.to_s, "(0,0;100,100){1=>one}") + + r = RBA::Edges::new + r.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 0), { 1 => "one" })) + r.insert(RBA::Edge::new(10, 0, 110, 0)) + s = r.each.collect(&:to_s).join(";") + assert_equal(s, "(10,0;110,0) props={};(0,0;100,0) props={1=>one}") + s = r.each_merged.collect(&:to_s).join(";") + assert_equal(s, "(10,0;110,0) props={};(0,0;100,0) props={1=>one}") + + r = RBA::Edges::new + r.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 0), { 1 => "one" })) + r.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(10, 0, 110, 0), { 1 => "one" })) + s = r.each_merged.collect(&:to_s).join(";") + assert_equal(s, "(0,0;110,0) props={1=>one}") + end end diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 78ee4766c..9cb46a1dd 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -180,12 +180,8 @@ class DBRegion_TestClass < TestBase RBA::Polygon::new(RBA::Box::new(20, 50, 120, 250)) ] ) assert_equal(csort(r.to_s), csort("(10,20;10,200;100,200;100,20);(20,50;20,250;120,250;120,50)")) - s = "" - r.each do |p| - s.empty? || s += ";" - s += p.to_s - end - assert_equal(s, "(10,20;10,200;100,200;100,20);(20,50;20,250;120,250;120,50)") + s = r.each.collect(&:to_s).join(";") + assert_equal(s, "(10,20;10,200;100,200;100,20) props={};(20,50;20,250;120,250;120,50) props={}") assert_equal(r.merged.to_s, "(10,20;10,200;20,200;20,250;120,250;120,50;100,50;100,20)") assert_equal(r.merged(false, 1).to_s, "(10,20;10,200;20,200;20,250;120,250;120,50;100,50;100,20)") assert_equal(r.merged(1).to_s, "(10,20;10,200;20,200;20,250;120,250;120,50;100,50;100,20)") @@ -1571,6 +1567,20 @@ class DBRegion_TestClass < TestBase r.insert(s) assert_equal(r.to_s, "(0,0;0,200;100,200;100,0){1=>one}") + r = RBA::Region::new + r.insert(RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" })) + r.insert(RBA::Box::new(10, 20, 110, 220)) + s = r.each.collect(&:to_s).join(";") + assert_equal(s, "(10,20;10,220;110,220;110,20) props={};(0,0;0,200;100,200;100,0) props={1=>one}") + s = r.each_merged.collect(&:to_s).join(";") + assert_equal(s, "(10,20;10,220;110,220;110,20) props={};(0,0;0,200;100,200;100,0) props={1=>one}") + + r = RBA::Region::new + r.insert(RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" })) + r.insert(RBA::BoxWithProperties::new(RBA::Box::new(10, 20, 110, 220), { 1 => "one" })) + s = r.each_merged.collect(&:to_s).join(";") + assert_equal(s, "(0,0;0,200;10,200;10,220;110,220;110,20;100,20;100,0) props={1=>one}") + end end diff --git a/testdata/ruby/dbTextsTest.rb b/testdata/ruby/dbTextsTest.rb index af2061531..0ddfabc02 100644 --- a/testdata/ruby/dbTextsTest.rb +++ b/testdata/ruby/dbTextsTest.rb @@ -459,6 +459,12 @@ class DBTexts_TestClass < TestBase r.insert(RBA::TextWithProperties::new(RBA::Text::new("abc", RBA::Trans::new), { 1 => "one" })) assert_equal(r.to_s, "('abc',r0 0,0){1=>one}") + r = RBA::Texts::new + r.insert(RBA::TextWithProperties::new(RBA::Text::new("abc", RBA::Trans::new), { 1 => "one" })) + r.insert(RBA::Text::new("xuv", RBA::Trans::new)) + s = r.each.collect(&:to_s).join(";") + assert_equal(s, "('xuv',r0 0,0) props={};('abc',r0 0,0) props={1=>one}") + end end From 54242bc848fa0b0ad6dd8cba22176e5bb73d308e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 15 Feb 2025 18:03:15 +0100 Subject: [PATCH 016/392] Fixed some issues with operator+ and operator+= on Texts, Edges, Regions and EdgePairs with properties --- src/db/db/dbAsIfFlatEdgePairs.cc | 8 ---- src/db/db/dbAsIfFlatEdges.cc | 26 +++++----- src/db/db/dbAsIfFlatRegion.cc | 26 +++++----- src/db/db/dbAsIfFlatTexts.cc | 26 +++++----- src/db/db/dbDeepEdgePairs.cc | 9 ++-- src/db/db/dbDeepEdges.cc | 6 ++- src/db/db/dbDeepRegion.cc | 1 + src/db/db/dbDeepTexts.cc | 8 +++- src/db/db/dbEdgePairs.cc | 7 +++ src/db/db/dbEdgePairs.h | 6 +++ src/db/db/dbEdges.cc | 7 +++ src/db/db/dbEdges.h | 6 +++ src/db/db/dbFlatEdges.cc | 28 +++++------ src/db/db/dbFlatRegion.cc | 26 ++++------ src/db/db/dbFlatTexts.cc | 28 +++++------ src/db/db/dbTexts.cc | 7 +++ src/db/db/dbTexts.h | 6 +++ src/db/unit_tests/dbEdgePairsTests.cc | 69 +++++++++++++++++++++++++++ src/db/unit_tests/dbEdgesTests.cc | 68 ++++++++++++++++++++++++++ src/db/unit_tests/dbRegionTests.cc | 69 +++++++++++++++++++++++++++ src/db/unit_tests/dbTextsTests.cc | 68 ++++++++++++++++++++++++++ 21 files changed, 410 insertions(+), 95 deletions(-) diff --git a/src/db/db/dbAsIfFlatEdgePairs.cc b/src/db/db/dbAsIfFlatEdgePairs.cc index 568ea3efb..f442979c4 100644 --- a/src/db/db/dbAsIfFlatEdgePairs.cc +++ b/src/db/db/dbAsIfFlatEdgePairs.cc @@ -630,10 +630,6 @@ AsIfFlatEdgePairs::add (const EdgePairs &other) const std::unique_ptr new_edge_pairs (new FlatEdgePairs (*other_flat)); new_edge_pairs->invalidate_cache (); - size_t n = new_edge_pairs->raw_edge_pairs ().size () + count (); - - new_edge_pairs->reserve (n); - for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { db::properties_id_type prop_id = p.prop_id (); if (prop_id) { @@ -649,10 +645,6 @@ AsIfFlatEdgePairs::add (const EdgePairs &other) const std::unique_ptr new_edge_pairs (new FlatEdgePairs ()); - size_t n = count () + other.count (); - - new_edge_pairs->reserve (n); - for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { db::properties_id_type prop_id = p.prop_id (); if (prop_id) { diff --git a/src/db/db/dbAsIfFlatEdges.cc b/src/db/db/dbAsIfFlatEdges.cc index 6a4b51e06..13ee3b066 100644 --- a/src/db/db/dbAsIfFlatEdges.cc +++ b/src/db/db/dbAsIfFlatEdges.cc @@ -1012,12 +1012,12 @@ AsIfFlatEdges::add (const Edges &other) const new_edges->set_is_merged (false); new_edges->invalidate_cache (); - size_t n = new_edges->raw_edges ().size () + count (); - - new_edges->reserve (n); - for (EdgesIterator p (begin ()); ! p.at_end (); ++p) { - new_edges->raw_edges ().insert (*p); + if (p.prop_id () == 0) { + new_edges->raw_edges ().insert (*p); + } else { + new_edges->raw_edges ().insert (db::EdgeWithProperties (*p, p.prop_id ())); + } } return new_edges.release (); @@ -1026,15 +1026,19 @@ AsIfFlatEdges::add (const Edges &other) const std::unique_ptr new_edges (new FlatEdges (false /*not merged*/)); - size_t n = count () + other.count (); - - new_edges->reserve (n); - for (EdgesIterator p (begin ()); ! p.at_end (); ++p) { - new_edges->raw_edges ().insert (*p); + if (p.prop_id () == 0) { + new_edges->raw_edges ().insert (*p); + } else { + new_edges->raw_edges ().insert (db::EdgeWithProperties (*p, p.prop_id ())); + } } for (EdgesIterator p (other.begin ()); ! p.at_end (); ++p) { - new_edges->raw_edges ().insert (*p); + if (p.prop_id () == 0) { + new_edges->raw_edges ().insert (*p); + } else { + new_edges->raw_edges ().insert (db::EdgeWithProperties (*p, p.prop_id ())); + } } return new_edges.release (); diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index c4cc80695..d69f280e3 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -1821,12 +1821,12 @@ AsIfFlatRegion::add (const Region &other) const new_region->set_is_merged (false); new_region->invalidate_cache (); - size_t n = new_region->raw_polygons ().size () + count (); - - new_region->reserve (n); - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - new_region->raw_polygons ().insert (*p); + if (p.prop_id () == 0) { + new_region->raw_polygons ().insert (*p); + } else { + new_region->raw_polygons ().insert (db::PolygonWithProperties (*p, p.prop_id ())); + } } return new_region.release (); @@ -1835,15 +1835,19 @@ AsIfFlatRegion::add (const Region &other) const std::unique_ptr new_region (new FlatRegion (false /*not merged*/)); - size_t n = count () + other.count (); - - new_region->reserve (n); - for (RegionIterator p (begin ()); ! p.at_end (); ++p) { - new_region->raw_polygons ().insert (*p); + if (p.prop_id () == 0) { + new_region->raw_polygons ().insert (*p); + } else { + new_region->raw_polygons ().insert (db::PolygonWithProperties (*p, p.prop_id ())); + } } for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { - new_region->raw_polygons ().insert (*p); + if (p.prop_id () == 0) { + new_region->raw_polygons ().insert (*p); + } else { + new_region->raw_polygons ().insert (db::PolygonWithProperties (*p, p.prop_id ())); + } } return new_region.release (); diff --git a/src/db/db/dbAsIfFlatTexts.cc b/src/db/db/dbAsIfFlatTexts.cc index 56d5cc803..de06092f0 100644 --- a/src/db/db/dbAsIfFlatTexts.cc +++ b/src/db/db/dbAsIfFlatTexts.cc @@ -247,12 +247,12 @@ AsIfFlatTexts::add (const Texts &other) const std::unique_ptr new_texts (new FlatTexts (*other_flat)); new_texts->invalidate_cache (); - size_t n = new_texts->raw_texts ().size () + count (); - - new_texts->reserve (n); - for (TextsIterator p (begin ()); ! p.at_end (); ++p) { - new_texts->raw_texts ().insert (*p); + if (p.prop_id () == 0) { + new_texts->raw_texts ().insert (*p); + } else { + new_texts->raw_texts ().insert (TextWithProperties (*p, p.prop_id ())); + } } return new_texts.release (); @@ -261,15 +261,19 @@ AsIfFlatTexts::add (const Texts &other) const std::unique_ptr new_texts (new FlatTexts ()); - size_t n = count () + other.count (); - - new_texts->reserve (n); - for (TextsIterator p (begin ()); ! p.at_end (); ++p) { - new_texts->raw_texts ().insert (*p); + if (p.prop_id () == 0) { + new_texts->raw_texts ().insert (*p); + } else { + new_texts->raw_texts ().insert (db::TextWithProperties (*p, p.prop_id ())); + } } for (TextsIterator p (other.begin ()); ! p.at_end (); ++p) { - new_texts->raw_texts ().insert (*p); + if (p.prop_id () == 0) { + new_texts->raw_texts ().insert (*p); + } else { + new_texts->raw_texts ().insert (db::TextWithProperties (*p, p.prop_id ())); + } } return new_texts.release (); diff --git a/src/db/db/dbDeepEdgePairs.cc b/src/db/db/dbDeepEdgePairs.cc index 16cb80de9..f973dc4eb 100644 --- a/src/db/db/dbDeepEdgePairs.cc +++ b/src/db/db/dbDeepEdgePairs.cc @@ -166,7 +166,7 @@ void DeepEdgePairs::do_insert (const db::EdgePair &edge_pair, db::properties_id_ db::Layout &layout = deep_layer ().layout (); if (layout.begin_top_down () != layout.end_top_down ()) { db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); - if (prop_id != 0) { + if (prop_id == 0) { top_cell.shapes (deep_layer ().layer ()).insert (edge_pair); } else { top_cell.shapes (deep_layer ().layer ()).insert (db::EdgePairWithProperties (edge_pair, prop_id)); @@ -344,9 +344,12 @@ DeepEdgePairs::add_in_place (const EdgePairs &other) 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); + if (p.prop_id () == 0) { + shapes.insert (*p); + } else { + shapes.insert (db::EdgePairWithProperties (*p, p.prop_id ())); + } } - } return this; diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc index 8933e6c93..98a73b2eb 100644 --- a/src/db/db/dbDeepEdges.cc +++ b/src/db/db/dbDeepEdges.cc @@ -1236,7 +1236,11 @@ DeepEdges::add_in_place (const Edges &other) 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); + if (p.prop_id () == 0) { + shapes.insert (*p); + } else { + shapes.insert (db::EdgeWithProperties (*p, p.prop_id ())); + } } } diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 54c9c2f44..f9b985944 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1125,6 +1125,7 @@ DeepRegion::add_in_place (const Region &other) 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.set_prop_id (p.prop_id ()); pr.put (*p); } diff --git a/src/db/db/dbDeepTexts.cc b/src/db/db/dbDeepTexts.cc index b542a5e0a..5efd7f080 100644 --- a/src/db/db/dbDeepTexts.cc +++ b/src/db/db/dbDeepTexts.cc @@ -183,7 +183,7 @@ void DeepTexts::do_insert (const db::Text &text, db::properties_id_type prop_id) db::Layout &layout = deep_layer ().layout (); if (layout.begin_top_down () != layout.end_top_down ()) { db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); - if (prop_id != 0) { + if (prop_id == 0) { top_cell.shapes (deep_layer ().layer ()).insert (db::TextRef (text, layout.shape_repository ())); } else { top_cell.shapes (deep_layer ().layer ()).insert (db::TextRefWithProperties (db::TextRef (text, layout.shape_repository ()), prop_id)); @@ -365,7 +365,11 @@ DeepTexts::add_in_place (const Texts &other) db::Shapes &shapes = deep_layer ().initial_cell ().shapes (deep_layer ().layer ()); for (db::Texts::const_iterator p = other.begin (); ! p.at_end (); ++p) { - shapes.insert (*p); + if (p.prop_id () == 0) { + shapes.insert (*p); + } else { + shapes.insert (db::TextWithProperties (*p, p.prop_id ())); + } } } diff --git a/src/db/db/dbEdgePairs.cc b/src/db/db/dbEdgePairs.cc index 68f3f64c7..99da763ca 100644 --- a/src/db/db/dbEdgePairs.cc +++ b/src/db/db/dbEdgePairs.cc @@ -96,6 +96,13 @@ EdgePairs::EdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss, con mp_delegate = new DeepEdgePairs (si, dss, trans); } +EdgePairs::EdgePairs (DeepShapeStore &dss) +{ + tl_assert (dss.is_singular ()); + unsigned int layout_index = 0; // singular layout index + mp_delegate = new DeepEdgePairs (DeepLayer (&dss, layout_index, dss.layout (layout_index).insert_layer ())); +} + void EdgePairs::write (const std::string &fn) const { diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index 723885d08..0917813aa 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -196,6 +196,12 @@ public: */ explicit EdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans); + /** + * @brief Creates a new empty layer inside the dss + * This method requires the DSS to be singular. + */ + explicit EdgePairs (DeepShapeStore &dss); + /** * @brief Writes the edge pair collection to a file * diff --git a/src/db/db/dbEdges.cc b/src/db/db/dbEdges.cc index cd0304d17..0121b4018 100644 --- a/src/db/db/dbEdges.cc +++ b/src/db/db/dbEdges.cc @@ -107,6 +107,13 @@ Edges::Edges (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::I mp_delegate = new DeepEdges (si, dss, trans, as_edges, merged_semantics); } +Edges::Edges (DeepShapeStore &dss) +{ + tl_assert (dss.is_singular ()); + unsigned int layout_index = 0; // singular layout index + mp_delegate = new DeepEdges (DeepLayer (&dss, layout_index, dss.layout (layout_index).insert_layer ())); +} + const db::RecursiveShapeIterator & Edges::iter () const { diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index db2398164..3a29d1650 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -258,6 +258,12 @@ public: */ explicit Edges (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans, bool as_edges = true, bool merged_semantics = true); + /** + * @brief Creates a new empty layer inside the dss + * This method requires the DSS to be singular. + */ + explicit Edges (DeepShapeStore &dss); + /** * @brief Implementation of the ShapeCollection interface */ diff --git a/src/db/db/dbFlatEdges.cc b/src/db/db/dbFlatEdges.cc index dbd40a87a..fb0e6c507 100644 --- a/src/db/db/dbFlatEdges.cc +++ b/src/db/db/dbFlatEdges.cc @@ -314,18 +314,16 @@ EdgesDelegate *FlatEdges::add (const Edges &other) const if (other_flat) { new_region->raw_edges ().insert (other_flat->raw_edges ().get_layer ().begin (), other_flat->raw_edges ().get_layer ().end ()); + 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); + if (p.prop_id () == 0) { + new_region->raw_edges ().insert (*p); + } else { + new_region->raw_edges ().insert (db::EdgeWithProperties (*p, p.prop_id ())); + } } } @@ -344,18 +342,16 @@ EdgesDelegate *FlatEdges::add_in_place (const Edges &other) if (other_flat) { e.insert (other_flat->raw_edges ().get_layer ().begin (), other_flat->raw_edges ().get_layer ().end ()); + e.insert (other_flat->raw_edges ().get_layer ().begin (), other_flat->raw_edges ().get_layer ().end ()); } else { - size_t n = e.size (); for (EdgesIterator p (other.begin ()); ! p.at_end (); ++p) { - ++n; - } - - e.reserve (db::Edge::tag (), n); - - for (EdgesIterator p (other.begin ()); ! p.at_end (); ++p) { - e.insert (*p); + if (p.prop_id () == 0) { + e.insert (*p); + } else { + e.insert (db::EdgeWithProperties (*p, p.prop_id ())); + } } } diff --git a/src/db/db/dbFlatRegion.cc b/src/db/db/dbFlatRegion.cc index 47dad2e5f..ae4cf1b8b 100644 --- a/src/db/db/dbFlatRegion.cc +++ b/src/db/db/dbFlatRegion.cc @@ -323,15 +323,12 @@ RegionDelegate *FlatRegion::add (const Region &other) const } 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); + if (p.prop_id () == 0) { + new_region->raw_polygons ().insert (*p); + } else { + new_region->raw_polygons ().insert (db::PolygonWithProperties (*p, p.prop_id ())); + } } } @@ -354,15 +351,12 @@ RegionDelegate *FlatRegion::add_in_place (const Region &other) } else { - size_t n = polygons.size (); for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { - ++n; - } - - polygons.reserve (db::Polygon::tag (), n); - - for (RegionIterator p (other.begin ()); ! p.at_end (); ++p) { - polygons.insert (*p); + if (p.prop_id () == 0) { + polygons.insert (*p); + } else { + polygons.insert (db::PolygonWithProperties (*p, p.prop_id ())); + } } } diff --git a/src/db/db/dbFlatTexts.cc b/src/db/db/dbFlatTexts.cc index 4028812b0..8f05ae5db 100644 --- a/src/db/db/dbFlatTexts.cc +++ b/src/db/db/dbFlatTexts.cc @@ -125,18 +125,16 @@ TextsDelegate *FlatTexts::add (const Texts &other) const if (other_flat) { new_texts->raw_texts ().insert (other_flat->raw_texts ().get_layer ().begin (), other_flat->raw_texts ().get_layer ().end ()); + new_texts->raw_texts ().insert (other_flat->raw_texts ().get_layer ().begin (), other_flat->raw_texts ().get_layer ().end ()); } else { - size_t n = new_texts->raw_texts ().size (); for (TextsIterator p (other.begin ()); ! p.at_end (); ++p) { - ++n; - } - - new_texts->raw_texts ().reserve (db::Text::tag (), n); - - for (TextsIterator p (other.begin ()); ! p.at_end (); ++p) { - new_texts->raw_texts ().insert (*p); + if (p.prop_id () == 0) { + new_texts->raw_texts ().insert (*p); + } else { + new_texts->raw_texts ().insert (db::TextWithProperties (*p, p.prop_id ())); + } } } @@ -154,18 +152,16 @@ TextsDelegate *FlatTexts::add_in_place (const Texts &other) if (other_flat) { texts.insert (other_flat->raw_texts ().get_layer ().begin (), other_flat->raw_texts ().get_layer ().end ()); + texts.insert (other_flat->raw_texts ().get_layer ().begin (), other_flat->raw_texts ().get_layer ().end ()); } else { - size_t n = texts.size (); for (TextsIterator p (other.begin ()); ! p.at_end (); ++p) { - ++n; - } - - texts.reserve (db::Text::tag (), n); - - for (TextsIterator p (other.begin ()); ! p.at_end (); ++p) { - texts.insert (*p); + if (p.prop_id () == 0) { + texts.insert (*p); + } else { + texts.insert (db::TextWithProperties (*p, p.prop_id ())); + } } } diff --git a/src/db/db/dbTexts.cc b/src/db/db/dbTexts.cc index 98a86513a..37af51e35 100644 --- a/src/db/db/dbTexts.cc +++ b/src/db/db/dbTexts.cc @@ -92,6 +92,13 @@ Texts::Texts (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::I mp_delegate = new DeepTexts (si, dss, trans); } +Texts::Texts (DeepShapeStore &dss) +{ + tl_assert (dss.is_singular ()); + unsigned int layout_index = 0; // singular layout index + mp_delegate = new DeepTexts (DeepLayer (&dss, layout_index, dss.layout (layout_index).insert_layer ())); +} + void Texts::write (const std::string &fn) const { diff --git a/src/db/db/dbTexts.h b/src/db/db/dbTexts.h index d69fe8a40..c16f30e36 100644 --- a/src/db/db/dbTexts.h +++ b/src/db/db/dbTexts.h @@ -192,6 +192,12 @@ public: */ explicit Texts (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans); + /** + * @brief Creates a new empty layer inside the dss + * This method requires the DSS to be singular. + */ + explicit Texts (DeepShapeStore &dss); + /** * @brief Writes the text collection to a file * diff --git a/src/db/unit_tests/dbEdgePairsTests.cc b/src/db/unit_tests/dbEdgePairsTests.cc index 9d4de4df7..0974f24cd 100644 --- a/src/db/unit_tests/dbEdgePairsTests.cc +++ b/src/db/unit_tests/dbEdgePairsTests.cc @@ -210,3 +210,72 @@ TEST(5_InternalAngleFilter) EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep45), false); EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep45inv), false); } + +TEST(6_add_with_properties) +{ + db::DeepShapeStore dss ("TOP", 0.001); + db::EdgePairs rd1 (dss), rd2 (dss); + db::EdgePairs rf1, rf2; + + db::PropertiesSet ps; + ps.insert ("net", 17); + db::properties_id_type pid = db::properties_id (ps); + + rf1.insert (db::EdgePairWithProperties (db::EdgePair (db::Edge (-10, 20, 20, 60), db::Edge (-10, 30, 20, 70)), pid)); + rd1.insert (db::EdgePairWithProperties (db::EdgePair (db::Edge (-10, 20, 20, 60), db::Edge (-10, 30, 20, 70)), pid)); + + rf2.insert (db::EdgePairWithProperties (db::EdgePair (db::Edge (10, 20, -20, 60), db::Edge (10, 30, -20, 70)), pid)); + rd2.insert (db::EdgePairWithProperties (db::EdgePair (db::Edge (10, 20, -20, 60), db::Edge (10, 30, -20, 70)), pid)); + + db::Layout ly; + db::Cell &top_cell = ly.cell (ly.add_cell ("TOP")); + unsigned int l1 = ly.insert_layer (); + unsigned int l2 = ly.insert_layer (); + + top_cell.shapes (l1).insert (db::EdgePairWithProperties (db::EdgePair (db::Edge (-10, 20, 20, 60), db::Edge (-10, 30, 20, 70)), pid)); + top_cell.shapes (l2).insert (db::EdgePairWithProperties (db::EdgePair (db::Edge (10, 20, -20, 60), db::Edge (10, 30, -20, 70)), pid)); + + db::EdgePairs ro1 (db::RecursiveShapeIterator (ly, top_cell, l1)); + db::EdgePairs ro2 (db::RecursiveShapeIterator (ly, top_cell, l2)); + + // enable properties + ro1.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + ro2.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + + db::EdgePairs r; + r += rf1; + r += rf2; + EXPECT_EQ (r.to_string (), "(-10,20;20,60)/(-10,30;20,70){net=>17};(10,20;-20,60)/(10,30;-20,70){net=>17}"); + EXPECT_EQ ((rf1 + rf2).to_string (), "(-10,20;20,60)/(-10,30;20,70){net=>17};(10,20;-20,60)/(10,30;-20,70){net=>17}"); + + r = db::EdgePairs (); + r += rd1; + r += rf2; + EXPECT_EQ (r.to_string (), "(-10,20;20,60)/(-10,30;20,70){net=>17};(10,20;-20,60)/(10,30;-20,70){net=>17}"); + EXPECT_EQ ((rd1 + rf2).to_string (), "(-10,20;20,60)/(-10,30;20,70){net=>17};(10,20;-20,60)/(10,30;-20,70){net=>17}"); + + r = db::EdgePairs (); + r += rf1; + r += rd2; + EXPECT_EQ (r.to_string (), "(-10,20;20,60)/(-10,30;20,70){net=>17};(10,20;-20,60)/(10,30;-20,70){net=>17}"); + EXPECT_EQ ((rf1 + rd2).to_string (), "(-10,20;20,60)/(-10,30;20,70){net=>17};(10,20;-20,60)/(10,30;-20,70){net=>17}"); + + r = db::EdgePairs (); + r += rd1; + r += rd2; + EXPECT_EQ (r.to_string (), "(-10,20;20,60)/(-10,30;20,70){net=>17};(10,20;-20,60)/(10,30;-20,70){net=>17}"); + EXPECT_EQ ((rd1 + rd2).to_string (), "(-10,20;20,60)/(-10,30;20,70){net=>17};(10,20;-20,60)/(10,30;-20,70){net=>17}"); + + r = db::EdgePairs (); + r += ro1; + r += ro2; + EXPECT_EQ (r.to_string (), "(-10,20;20,60)/(-10,30;20,70){net=>17};(10,20;-20,60)/(10,30;-20,70){net=>17}"); + EXPECT_EQ ((ro1 + ro2).to_string (), "(-10,20;20,60)/(-10,30;20,70){net=>17};(10,20;-20,60)/(10,30;-20,70){net=>17}"); + + r = db::EdgePairs (); + r += ro1; + r += rf2; + EXPECT_EQ (r.to_string (), "(10,20;-20,60)/(10,30;-20,70){net=>17};(-10,20;20,60)/(-10,30;20,70){net=>17}"); + EXPECT_EQ ((ro1 + rf2).to_string (), "(10,20;-20,60)/(10,30;-20,70){net=>17};(-10,20;20,60)/(-10,30;20,70){net=>17}"); +} + diff --git a/src/db/unit_tests/dbEdgesTests.cc b/src/db/unit_tests/dbEdgesTests.cc index 1a07bdae1..15a4b562b 100644 --- a/src/db/unit_tests/dbEdgesTests.cc +++ b/src/db/unit_tests/dbEdgesTests.cc @@ -1375,6 +1375,74 @@ TEST(31) db::compare_layouts (_this, target, tl::testdata () + "/algo/deep_edges_au3_flat.gds"); } +TEST(32_add_with_properties) +{ + db::DeepShapeStore dss ("TOP", 0.001); + db::Edges rd1 (dss), rd2 (dss); + db::Edges rf1, rf2; + + db::PropertiesSet ps; + ps.insert ("net", 17); + db::properties_id_type pid = db::properties_id (ps); + + rf1.insert (db::EdgeWithProperties (db::Edge (-10, 20, 20, 60), pid)); + rd1.insert (db::EdgeWithProperties (db::Edge (-10, 20, 20, 60), pid)); + + rf2.insert (db::EdgeWithProperties (db::Edge (10, 20, 40, 60), pid)); + rd2.insert (db::EdgeWithProperties (db::Edge (10, 20, 40, 60), pid)); + + db::Layout ly; + db::Cell &top_cell = ly.cell (ly.add_cell ("TOP")); + unsigned int l1 = ly.insert_layer (); + unsigned int l2 = ly.insert_layer (); + + top_cell.shapes (l1).insert (db::EdgeWithProperties (db::Edge (-10, 20, 20, 60), pid)); + top_cell.shapes (l2).insert (db::EdgeWithProperties (db::Edge (10, 20, 40, 60), pid)); + + db::Edges ro1 (db::RecursiveShapeIterator (ly, top_cell, l1), false); + db::Edges ro2 (db::RecursiveShapeIterator (ly, top_cell, l2), false); + + // enable properties + ro1.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + ro2.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + + db::Edges r; + r += rf1; + r += rf2; + EXPECT_EQ (r.to_string (), "(-10,20;20,60){net=>17};(10,20;40,60){net=>17}"); + EXPECT_EQ ((rf1 + rf2).to_string (), "(-10,20;20,60){net=>17};(10,20;40,60){net=>17}"); + + r = db::Edges (); + r += rd1; + r += rf2; + EXPECT_EQ (r.to_string (), "(-10,20;20,60){net=>17};(10,20;40,60){net=>17}"); + EXPECT_EQ ((rd1 + rf2).to_string (), "(-10,20;20,60){net=>17};(10,20;40,60){net=>17}"); + + r = db::Edges (); + r += rf1; + r += rd2; + EXPECT_EQ (r.to_string (), "(-10,20;20,60){net=>17};(10,20;40,60){net=>17}"); + EXPECT_EQ ((rf1 + rd2).to_string (), "(-10,20;20,60){net=>17};(10,20;40,60){net=>17}"); + + r = db::Edges (); + r += rd1; + r += rd2; + EXPECT_EQ (r.to_string (), "(-10,20;20,60){net=>17};(10,20;40,60){net=>17}"); + EXPECT_EQ ((rd1 + rd2).to_string (), "(-10,20;20,60){net=>17};(10,20;40,60){net=>17}"); + + r = db::Edges (); + r += ro1; + r += ro2; + EXPECT_EQ (r.to_string (), "(-10,20;20,60){net=>17};(10,20;40,60){net=>17}"); + EXPECT_EQ ((ro1 + ro2).to_string (), "(-10,20;20,60){net=>17};(10,20;40,60){net=>17}"); + + r = db::Edges (); + r += ro1; + r += rf2; + EXPECT_EQ (r.to_string (), "(10,20;40,60){net=>17};(-10,20;20,60){net=>17}"); + EXPECT_EQ ((ro1 + rf2).to_string (), "(10,20;40,60){net=>17};(-10,20;20,60){net=>17}"); +} + // GitHub issue #72 (Edges/Region NOT issue) TEST(100) { diff --git a/src/db/unit_tests/dbRegionTests.cc b/src/db/unit_tests/dbRegionTests.cc index 3bb98718a..6f1b1df49 100644 --- a/src/db/unit_tests/dbRegionTests.cc +++ b/src/db/unit_tests/dbRegionTests.cc @@ -2804,6 +2804,74 @@ TEST(63_sized_outside_deep) } } +TEST(64_add_with_properties) +{ + db::DeepShapeStore dss ("TOP", 0.001); + db::Region rd1 (dss), rd2 (dss); + db::Region rf1, rf2; + + db::PropertiesSet ps; + ps.insert ("net", 17); + db::properties_id_type pid = db::properties_id (ps); + + rf1.insert (db::BoxWithProperties (db::Box (-10, 20, 20, 60), pid)); + rd1.insert (db::BoxWithProperties (db::Box (-10, 20, 20, 60), pid)); + + rf2.insert (db::BoxWithProperties (db::Box (10, 20, 40, 60), pid)); + rd2.insert (db::BoxWithProperties (db::Box (10, 20, 40, 60), pid)); + + db::Layout ly; + db::Cell &top_cell = ly.cell (ly.add_cell ("TOP")); + unsigned int l1 = ly.insert_layer (); + unsigned int l2 = ly.insert_layer (); + + top_cell.shapes (l1).insert (db::BoxWithProperties (db::Box (-10, 20, 20, 60), pid)); + top_cell.shapes (l2).insert (db::BoxWithProperties (db::Box (10, 20, 40, 60), pid)); + + db::Region ro1 (db::RecursiveShapeIterator (ly, top_cell, l1)); + db::Region ro2 (db::RecursiveShapeIterator (ly, top_cell, l2)); + + // enable properties + ro1.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + ro2.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + + db::Region r; + r += rf1; + r += rf2; + EXPECT_EQ (r.to_string (), "(-10,20;-10,60;20,60;20,20){net=>17};(10,20;10,60;40,60;40,20){net=>17}"); + EXPECT_EQ ((rf1 + rf2).to_string (), "(-10,20;-10,60;20,60;20,20){net=>17};(10,20;10,60;40,60;40,20){net=>17}"); + + r = db::Region (); + r += rd1; + r += rf2; + EXPECT_EQ (r.to_string (), "(-10,20;-10,60;20,60;20,20){net=>17};(10,20;10,60;40,60;40,20){net=>17}"); + EXPECT_EQ ((rd1 + rf2).to_string (), "(-10,20;-10,60;20,60;20,20){net=>17};(10,20;10,60;40,60;40,20){net=>17}"); + + r = db::Region (); + r += rf1; + r += rd2; + EXPECT_EQ (r.to_string (), "(-10,20;-10,60;20,60;20,20){net=>17};(10,20;10,60;40,60;40,20){net=>17}"); + EXPECT_EQ ((rf1 + rd2).to_string (), "(-10,20;-10,60;20,60;20,20){net=>17};(10,20;10,60;40,60;40,20){net=>17}"); + + r = db::Region (); + r += rd1; + r += rd2; + EXPECT_EQ (r.to_string (), "(-10,20;-10,60;20,60;20,20){net=>17};(10,20;10,60;40,60;40,20){net=>17}"); + EXPECT_EQ ((rd1 + rd2).to_string (), "(-10,20;-10,60;20,60;20,20){net=>17};(10,20;10,60;40,60;40,20){net=>17}"); + + r = db::Region (); + r += ro1; + r += ro2; + EXPECT_EQ (r.to_string (), "(-10,20;-10,60;20,60;20,20){net=>17};(10,20;10,60;40,60;40,20){net=>17}"); + EXPECT_EQ ((ro1 + ro2).to_string (), "(-10,20;-10,60;20,60;20,20){net=>17};(10,20;10,60;40,60;40,20){net=>17}"); + + r = db::Region (); + r += ro1; + r += rf2; + EXPECT_EQ (r.to_string (), "(10,20;10,60;40,60;40,20){net=>17};(-10,20;-10,60;20,60;20,20){net=>17}"); + EXPECT_EQ ((ro1 + rf2).to_string (), "(10,20;10,60;40,60;40,20){net=>17};(-10,20;-10,60;20,60;20,20){net=>17}"); +} + TEST(100_Processors) { db::Region r; @@ -2895,3 +2963,4 @@ TEST(issue_909) db::Region r = r1 + r2; EXPECT_EQ (r.to_string (), "(0,0;0,100;100,100;100,0);(0,0;0,300;200,300;200,0)"); } + diff --git a/src/db/unit_tests/dbTextsTests.cc b/src/db/unit_tests/dbTextsTests.cc index a0a215475..5bddc26fc 100644 --- a/src/db/unit_tests/dbTextsTests.cc +++ b/src/db/unit_tests/dbTextsTests.cc @@ -208,3 +208,71 @@ TEST(7) texts.pull_interacting (region_out, region); EXPECT_EQ (region_out.to_string (), "(50,-300;50,-100;150,-100;150,-300)"); } + +TEST(8_add_with_properties) +{ + db::DeepShapeStore dss ("TOP", 0.001); + db::Texts rd1 (dss), rd2 (dss); + db::Texts rf1, rf2; + + db::PropertiesSet ps; + ps.insert ("net", 17); + db::properties_id_type pid = db::properties_id (ps); + + rf1.insert (db::TextWithProperties (db::Text ("abc", db::Trans (db::Vector (10, 20))), pid)); + rd1.insert (db::TextWithProperties (db::Text ("abc", db::Trans (db::Vector (10, 20))), pid)); + + rf2.insert (db::TextWithProperties (db::Text ("uvw", db::Trans (db::Vector (-10, 20))), pid)); + rd2.insert (db::TextWithProperties (db::Text ("uvw", db::Trans (db::Vector (-10, 20))), pid)); + + db::Layout ly; + db::Cell &top_cell = ly.cell (ly.add_cell ("TOP")); + unsigned int l1 = ly.insert_layer (); + unsigned int l2 = ly.insert_layer (); + + top_cell.shapes (l1).insert (db::TextWithProperties (db::Text ("abc", db::Trans (db::Vector (10, 20))), pid)); + top_cell.shapes (l2).insert (db::TextWithProperties (db::Text ("uvw", db::Trans (db::Vector (-10, 20))), pid)); + + db::Texts ro1 (db::RecursiveShapeIterator (ly, top_cell, l1)); + db::Texts ro2 (db::RecursiveShapeIterator (ly, top_cell, l2)); + + // enable properties + ro1.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + ro2.apply_property_translator (db::PropertiesTranslator::make_pass_all ()); + + db::Texts r; + r += rf1; + r += rf2; + EXPECT_EQ (r.to_string (), "('abc',r0 10,20){net=>17};('uvw',r0 -10,20){net=>17}"); + EXPECT_EQ ((rf1 + rf2).to_string (), "('abc',r0 10,20){net=>17};('uvw',r0 -10,20){net=>17}"); + + r = db::Texts (); + r += rd1; + r += rf2; + EXPECT_EQ (r.to_string (), "('uvw',r0 -10,20){net=>17};('abc',r0 10,20){net=>17}"); + EXPECT_EQ ((rd1 + rf2).to_string (), "('uvw',r0 -10,20){net=>17};('abc',r0 10,20){net=>17}"); + + r = db::Texts (); + r += rf1; + r += rd2; + EXPECT_EQ (r.to_string (), "('abc',r0 10,20){net=>17};('uvw',r0 -10,20){net=>17}"); + EXPECT_EQ ((rf1 + rd2).to_string (), "('abc',r0 10,20){net=>17};('uvw',r0 -10,20){net=>17}"); + + r = db::Texts (); + r += rd1; + r += rd2; + EXPECT_EQ (r.to_string (), "('abc',r0 10,20){net=>17};('uvw',r0 -10,20){net=>17}"); + EXPECT_EQ ((rd1 + rd2).to_string (), "('abc',r0 10,20){net=>17};('uvw',r0 -10,20){net=>17}"); + + r = db::Texts (); + r += ro1; + r += ro2; + EXPECT_EQ (r.to_string (), "('abc',r0 10,20){net=>17};('uvw',r0 -10,20){net=>17}"); + EXPECT_EQ ((ro1 + ro2).to_string (), "('abc',r0 10,20){net=>17};('uvw',r0 -10,20){net=>17}"); + + r = db::Texts (); + r += ro1; + r += rf2; + EXPECT_EQ (r.to_string (), "('uvw',r0 -10,20){net=>17};('abc',r0 10,20){net=>17}"); + EXPECT_EQ ((ro1 + rf2).to_string (), "('uvw',r0 -10,20){net=>17};('abc',r0 10,20){net=>17}"); +} From a21957629610ee261e9d737c26fafe5944808661 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Feb 2025 00:08:35 +0100 Subject: [PATCH 017/392] WIP: Region, Edges, EdgePairs and Texts filters can filter by property now, first implementation of Region property filter. --- src/db/db/dbAsIfFlatEdgePairs.cc | 2 +- src/db/db/dbAsIfFlatEdges.cc | 8 +- src/db/db/dbAsIfFlatRegion.cc | 12 +- src/db/db/dbAsIfFlatTexts.cc | 8 +- src/db/db/dbCompoundOperation.cc | 8 +- src/db/db/dbCompoundOperation.h | 13 +- src/db/db/dbDeepEdgePairs.cc | 4 +- src/db/db/dbDeepEdges.cc | 4 +- src/db/db/dbDeepRegion.cc | 8 +- src/db/db/dbDeepTexts.cc | 4 +- src/db/db/dbEdgePairFilters.cc | 12 +- src/db/db/dbEdgePairFilters.h | 8 +- src/db/db/dbEdgePairs.h | 2 +- src/db/db/dbEdgesDelegate.h | 4 +- src/db/db/dbEdgesUtils.cc | 4 +- src/db/db/dbEdgesUtils.h | 22 +-- src/db/db/dbFlatEdgePairs.cc | 2 +- src/db/db/dbFlatEdges.cc | 2 +- src/db/db/dbFlatRegion.cc | 2 +- src/db/db/dbFlatTexts.cc | 2 +- src/db/db/dbRegionDelegate.h | 8 +- src/db/db/dbRegionUtils.cc | 44 ++--- src/db/db/dbRegionUtils.h | 48 ++--- src/db/db/dbTexts.h | 2 +- src/db/db/dbTextsUtils.h | 4 +- src/db/db/gsiDeclDbEdgePairs.cc | 10 +- src/db/db/gsiDeclDbEdges.cc | 26 ++- src/db/db/gsiDeclDbNetlistDeviceExtractor.cc | 40 +++++ src/db/db/gsiDeclDbRegion.cc | 180 +++++++++++++++++-- src/db/db/gsiDeclDbTexts.cc | 10 +- src/db/unit_tests/dbEdgePairsTests.cc | 68 +++---- testdata/ruby/dbRegionTest.rb | 46 +++++ 32 files changed, 446 insertions(+), 171 deletions(-) diff --git a/src/db/db/dbAsIfFlatEdgePairs.cc b/src/db/db/dbAsIfFlatEdgePairs.cc index f442979c4..575353c66 100644 --- a/src/db/db/dbAsIfFlatEdgePairs.cc +++ b/src/db/db/dbAsIfFlatEdgePairs.cc @@ -233,7 +233,7 @@ AsIfFlatEdgePairs::filtered (const EdgePairFilterBase &filter) const std::unique_ptr new_edge_pairs (new FlatEdgePairs ()); for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { - if (filter.selected (*p)) { + if (filter.selected (*p, p.prop_id ())) { db::properties_id_type prop_id = p.prop_id (); if (prop_id != 0) { new_edge_pairs->insert (db::EdgePairWithProperties (*p, prop_id)); diff --git a/src/db/db/dbAsIfFlatEdges.cc b/src/db/db/dbAsIfFlatEdges.cc index 13ee3b066..2ac09819a 100644 --- a/src/db/db/dbAsIfFlatEdges.cc +++ b/src/db/db/dbAsIfFlatEdges.cc @@ -711,8 +711,12 @@ AsIfFlatEdges::filtered (const EdgeFilterBase &filter) const std::unique_ptr new_region (new FlatEdges ()); for (EdgesIterator p (begin_merged ()); ! p.at_end (); ++p) { - if (filter.selected (*p)) { - new_region->insert (*p); + if (filter.selected (*p, p.prop_id ())) { + if (p.prop_id () != 0) { + new_region->insert (db::EdgeWithProperties (*p, p.prop_id ())); + } else { + new_region->insert (*p); + } } } diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index d69f280e3..b4de3b83f 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -167,7 +167,7 @@ AsIfFlatRegion::edges (const EdgeFilterBase *filter, const PolygonToEdgeProcesso proc->process (*p, heap); for (auto e = heap.begin (); e != heap.end (); ++e) { - if (! filter || filter->selected (*e)) { + if (! filter || filter->selected (*e, prop_id)) { if (prop_id != 0) { result->insert (db::EdgeWithProperties (*e, prop_id)); } else { @@ -179,7 +179,7 @@ AsIfFlatRegion::edges (const EdgeFilterBase *filter, const PolygonToEdgeProcesso } else { for (db::Polygon::polygon_edge_iterator e = p->begin_edge (); ! e.at_end (); ++e) { - if (! filter || filter->selected (*e)) { + if (! filter || filter->selected (*e, prop_id)) { if (prop_id != 0) { result->insert (db::EdgeWithProperties (*e, prop_id)); } else { @@ -420,8 +420,12 @@ AsIfFlatRegion::filtered (const PolygonFilterBase &filter) const std::unique_ptr new_region (new FlatRegion ()); for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - if (filter.selected (*p)) { - new_region->insert (*p); + if (filter.selected (*p, p.prop_id ())) { + if (p.prop_id () != 0) { + new_region->insert (db::PolygonWithProperties (*p, p.prop_id ())); + } else { + new_region->insert (*p); + } } } diff --git a/src/db/db/dbAsIfFlatTexts.cc b/src/db/db/dbAsIfFlatTexts.cc index de06092f0..e13c4ad8d 100644 --- a/src/db/db/dbAsIfFlatTexts.cc +++ b/src/db/db/dbAsIfFlatTexts.cc @@ -159,8 +159,12 @@ AsIfFlatTexts::filtered (const TextFilterBase &filter) const std::unique_ptr new_texts (new FlatTexts ()); for (TextsIterator p (begin ()); ! p.at_end (); ++p) { - if (filter.selected (*p)) { - new_texts->insert (*p); + if (filter.selected (*p, p.prop_id ())) { + if (p.prop_id () != 0) { + new_texts->insert (db::TextWithProperties (*p, p.prop_id ())); + } else { + new_texts->insert (*p); + } } } diff --git a/src/db/db/dbCompoundOperation.cc b/src/db/db/dbCompoundOperation.cc index 6cb08a68f..cd9f8dcad 100644 --- a/src/db/db/dbCompoundOperation.cc +++ b/src/db/db/dbCompoundOperation.cc @@ -1260,7 +1260,13 @@ CompoundRegionEdgePairFilterOperationNode::do_compute_local (CompoundRegionOpera bool CompoundRegionEdgePairFilterOperationNode::is_selected (const db::EdgePair &p) const { - return mp_filter->selected (p); + return mp_filter->selected (p, db::properties_id_type (0)); +} + +bool +CompoundRegionEdgePairFilterOperationNode::is_selected (const db::EdgePairWithProperties &p) const +{ + return mp_filter->selected (p, p.properties_id ()); } // --------------------------------------------------------------------------------------------- diff --git a/src/db/db/dbCompoundOperation.h b/src/db/db/dbCompoundOperation.h index 23f74b33e..ca42cf77e 100644 --- a/src/db/db/dbCompoundOperation.h +++ b/src/db/db/dbCompoundOperation.h @@ -1015,14 +1015,12 @@ private: child (0)->compute_local (cache, layout, cell, interactions, one, proc); if (m_sum_of_set) { - std::unordered_set wo_props; - wo_props.insert (one.front ().begin (), one.front ().end ()); - if (mp_filter->selected_set (wo_props)) { + if (mp_filter->selected_set (one.front ())) { results.front ().insert (one.front ().begin (), one.front ().end ()); } } else { for (typename std::unordered_set >::const_iterator p = one.front ().begin (); p != one.front ().end (); ++p) { - if (mp_filter->selected (*p)) { + if (mp_filter->selected (*p, p->properties_id ())) { results.front ().insert (*p); } } @@ -1066,14 +1064,12 @@ private: child (0)->compute_local (cache, layout, cell, interactions, one, proc); if (m_sum_of) { - std::unordered_set wo_props; - wo_props.insert (one.front ().begin (), one.front ().end ()); - if (mp_filter->selected (wo_props)) { + if (mp_filter->selected (one.front ())) { results.front ().insert (one.front ().begin (), one.front ().end ()); } } else { for (typename std::unordered_set::const_iterator p = one.front ().begin (); p != one.front ().end (); ++p) { - if (mp_filter->selected (*p)) { + if (mp_filter->selected (*p, p->properties_id ())) { results.front ().insert (*p); } } @@ -1108,6 +1104,7 @@ private: bool m_owns_filter; bool is_selected (const db::EdgePair &p) const; + bool is_selected (const db::EdgePairWithProperties &p) const; template void implement_compute_local (db::CompoundRegionOperationCache *cache, db::Layout *layout, db::Cell *cell, const shape_interactions &interactions, std::vector > &results, const db::LocalProcessorBase *proc) const diff --git a/src/db/db/dbDeepEdgePairs.cc b/src/db/db/dbDeepEdgePairs.cc index f973dc4eb..56907fe0d 100644 --- a/src/db/db/dbDeepEdgePairs.cc +++ b/src/db/db/dbDeepEdgePairs.cc @@ -423,7 +423,7 @@ DeepEdgePairs::apply_filter (const EdgePairFilterBase &filter) const const db::ICplxTrans &tr = *v; for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::EdgePairs); ! si.at_end (); ++si) { - if (filter.selected (si->edge_pair ().transformed (tr))) { + if (filter.selected (si->edge_pair ().transformed (tr), si->prop_id ())) { st->insert (*si); } } @@ -435,7 +435,7 @@ DeepEdgePairs::apply_filter (const EdgePairFilterBase &filter) const db::Shapes &st = c->shapes (res->deep_layer ().layer ()); for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::EdgePairs); ! si.at_end (); ++si) { - if (filter.selected (si->edge_pair ())) { + if (filter.selected (si->edge_pair (), si->prop_id ())) { st.insert (*si); } } diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc index 98a73b2eb..6ef0aaf54 100644 --- a/src/db/db/dbDeepEdges.cc +++ b/src/db/db/dbDeepEdges.cc @@ -848,7 +848,7 @@ DeepEdges::apply_filter (const EdgeFilterBase &filter) const } for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Edges); ! si.at_end (); ++si) { - if (filter.selected (si->edge ().transformed (*v))) { + if (filter.selected (si->edge ().transformed (*v), si->prop_id ())) { st->insert (*si); } } @@ -860,7 +860,7 @@ DeepEdges::apply_filter (const EdgeFilterBase &filter) const 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 ())) { + if (filter.selected (si->edge (), si->prop_id ())) { st.insert (*si); } } diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index f9b985944..458cb8a3d 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1511,7 +1511,7 @@ DeepRegion::edges (const EdgeFilterBase *filter, const PolygonToEdgeProcessorBas proc->process (poly, heap); for (auto e = heap.begin (); e != heap.end (); ++e) { - if (! filter || filter->selected ((*e).transformed (tr))) { + if (! filter || filter->selected ((*e).transformed (tr), si->prop_id ())) { st.insert (db::EdgeWithProperties (*e, si->prop_id ())); } } @@ -1519,7 +1519,7 @@ DeepRegion::edges (const EdgeFilterBase *filter, const PolygonToEdgeProcessorBas } else { for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { - if (! filter || filter->selected ((*e).transformed (tr))) { + if (! filter || filter->selected ((*e).transformed (tr), si->prop_id ())) { st.insert (db::EdgeWithProperties (*e, si->prop_id ())); } } @@ -1641,7 +1641,7 @@ DeepRegion::apply_filter (const PolygonFilterBase &filter) const 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 (*v))) { + if (filter.selected (poly.transformed (*v), si->prop_id ())) { st->insert (*si); } } @@ -1655,7 +1655,7 @@ DeepRegion::apply_filter (const PolygonFilterBase &filter) const 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)) { + if (filter.selected (poly, si->prop_id ())) { st.insert (*si); } } diff --git a/src/db/db/dbDeepTexts.cc b/src/db/db/dbDeepTexts.cc index 5efd7f080..92afeae82 100644 --- a/src/db/db/dbDeepTexts.cc +++ b/src/db/db/dbDeepTexts.cc @@ -442,7 +442,7 @@ DeepTexts *DeepTexts::apply_filter (const TextFilterBase &filter) const for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Texts); ! si.at_end (); ++si) { db::Text text; si->text (text); - if (filter.selected (text.transformed (*v))) { + if (filter.selected (text.transformed (*v), si->prop_id ())) { st->insert (*si); } } @@ -456,7 +456,7 @@ DeepTexts *DeepTexts::apply_filter (const TextFilterBase &filter) const for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Texts); ! si.at_end (); ++si) { db::Text text; si->text (text); - if (filter.selected (text)) { + if (filter.selected (text, si->prop_id ())) { st.insert (*si); } } diff --git a/src/db/db/dbEdgePairFilters.cc b/src/db/db/dbEdgePairFilters.cc index f4abbf7e4..d876afc2a 100644 --- a/src/db/db/dbEdgePairFilters.cc +++ b/src/db/db/dbEdgePairFilters.cc @@ -43,12 +43,12 @@ EdgeFilterBasedEdgePairFilter::~EdgeFilterBasedEdgePairFilter () // .. nothing yet .. } -bool EdgeFilterBasedEdgePairFilter::selected (const db::EdgePair &edge_pair) const +bool EdgeFilterBasedEdgePairFilter::selected (const db::EdgePair &edge_pair, db::properties_id_type prop_id) const { if (m_one_must_match) { - return mp_edge_filter->selected (edge_pair.first ()) || mp_edge_filter->selected (edge_pair.second ()); + return mp_edge_filter->selected (edge_pair.first (), prop_id) || mp_edge_filter->selected (edge_pair.second (), prop_id); } else { - return mp_edge_filter->selected (edge_pair.first ()) && mp_edge_filter->selected (edge_pair.second ()); + return mp_edge_filter->selected (edge_pair.first (), prop_id) && mp_edge_filter->selected (edge_pair.second (), prop_id); } } @@ -71,7 +71,7 @@ EdgePairFilterByDistance::EdgePairFilterByDistance (distance_type min_distance, // .. nothing yet .. } -bool EdgePairFilterByDistance::selected (const db::EdgePair &edge_pair) const +bool EdgePairFilterByDistance::selected (const db::EdgePair &edge_pair, db::properties_id_type) const { distance_type dist = edge_pair.distance (); bool sel = (dist >= m_min_distance && dist < m_max_distance); @@ -87,7 +87,7 @@ EdgePairFilterByArea::EdgePairFilterByArea (area_type min_area, area_type max_ar // .. nothing yet .. } -bool EdgePairFilterByArea::selected (const db::EdgePair &edge_pair) const +bool EdgePairFilterByArea::selected (const db::EdgePair &edge_pair, db::properties_id_type) const { area_type dist = edge_pair.to_simple_polygon (0).area (); bool sel = (dist >= m_min_area && dist < m_max_area); @@ -110,7 +110,7 @@ InternalAngleEdgePairFilter::InternalAngleEdgePairFilter (double amin, bool incl } bool -InternalAngleEdgePairFilter::selected (const db::EdgePair &edge_pair) const +InternalAngleEdgePairFilter::selected (const db::EdgePair &edge_pair, db::properties_id_type) const { db::Vector d1 = edge_pair.first ().d (); db::Vector d2 = edge_pair.second ().d (); diff --git a/src/db/db/dbEdgePairFilters.h b/src/db/db/dbEdgePairFilters.h index 60737fd1a..d721f618a 100644 --- a/src/db/db/dbEdgePairFilters.h +++ b/src/db/db/dbEdgePairFilters.h @@ -47,7 +47,7 @@ public: EdgeFilterBasedEdgePairFilter (EdgeFilterBase *edge_filter, bool one_must_match); virtual ~EdgeFilterBasedEdgePairFilter (); - virtual bool selected (const db::EdgePair &edge_pair) const; + virtual bool selected (const db::EdgePair &edge_pair, properties_id_type prop_id) const; virtual const TransformationReducer *vars () const; virtual bool wants_variants () const; @@ -69,7 +69,7 @@ public: EdgePairFilterByDistance (distance_type min_distance, distance_type max_distance, bool inverted); - virtual bool selected (const db::EdgePair &edge_pair) const; + virtual bool selected (const db::EdgePair &edge_pair, properties_id_type) const; virtual const TransformationReducer *vars () const { return &m_vars; } virtual bool wants_variants () const { return true; } @@ -92,7 +92,7 @@ public: EdgePairFilterByArea (area_type min_area, area_type max_area, bool inverted); - virtual bool selected (const db::EdgePair &edge_pair) const; + virtual bool selected (const db::EdgePair &edge_pair, properties_id_type) const; virtual const TransformationReducer *vars () const { return &m_vars; } virtual bool wants_variants () const { return true; } @@ -114,7 +114,7 @@ public: InternalAngleEdgePairFilter (double a, bool inverted); InternalAngleEdgePairFilter (double amin, bool include_amin, double amax, bool include_amax, bool inverted); - virtual bool selected (const db::EdgePair &edge_pair) const; + virtual bool selected (const db::EdgePair &edge_pair, properties_id_type) const; virtual const TransformationReducer *vars () const { return 0; } virtual bool wants_variants () const { return false; } diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index 0917813aa..aedf2c36d 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -58,7 +58,7 @@ public: EdgePairFilterBase () { } virtual ~EdgePairFilterBase () { } - virtual bool selected (const db::EdgePair &edge_pair) const = 0; + virtual bool selected (const db::EdgePair &edge_pair, db::properties_id_type prop_id) const = 0; virtual const TransformationReducer *vars () const = 0; virtual bool wants_variants () const = 0; }; diff --git a/src/db/db/dbEdgesDelegate.h b/src/db/db/dbEdgesDelegate.h index 0c4b0c387..f3e8cab77 100644 --- a/src/db/db/dbEdgesDelegate.h +++ b/src/db/db/dbEdgesDelegate.h @@ -56,13 +56,13 @@ public: * @brief Filters the edge * If this method returns true, the edge is kept. Otherwise it's discarded. */ - virtual bool selected (const db::Edge &edge) const = 0; + virtual bool selected (const db::Edge &edge, db::properties_id_type prop_id) const = 0; /** * @brief Filters the edge set * If this method returns true, the edges are kept. Otherwise they are discarded. */ - virtual bool selected (const std::unordered_set &edge) const = 0; + virtual bool selected (const std::unordered_set &edge) const = 0; /** * @brief Returns the transformation reducer for building cell variants diff --git a/src/db/db/dbEdgesUtils.cc b/src/db/db/dbEdgesUtils.cc index 6d60b4d77..15dcb5a1b 100644 --- a/src/db/db/dbEdgesUtils.cc +++ b/src/db/db/dbEdgesUtils.cc @@ -304,7 +304,7 @@ EdgeOrientationFilter::EdgeOrientationFilter (double a, bool inverse, bool absol } bool -EdgeOrientationFilter::selected (const db::Edge &edge) const +EdgeOrientationFilter::selected (const db::Edge &edge, db::properties_id_type) const { // NOTE: this edge normalization confines the angle to a range between (-90 .. 90] (-90 excluded). // A horizontal edge has 0 degree, a vertical one has 90 degree. @@ -342,7 +342,7 @@ static EdgeAngleChecker s_orthodiagonal_checkers [] = { }; bool -SpecialEdgeOrientationFilter::selected (const db::Edge &edge) const +SpecialEdgeOrientationFilter::selected (const db::Edge &edge, properties_id_type) const { const EdgeAngleChecker *eb, *ee; diff --git a/src/db/db/dbEdgesUtils.h b/src/db/db/dbEdgesUtils.h index c2f987278..8daa404e8 100644 --- a/src/db/db/dbEdgesUtils.h +++ b/src/db/db/dbEdgesUtils.h @@ -71,7 +71,7 @@ struct DB_PUBLIC EdgeLengthFilter /** * @brief Returns true if the edge length matches the criterion */ - virtual bool selected (const db::Edge &edge) const + virtual bool selected (const db::Edge &edge, db::properties_id_type) const { return check (edge.length ()); } @@ -79,10 +79,10 @@ struct DB_PUBLIC EdgeLengthFilter /** * @brief Returns true if the total edge length matches the criterion */ - bool selected (const std::unordered_set &edges) const + bool selected (const std::unordered_set &edges) const { length_type l = 0; - for (std::unordered_set::const_iterator e = edges.begin (); e != edges.end (); ++e) { + for (std::unordered_set::const_iterator e = edges.begin (); e != edges.end (); ++e) { l += e->length (); } return check (l); @@ -204,15 +204,15 @@ struct DB_PUBLIC EdgeOrientationFilter /** * @brief Returns true if the edge orientation matches the criterion */ - virtual bool selected (const db::Edge &edge) const; + virtual bool selected (const db::Edge &edge, properties_id_type) const; /** * @brief Returns true if all edge orientations match the criterion */ - virtual bool selected (const std::unordered_set &edges) const + virtual bool selected (const std::unordered_set &edges) const { - for (std::unordered_set::const_iterator e = edges.begin (); e != edges.end (); ++e) { - if (! selected (*e)) { + for (std::unordered_set::const_iterator e = edges.begin (); e != edges.end (); ++e) { + if (! selected (*e, e->properties_id ())) { return false; } } @@ -274,15 +274,15 @@ struct DB_PUBLIC SpecialEdgeOrientationFilter /** * @brief Returns true if the edge orientation matches the criterion */ - virtual bool selected (const db::Edge &edge) const; + virtual bool selected (const db::Edge &edge, db::properties_id_type) const; /** * @brief Returns true if all edge orientations match the criterion */ - virtual bool selected (const std::unordered_set &edges) const + virtual bool selected (const std::unordered_set &edges) const { - for (std::unordered_set::const_iterator e = edges.begin (); e != edges.end (); ++e) { - if (! selected (*e)) { + for (std::unordered_set::const_iterator e = edges.begin (); e != edges.end (); ++e) { + if (! selected (*e, e->properties_id ())) { return false; } } diff --git a/src/db/db/dbFlatEdgePairs.cc b/src/db/db/dbFlatEdgePairs.cc index e6b9808ac..cb554a3ea 100644 --- a/src/db/db/dbFlatEdgePairs.cc +++ b/src/db/db/dbFlatEdgePairs.cc @@ -103,7 +103,7 @@ FlatEdgePairs::filter_in_place (const EdgePairFilterBase &filter) edge_pair_iterator_type pw = ep.get_layer ().begin (); for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { - if (filter.selected (*p)) { + if (filter.selected (*p, p.prop_id ())) { if (pw == ep.get_layer ().end ()) { ep.get_layer ().insert (*p); pw = ep.get_layer ().end (); diff --git a/src/db/db/dbFlatEdges.cc b/src/db/db/dbFlatEdges.cc index fb0e6c507..44299bc8c 100644 --- a/src/db/db/dbFlatEdges.cc +++ b/src/db/db/dbFlatEdges.cc @@ -276,7 +276,7 @@ FlatEdges::filter_in_place (const EdgeFilterBase &filter) edge_iterator_wp_type pw_wp = e.get_layer ().begin (); for (EdgesIterator p (begin_merged ()); ! p.at_end (); ++p) { - if (filter.selected (*p)) { + if (filter.selected (*p, p.prop_id ())) { if (p.prop_id () != 0) { if (pw_wp == e.get_layer ().end ()) { e.get_layer ().insert (db::EdgeWithProperties (*p, p.prop_id ())); diff --git a/src/db/db/dbFlatRegion.cc b/src/db/db/dbFlatRegion.cc index ae4cf1b8b..f6abd1696 100644 --- a/src/db/db/dbFlatRegion.cc +++ b/src/db/db/dbFlatRegion.cc @@ -184,7 +184,7 @@ RegionDelegate *FlatRegion::filter_in_place (const PolygonFilterBase &filter) polygon_iterator_wp_type pw_wp = poly_layer_wp.begin (); for (RegionIterator p (filter.requires_raw_input () ? begin () : begin_merged ()); ! p.at_end (); ++p) { - if (filter.selected (*p)) { + if (filter.selected (*p, p.prop_id ())) { if (p.prop_id () != 0) { if (pw_wp == poly_layer_wp.end ()) { poly_layer_wp.insert (db::PolygonWithProperties (*p, p.prop_id ())); diff --git a/src/db/db/dbFlatTexts.cc b/src/db/db/dbFlatTexts.cc index 8f05ae5db..e2e0e54a2 100644 --- a/src/db/db/dbFlatTexts.cc +++ b/src/db/db/dbFlatTexts.cc @@ -101,7 +101,7 @@ FlatTexts::filter_in_place (const TextFilterBase &filter) text_iterator_type pw = texts.get_layer ().begin (); for (TextsIterator p (begin ()); ! p.at_end (); ++p) { - if (filter.selected (*p)) { + if (filter.selected (*p, p.prop_id ())) { if (pw == texts.get_layer ().end ()) { texts.get_layer ().insert (*p); pw = texts.get_layer ().end (); diff --git a/src/db/db/dbRegionDelegate.h b/src/db/db/dbRegionDelegate.h index 252d2df9b..44aa918f8 100644 --- a/src/db/db/dbRegionDelegate.h +++ b/src/db/db/dbRegionDelegate.h @@ -68,25 +68,25 @@ public: * @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; + virtual bool selected (const db::Polygon &polygon, db::properties_id_type prop_id) const = 0; /** * @brief Filters the polygon reference * If this method returns true, the polygon is kept. Otherwise it's discarded. */ - virtual bool selected (const db::PolygonRef &polygon) const = 0; + virtual bool selected (const db::PolygonRef &polygon, db::properties_id_type prop_id) const = 0; /** * @brief Filters the set of polygons (taking the overall properties) * If this method returns true, the polygon is kept. Otherwise it's discarded. */ - virtual bool selected_set (const std::unordered_set &polygons) const = 0; + virtual bool selected_set (const std::unordered_set &polygons) const = 0; /** * @brief Filters the set of polygon references (taking the overall properties) * If this method returns true, the polygon is kept. Otherwise it's discarded. */ - virtual bool selected_set (const std::unordered_set &polygons) const = 0; + virtual bool selected_set (const std::unordered_set &polygons) const = 0; /** * @brief Returns the transformation reducer for building cell variants diff --git a/src/db/db/dbRegionUtils.cc b/src/db/db/dbRegionUtils.cc index ec6b6b7a5..925bd314f 100644 --- a/src/db/db/dbRegionUtils.cc +++ b/src/db/db/dbRegionUtils.cc @@ -48,29 +48,29 @@ bool RegionPerimeterFilter::check (perimeter_type p) const } } -bool RegionPerimeterFilter::selected (const db::Polygon &poly) const +bool RegionPerimeterFilter::selected (const db::Polygon &poly, db::properties_id_type) const { return check (poly.perimeter ()); } -bool RegionPerimeterFilter::selected (const db::PolygonRef &poly) const +bool RegionPerimeterFilter::selected (const db::PolygonRef &poly, db::properties_id_type) const { return check (poly.perimeter ()); } -bool RegionPerimeterFilter::selected_set (const std::unordered_set &poly) const +bool RegionPerimeterFilter::selected_set (const std::unordered_set &poly) const { perimeter_type ps = 0; - for (std::unordered_set::const_iterator p = poly.begin (); p != poly.end (); ++p) { + for (std::unordered_set::const_iterator p = poly.begin (); p != poly.end (); ++p) { ps += p->perimeter (); } return check (ps); } -bool RegionPerimeterFilter::selected_set (const std::unordered_set &poly) const +bool RegionPerimeterFilter::selected_set (const std::unordered_set &poly) const { perimeter_type ps = 0; - for (std::unordered_set::const_iterator p = poly.begin (); p != poly.end (); ++p) { + for (std::unordered_set::const_iterator p = poly.begin (); p != poly.end (); ++p) { ps += p->perimeter (); } return check (ps); @@ -99,29 +99,29 @@ bool RegionAreaFilter::check (area_type a) const } } -bool RegionAreaFilter::selected (const db::Polygon &poly) const +bool RegionAreaFilter::selected (const db::Polygon &poly, db::properties_id_type) const { return check (poly.area ()); } -bool RegionAreaFilter::selected (const db::PolygonRef &poly) const +bool RegionAreaFilter::selected (const db::PolygonRef &poly, properties_id_type) const { return check (poly.area ()); } -bool RegionAreaFilter::selected_set (const std::unordered_set &poly) const +bool RegionAreaFilter::selected_set (const std::unordered_set &poly) const { area_type as = 0; - for (std::unordered_set::const_iterator p = poly.begin (); p != poly.end (); ++p) { + for (std::unordered_set::const_iterator p = poly.begin (); p != poly.end (); ++p) { as += p->area (); } return check (as); } -bool RegionAreaFilter::selected_set (const std::unordered_set &poly) const +bool RegionAreaFilter::selected_set (const std::unordered_set &poly) const { area_type as = 0; - for (std::unordered_set::const_iterator p = poly.begin (); p != poly.end (); ++p) { + for (std::unordered_set::const_iterator p = poly.begin (); p != poly.end (); ++p) { as += p->area (); } return check (as); @@ -143,13 +143,13 @@ RectilinearFilter::RectilinearFilter (bool inverse) } bool -RectilinearFilter::selected (const db::Polygon &poly) const +RectilinearFilter::selected (const db::Polygon &poly, db::properties_id_type) const { return poly.is_rectilinear () != m_inverse; } bool -RectilinearFilter::selected (const db::PolygonRef &poly) const +RectilinearFilter::selected (const db::PolygonRef &poly, db::properties_id_type) const { return poly.is_rectilinear () != m_inverse; } @@ -170,14 +170,14 @@ HoleCountFilter::HoleCountFilter (size_t min_count, size_t max_count, bool inver } bool -HoleCountFilter::selected (const db::Polygon &poly) const +HoleCountFilter::selected (const db::Polygon &poly, db::properties_id_type) const { bool ok = poly.holes () < m_max_count && poly.holes () >= m_min_count; return ok != m_inverse; } bool -HoleCountFilter::selected (const db::PolygonRef &poly) const +HoleCountFilter::selected (const db::PolygonRef &poly, properties_id_type) const { bool ok = poly.obj ().holes () < m_max_count && poly.obj ().holes () >= m_min_count; return ok != m_inverse; @@ -198,7 +198,7 @@ RectangleFilter::RectangleFilter (bool is_square, bool inverse) } bool -RectangleFilter::selected (const db::Polygon &poly) const +RectangleFilter::selected (const db::Polygon &poly, properties_id_type) const { bool ok = poly.is_box (); if (ok && m_is_square) { @@ -209,7 +209,7 @@ RectangleFilter::selected (const db::Polygon &poly) const } bool -RectangleFilter::selected (const db::PolygonRef &poly) const +RectangleFilter::selected (const db::PolygonRef &poly, properties_id_type) const { bool ok = poly.is_box (); if (ok && m_is_square) { @@ -256,13 +256,13 @@ RegionBBoxFilter::check (const db::Box &box) const } bool -RegionBBoxFilter::selected (const db::Polygon &poly) const +RegionBBoxFilter::selected (const db::Polygon &poly, properties_id_type) const { return check (poly.box ()); } bool -RegionBBoxFilter::selected (const db::PolygonRef &poly) const +RegionBBoxFilter::selected (const db::PolygonRef &poly, properties_id_type) const { return check (poly.box ()); } @@ -322,7 +322,7 @@ static double compute_ratio_parameter (const P &poly, RegionRatioFilter::paramet return v; } -bool RegionRatioFilter::selected (const db::Polygon &poly) const +bool RegionRatioFilter::selected (const db::Polygon &poly, properties_id_type) const { double v = compute_ratio_parameter (poly, m_parameter); @@ -330,7 +330,7 @@ bool RegionRatioFilter::selected (const db::Polygon &poly) const return ok != m_inverse; } -bool RegionRatioFilter::selected (const db::PolygonRef &poly) const +bool RegionRatioFilter::selected (const db::PolygonRef &poly, properties_id_type) const { double v = compute_ratio_parameter (poly, m_parameter); diff --git a/src/db/db/dbRegionUtils.h b/src/db/db/dbRegionUtils.h index f637dcbde..49a0ac003 100644 --- a/src/db/db/dbRegionUtils.h +++ b/src/db/db/dbRegionUtils.h @@ -57,22 +57,22 @@ struct DB_PUBLIC RegionPerimeterFilter /** * @brief Returns true if the polygon's perimeter matches the criterion */ - virtual bool selected (const db::Polygon &poly) const; + virtual bool selected (const db::Polygon &poly, properties_id_type) const; /** * @brief Returns true if the polygon's perimeter matches the criterion */ - virtual bool selected (const db::PolygonRef &poly) const; + virtual bool selected (const db::PolygonRef &poly, properties_id_type) const; /** * @brief Returns true if the polygon's perimeter sum matches the criterion */ - virtual bool selected_set (const std::unordered_set &polygons) const; + virtual bool selected_set (const std::unordered_set &polygons) const; /** * @brief Returns true if the polygon's perimeter sum matches the criterion */ - virtual bool selected_set (const std::unordered_set &polygons) const; + virtual bool selected_set (const std::unordered_set &polygons) const; /** * @brief This filter is isotropic @@ -123,22 +123,22 @@ struct DB_PUBLIC RegionAreaFilter /** * @brief Returns true if the polygon's area matches the criterion */ - virtual bool selected (const db::Polygon &poly) const; + virtual bool selected (const db::Polygon &poly, properties_id_type) const; /** * @brief Returns true if the polygon's area matches the criterion */ - virtual bool selected (const db::PolygonRef &poly) const; + virtual bool selected (const db::PolygonRef &poly, properties_id_type) const; /** * @brief Returns true if the polygon's area sum matches the criterion */ - virtual bool selected_set (const std::unordered_set &polygons) const; + virtual bool selected_set (const std::unordered_set &polygons) const; /** * @brief Returns true if the polygon's area sum matches the criterion */ - virtual bool selected_set (const std::unordered_set &polygons) const; + virtual bool selected_set (const std::unordered_set &polygons) const; /** * @brief This filter is isotropic @@ -175,20 +175,20 @@ struct DB_PUBLIC AllMustMatchFilter */ AllMustMatchFilter () { } - virtual bool selected_set (const std::unordered_set &polygons) const + virtual bool selected_set (const std::unordered_set &polygons) const { - for (std::unordered_set::const_iterator p = polygons.begin (); p != polygons.end (); ++p) { - if (! selected (*p)) { + for (std::unordered_set::const_iterator p = polygons.begin (); p != polygons.end (); ++p) { + if (! selected (*p, p->properties_id ())) { return false; } } return true; } - virtual bool selected_set (const std::unordered_set &polygons) const + virtual bool selected_set (const std::unordered_set &polygons) const { - for (std::unordered_set::const_iterator p = polygons.begin (); p != polygons.end (); ++p) { - if (! selected (*p)) { + for (std::unordered_set::const_iterator p = polygons.begin (); p != polygons.end (); ++p) { + if (! selected (*p, p->properties_id ())) { return false; } } @@ -215,12 +215,12 @@ struct DB_PUBLIC RectilinearFilter /** * @brief Returns true if the polygon is rectilinear */ - virtual bool selected (const db::Polygon &poly) const; + virtual bool selected (const db::Polygon &poly, properties_id_type) const; /** * @brief Returns true if the polygon is rectilinear */ - virtual bool selected (const db::PolygonRef &poly) const; + virtual bool selected (const db::PolygonRef &poly, properties_id_type) const; /** * @brief This filter does not need variants @@ -259,12 +259,12 @@ struct DB_PUBLIC RectangleFilter /** * @brief Returns true if the polygon is a rectangle */ - virtual bool selected (const db::Polygon &poly) const; + virtual bool selected (const db::Polygon &poly, properties_id_type) const; /** * @brief Returns true if the polygon is a rectangle */ - virtual bool selected (const db::PolygonRef &poly) const; + virtual bool selected (const db::PolygonRef &poly, properties_id_type) const; /** * @brief This filter does not need variants @@ -304,12 +304,12 @@ struct DB_PUBLIC HoleCountFilter /** * @brief Returns true if the polygon is a rectangle */ - virtual bool selected (const db::Polygon &poly) const; + virtual bool selected (const db::Polygon &poly, properties_id_type) const; /** * @brief Returns true if the polygon is a rectangle */ - virtual bool selected (const db::PolygonRef &poly) const; + virtual bool selected (const db::PolygonRef &poly, properties_id_type) const; /** * @brief This filter does not need variants @@ -375,12 +375,12 @@ struct DB_PUBLIC RegionBBoxFilter /** * @brief Returns true if the polygon's bounding box matches the criterion */ - virtual bool selected (const db::Polygon &poly) const; + virtual bool selected (const db::Polygon &poly, properties_id_type) const; /** * @brief Returns true if the polygon's bounding box matches the criterion */ - virtual bool selected (const db::PolygonRef &poly) const; + virtual bool selected (const db::PolygonRef &poly, properties_id_type) const; /** * @brief This filter is isotropic unless the parameter is width or height @@ -444,12 +444,12 @@ struct DB_PUBLIC RegionRatioFilter /** * @brief Returns true if the polygon's area matches the criterion */ - virtual bool selected (const db::Polygon &poly) const; + virtual bool selected (const db::Polygon &poly, properties_id_type) const; /** * @brief Returns true if the polygon's area matches the criterion */ - virtual bool selected (const db::PolygonRef &poly) const; + virtual bool selected (const db::PolygonRef &poly, properties_id_type) const; /** * @brief This filter is isotropic unless the parameter is width or height diff --git a/src/db/db/dbTexts.h b/src/db/db/dbTexts.h index c16f30e36..e714377df 100644 --- a/src/db/db/dbTexts.h +++ b/src/db/db/dbTexts.h @@ -57,7 +57,7 @@ public: TextFilterBase () { } virtual ~TextFilterBase () { } - virtual bool selected (const db::Text &text) const = 0; + virtual bool selected (const db::Text &text, db::properties_id_type prop_id) const = 0; virtual const TransformationReducer *vars () const = 0; virtual bool wants_variants () const = 0; }; diff --git a/src/db/db/dbTextsUtils.h b/src/db/db/dbTextsUtils.h index a07fc1f52..f35d237a0 100644 --- a/src/db/db/dbTextsUtils.h +++ b/src/db/db/dbTextsUtils.h @@ -58,7 +58,7 @@ struct DB_PUBLIC TextStringFilter /** * @brief Returns true if the text matches the criterion */ - virtual bool selected (const db::Text &text) const + virtual bool selected (const db::Text &text, db::properties_id_type) const { return (text.string () == m_text) != m_inverse; } @@ -118,7 +118,7 @@ struct DB_PUBLIC TextPatternFilter /** * @brief Returns true if the text matches the criterion */ - virtual bool selected (const db::Text &text) const + virtual bool selected (const db::Text &text, db::properties_id_type) const { return m_pattern.match (text.string ()) != m_inverse; } diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 3ac118dd5..aec39b3eb 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -45,17 +45,17 @@ class EdgePairFilterImpl public: EdgePairFilterImpl () { } - bool issue_selected (const db::EdgePair &) const + bool issue_selected (const db::EdgePairWithProperties &) const { return false; } - virtual bool selected (const db::EdgePair &edge_pair) const + virtual bool selected (const db::EdgePair &edge_pair, db::properties_id_type prop_id) const { if (f_selected.can_issue ()) { - return f_selected.issue (&EdgePairFilterImpl::issue_selected, edge_pair); + return f_selected.issue (&EdgePairFilterImpl::issue_selected, db::EdgePairWithProperties (edge_pair, prop_id)); } else { - return issue_selected (edge_pair); + return issue_selected (db::EdgePairWithProperties (edge_pair, prop_id)); } } @@ -73,6 +73,8 @@ Class decl_EdgePairFilterImpl ("db", "EdgePairFilter", "@brief Selects an edge pair\n" "This method is the actual payload. It needs to be reimplemented in a derived class.\n" "It needs to analyze the edge pair and return 'true' if it should be kept and 'false' if it should be discarded." + "\n" + "Since version 0.30, the edge pair carries properties." ), "@brief A generic edge pair filter adaptor\n" "\n" diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 0d8f307ad..8339805b4 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -46,26 +46,34 @@ class EdgeFilterImpl public: EdgeFilterImpl () { } - bool issue_selected (const db::Edge &) const + bool issue_selected (const db::EdgeWithProperties &) const { return false; } - virtual bool selected (const db::Edge &edge) const + virtual bool selected (const db::Edge &edge, db::properties_id_type prop_id) const { if (f_selected.can_issue ()) { - return f_selected.issue (&EdgeFilterImpl::issue_selected, edge); + return f_selected.issue (&EdgeFilterImpl::issue_selected, db::EdgeWithProperties (edge, prop_id)); } else { - return issue_selected (edge); + return issue_selected (db::EdgeWithProperties (edge, prop_id)); } } // Returns true if all edges match the criterion - virtual bool selected (const std::unordered_set &edges) const + virtual bool selected (const std::unordered_set &edges) const { - for (std::unordered_set::const_iterator e = edges.begin (); e != edges.end (); ++e) { - if (! selected (*e)) { - return false; + if (f_selected.can_issue ()) { + for (std::unordered_set::const_iterator e = edges.begin (); e != edges.end (); ++e) { + if (! f_selected.issue (&EdgeFilterImpl::issue_selected, *e)) { + return false; + } + } + } else { + for (std::unordered_set::const_iterator e = edges.begin (); e != edges.end (); ++e) { + if (! issue_selected (*e)) { + return false; + } } } return true; @@ -85,6 +93,8 @@ Class decl_EdgeFilterImpl ("db", "EdgeFilter", "@brief Selects an edge\n" "This method is the actual payload. It needs to be reimplemented in a derived class.\n" "It needs to analyze the edge and return 'true' if it should be kept and 'false' if it should be discarded." + "\n" + "Since version 0.30, the edge carries properties." ), "@brief A generic edge filter adaptor\n" "\n" diff --git a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc index 47087b104..454d1aaa5 100644 --- a/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc +++ b/src/db/db/gsiDeclDbNetlistDeviceExtractor.cc @@ -263,6 +263,26 @@ define_terminal_by_names (GenericDeviceExtractor *extractor, db::Device *device, extractor->define_terminal (device, terminal_id, layer_id, shape); } +static void error1 (GenericDeviceExtractor *ext, const std::string &message, const db::DPolygonWithProperties &poly) +{ + ext->error (message, poly); +} + +static void error2 (GenericDeviceExtractor *ext, const std::string &message, const db::PolygonWithProperties &poly) +{ + ext->error (message, poly); +} + +static void warn1 (GenericDeviceExtractor *ext, const std::string &message, const db::DPolygonWithProperties &poly) +{ + ext->warn (message, poly); +} + +static void warn2 (GenericDeviceExtractor *ext, const std::string &message, const db::PolygonWithProperties &poly) +{ + ext->warn (message, poly); +} + Class decl_GenericDeviceExtractor (decl_dbNetlistDeviceExtractor, "db", "GenericDeviceExtractor", gsi::callback ("setup", &GenericDeviceExtractor::setup, &GenericDeviceExtractor::cb_setup, "@brief Sets up the extractor.\n" @@ -397,10 +417,20 @@ Class decl_GenericDeviceExtractor (decl_dbNetlistDeviceE gsi::arg ("message"), gsi::arg ("geometry"), "@brief Issues an error with the given message and micrometer-units polygon geometry\n" ) + + gsi::method_ext ("error", &error1, + gsi::arg ("message"), gsi::arg ("geometry"), + "@brief Issues an error with the given message and micrometer-units polygon geometry with properties\n" + "This flavor has been introduced in version 0.30." + ) + 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 database-unit polygon geometry\n" ) + + gsi::method_ext ("error", &error2, + gsi::arg ("message"), gsi::arg ("geometry"), + "@brief Issues an error with the given message and database-units polygon geometry with properties\n" + "This flavor has been introduced in version 0.30." + ) + 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" @@ -423,11 +453,21 @@ Class decl_GenericDeviceExtractor (decl_dbNetlistDeviceE "@brief Issues a warning with the given message and micrometer-units polygon geometry\n" "Warnings have been introduced in version 0.28.13." ) + + gsi::method_ext ("warn", &warn1, + gsi::arg ("message"), gsi::arg ("geometry"), + "@brief Issues a warning with the given message and micrometer-units polygon geometry with properties\n" + "This flavor has been introduced in version 0.30." + ) + gsi::method ("warn", (void (GenericDeviceExtractor::*) (const std::string &, const db::Polygon &)) &GenericDeviceExtractor::warn, gsi::arg ("message"), gsi::arg ("geometry"), "@brief Issues a warning with the given message and database-unit polygon geometry\n" "Warnings have been introduced in version 0.28.13." ) + + gsi::method_ext ("warn", &warn2, + gsi::arg ("message"), gsi::arg ("geometry"), + "@brief Issues a warning with the given message and database-unit polygon geometry\n" + "This flavor has been introduced in version 0.30." + ) + gsi::method ("warn", (void (GenericDeviceExtractor::*) (const std::string &, const std::string &, const std::string &)) &GenericDeviceExtractor::warn, gsi::arg ("category_name"), gsi::arg ("category_description"), gsi::arg ("message"), "@brief Issues a warning with the given category name and description, message\n" diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index b3cb7bcff..b01ffd261 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -36,6 +36,7 @@ #include "dbRegionProcessors.h" #include "dbCompoundOperation.h" #include "dbLayoutToNetlist.h" +#include "dbPropertiesRepository.h" #include "tlGlobPattern.h" #include "gsiDeclDbContainerHelpers.h" @@ -50,31 +51,38 @@ namespace gsi // --------------------------------------------------------------------------------- // PolygonFilter binding -class PolygonFilterImpl +class PolygonFilterBase : public shape_filter_impl { +public: + PolygonFilterBase () { } +}; + +class PolygonFilterImpl + : public PolygonFilterBase +{ public: PolygonFilterImpl () { } - bool issue_selected (const db::Polygon &) const + bool issue_selected (const db::PolygonWithProperties &) const { return false; } - virtual bool selected (const db::Polygon &polygon) const + virtual bool selected (const db::Polygon &polygon, db::properties_id_type prop_id) const { if (f_selected.can_issue ()) { - return f_selected.issue (&PolygonFilterImpl::issue_selected, polygon); + return f_selected.issue (&PolygonFilterImpl::issue_selected, db::PolygonWithProperties (polygon, prop_id)); } else { return issue_selected (polygon); } } - virtual bool selected (const db::PolygonRef &polygon) const + virtual bool selected (const db::PolygonRef &polygon, db::properties_id_type prop_id) const { db::Polygon p; polygon.instantiate (p); - return selected (p); + return selected (p, prop_id); } gsi::Callback f_selected; @@ -85,12 +93,164 @@ private: PolygonFilterImpl (const PolygonFilterImpl &); }; -Class decl_PolygonFilterImpl ("db", "PolygonFilter", +/** + * @brief A properties filter + */ +class PropertiesFilter +{ +public: + PropertiesFilter (const tl::Variant &name, const tl::Variant &value, bool inverse) + : m_name_id (db::property_names_id (name)), m_value_from (value), m_exact (true), m_glob (false), m_inverse (inverse) + { + // .. nothing yet .. + } + + PropertiesFilter (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse) + : m_name_id (db::property_names_id (name)), m_value_from (from), m_value_to (to), m_exact (false), m_glob (false), m_inverse (inverse) + { + // .. nothing yet .. + } + + PropertiesFilter (const tl::Variant &name, const std::string &pattern, bool inverse) + : m_name_id (db::property_names_id (name)), m_pattern (pattern), m_exact (true), m_glob (true), m_inverse (inverse) + { + // .. nothing yet .. + } + + bool prop_selected (db::properties_id_type prop_id) const + { + auto c = m_cache.find (prop_id); + if (c != m_cache.end ()) { + return c->second; + } + + bool res = prop_selected_impl (prop_id); + m_cache.insert (std::make_pair (prop_id, res)); + return res; + } + +private: + bool prop_selected_impl (db::properties_id_type prop_id) const + { + const db::PropertiesSet &ps = db::properties (prop_id); + if (ps.has_value (m_name_id)) { + + const tl::Variant &value = ps.value (m_name_id); + + if (m_glob) { + return m_pattern.match (value.to_string ()) != m_inverse; + } else if (m_exact) { + return (value == m_value_from) != m_inverse; + } else { + return ((m_value_from.is_nil () || ! (value < m_value_from)) && (m_value_to.is_nil () || value < m_value_to)) != m_inverse; + } + + } else { + return m_inverse; + } + } + + mutable std::map m_cache; + db::property_names_id_type m_name_id; + tl::Variant m_value_from, m_value_to; + tl::GlobPattern m_pattern; + bool m_exact; + bool m_glob; + bool m_inverse; +}; + +class PolygonPropertiesFilter + : public PolygonFilterBase, public PropertiesFilter +{ +public: + PolygonPropertiesFilter (const tl::Variant &name, const std::string &pattern, bool inverse) + : PropertiesFilter (name, pattern, inverse) + { + // .. nothing yet .. + } + + PolygonPropertiesFilter (const tl::Variant &name, const tl::Variant &value, bool inverse) + : PropertiesFilter (name, value, inverse) + { + // .. nothing yet .. + } + + PolygonPropertiesFilter (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse) + : PropertiesFilter (name, from, to, inverse) + { + // .. nothing yet .. + } + + bool selected (const db::Polygon &, db::properties_id_type prop_id) const + { + return PropertiesFilter::prop_selected (prop_id); + } + + bool selected (const db::PolygonRef &, db::properties_id_type prop_id) const + { + return PropertiesFilter::prop_selected (prop_id); + } +}; + +static PolygonFilterBase *make_ppf1 (const tl::Variant &name, const tl::Variant &value, bool inverse) +{ + return new PolygonPropertiesFilter (name, value, inverse); +} + +static PolygonFilterBase *make_ppf2 (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse) +{ + return new PolygonPropertiesFilter (name, from, to, inverse); +} + +static PolygonFilterBase *make_pg (const tl::Variant &name, const std::string &glob, bool inverse) +{ + return new PolygonPropertiesFilter (name, glob, inverse); +} + +Class decl_PolygonFilterBase ("db", "PolygonFilterBase", + gsi::constructor ("property_glob", &make_pg, gsi::arg ("name"), gsi::arg ("pattern"), gsi::arg ("inverse", false), + "@brief Creates a single-valued property filter\n" + "@param name The name of the property to use.\n" + "@param value The glob pattern to match the property value against.\n" + "@param inverse If true, inverts the selection - i.e. all polygons without a matching property are selected.\n" + "\n" + "Apply this filter with \\Region#filtered.\n" + "\n" + "This feature has been introduced in version 0.30." + ) + + gsi::constructor ("property_filter", &make_ppf1, gsi::arg ("name"), gsi::arg ("value"), gsi::arg ("inverse", false), + "@brief Creates a single-valued property filter\n" + "@param name The name of the property to use.\n" + "@param value The value against which the property is checked (exact match).\n" + "@param inverse If true, inverts the selection - i.e. all polygons without a property with the given name and value are selected.\n" + "\n" + "Apply this filter with \\Region#filtered.\n" + "\n" + "This feature has been introduced in version 0.30." + ) + + gsi::constructor ("property_filter_bounded", &make_ppf2, gsi::arg ("name"), gsi::arg ("from"), gsi::arg ("to"), gsi::arg ("inverse", false), + "@brief Creates a single-valued property filter\n" + "@param name The name of the property to use.\n" + "@param from The lower value against which the property is checked or 'nil' if no lower bound shall be used.\n" + "@param to The upper value against which the property is checked or 'nil' if no upper bound shall be used.\n" + "@param inverse If true, inverts the selection - i.e. all polygons without a property with the given name and value range are selected.\n" + "\n" + "This version does a bounded match. The value of the propery needs to be larger or equal to 'from' and less than 'to'.\n" + "Apply this filter with \\Region#filtered.\n" + "\n" + "This feature has been introduced in version 0.30." + ), + "@hide" +); + +Class decl_PolygonFilterImpl (decl_PolygonFilterBase, "db", "PolygonFilter", PolygonFilterImpl::method_decls (true) + callback ("selected", &PolygonFilterImpl::issue_selected, &PolygonFilterImpl::f_selected, gsi::arg ("polygon"), "@brief Selects a polygon\n" "This method is the actual payload. It needs to be reimplemented in a derived class.\n" - "It needs to analyze the polygon and return 'true' if it should be kept and 'false' if it should be discarded." + "It needs to analyze the polygon and return 'true' if it should be kept and 'false' if it should be discarded.\n" + "\n" + "Since version 0.30, the polygon carries properties." ), "@brief A generic polygon filter adaptor\n" "\n" @@ -544,12 +704,12 @@ static db::Edges extent_refs_edges (const db::Region *r, double fx1, double fy1, return r->processed (db::RelativeExtentsAsEdges (fx1, fy1, fx2, fy2)); } -static db::Region filtered (const db::Region *r, const PolygonFilterImpl *f) +static db::Region filtered (const db::Region *r, const PolygonFilterBase *f) { return r->filtered (*f); } -static void filter (db::Region *r, const PolygonFilterImpl *f) +static void filter (db::Region *r, const PolygonFilterBase *f) { r->filter (*f); } diff --git a/src/db/db/gsiDeclDbTexts.cc b/src/db/db/gsiDeclDbTexts.cc index 0de49ade6..46c516123 100644 --- a/src/db/db/gsiDeclDbTexts.cc +++ b/src/db/db/gsiDeclDbTexts.cc @@ -42,17 +42,17 @@ class TextFilterImpl public: TextFilterImpl () { } - bool issue_selected (const db::Text &) const + bool issue_selected (const db::TextWithProperties &) const { return false; } - virtual bool selected (const db::Text &text) const + virtual bool selected (const db::Text &text, db::properties_id_type prop_id) const { if (f_selected.can_issue ()) { - return f_selected.issue (&TextFilterImpl::issue_selected, text); + return f_selected.issue (&TextFilterImpl::issue_selected, db::TextWithProperties (text, prop_id)); } else { - return issue_selected (text); + return issue_selected (db::TextWithProperties (text, prop_id)); } } @@ -70,6 +70,8 @@ Class decl_TextFilterImpl ("db", "TextFilter", "@brief Selects a text\n" "This method is the actual payload. It needs to be reimplemented in a derived class.\n" "It needs to analyze the text and return 'true' if it should be kept and 'false' if it should be discarded." + "\n" + "Since version 0.30, the text carries properties." ), "@brief A generic text filter adaptor\n" "\n" diff --git a/src/db/unit_tests/dbEdgePairsTests.cc b/src/db/unit_tests/dbEdgePairsTests.cc index 0974f24cd..1f6153c3d 100644 --- a/src/db/unit_tests/dbEdgePairsTests.cc +++ b/src/db/unit_tests/dbEdgePairsTests.cc @@ -115,7 +115,7 @@ TEST(2) struct EPTestFilter : public db::EdgePairFilterBase { - bool selected (const db::EdgePair &ep) const + bool selected (const db::EdgePair &ep, db::properties_id_type) const { return ep.first ().double_length () < 50; } @@ -171,44 +171,44 @@ TEST(5_InternalAngleFilter) db::EdgePair ep90 (db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 0), db::Point (0, 100))); db::EdgePair epm90 (db::Edge (db::Point (0, 0), db::Point (100, 0)), db::Edge (db::Point (0, 100), db::Point (0, 0))); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, false).selected (ep0), true); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, false).selected (ep180), true); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, false).selected (ep90), false); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, false).selected (epm90), false); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, false).selected (ep45), false); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, false).selected (ep0, 0), true); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, false).selected (ep180, 0), true); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, false).selected (ep90, 0), false); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, false).selected (epm90, 0), false); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, false).selected (ep45, 0), false); - EXPECT_EQ (db::InternalAngleEdgePairFilter (90.0, false).selected (ep0), false); - EXPECT_EQ (db::InternalAngleEdgePairFilter (90.0, false).selected (ep180), false); - EXPECT_EQ (db::InternalAngleEdgePairFilter (90.0, false).selected (ep90), true); - EXPECT_EQ (db::InternalAngleEdgePairFilter (90.0, false).selected (epm90), true); - EXPECT_EQ (db::InternalAngleEdgePairFilter (90.0, false).selected (ep45), false); + EXPECT_EQ (db::InternalAngleEdgePairFilter (90.0, false).selected (ep0, 0), false); + EXPECT_EQ (db::InternalAngleEdgePairFilter (90.0, false).selected (ep180, 0), false); + EXPECT_EQ (db::InternalAngleEdgePairFilter (90.0, false).selected (ep90, 0), true); + EXPECT_EQ (db::InternalAngleEdgePairFilter (90.0, false).selected (epm90, 0), true); + EXPECT_EQ (db::InternalAngleEdgePairFilter (90.0, false).selected (ep45, 0), false); - EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (ep0), false); - EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (ep180), false); - EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (ep90), false); - EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (epm90), false); - EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (ep45), true); - EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (ep45inv), true); + EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (ep0, 0), false); + EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (ep180, 0), false); + EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (ep90, 0), false); + EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (epm90, 0), false); + EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (ep45, 0), true); + EXPECT_EQ (db::InternalAngleEdgePairFilter (45.0, false).selected (ep45inv, 0), true); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true).selected (ep0), false); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true).selected (ep180), false); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true).selected (ep90), true); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true).selected (epm90), true); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true).selected (ep45), true); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true).selected (ep0, 0), false); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true).selected (ep180, 0), false); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true).selected (ep90, 0), true); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true).selected (epm90, 0), true); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true).selected (ep45, 0), true); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (ep0), true); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (ep180), true); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (ep90), false); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (epm90), false); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (ep45), true); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (ep45inv), true); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (ep0, 0), true); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (ep180, 0), true); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (ep90, 0), false); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (epm90, 0), false); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (ep45, 0), true); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, false).selected (ep45inv, 0), true); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep0), false); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep180), false); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep90), true); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (epm90), true); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep45), false); - EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep45inv), false); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep0, 0), false); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep180, 0), false); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep90, 0), true); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (epm90, 0), true); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep45, 0), false); + EXPECT_EQ (db::InternalAngleEdgePairFilter (0.0, true, 45.0, true, true).selected (ep45inv, 0), false); } TEST(6_add_with_properties) diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 9cb46a1dd..e60250ee8 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -1583,6 +1583,52 @@ class DBRegion_TestClass < TestBase end + # properties + def test_prop_filters + + r = RBA::Region::new + r.insert(RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { "one" => -1 })) + r.insert(RBA::PolygonWithProperties::new(RBA::Box::new(1, 1, 101, 201), { "one" => 17 })) + r.insert(RBA::PolygonWithProperties::new(RBA::Box::new(2, 2, 102, 202), { "one" => 42 })) + + assert_equal(r.filtered(RBA::PolygonFilter::property_filter("one", 11)).to_s, "") + assert_equal(r.filtered(RBA::PolygonFilter::property_filter("two", 17)).to_s, "") + assert_equal(r.filtered(RBA::PolygonFilter::property_filter("one", 17)).to_s, "(1,1;1,201;101,201;101,1){one=>17}") + assert_equal(r.filtered(RBA::PolygonFilter::property_filter("one", 17, true)).to_s, "(2,2;2,202;102,202;102,2){one=>42};(0,0;0,200;100,200;100,0){one=>-1}") + assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, nil)).to_s, "(1,1;1,201;101,201;101,1){one=>17};(2,2;2,202;102,202;102,2){one=>42}") + assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, 18)).to_s, "(1,1;1,201;101,201;101,1){one=>17}") + assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, 18, true)).to_s, "(2,2;2,202;102,202;102,2){one=>42};(0,0;0,200;100,200;100,0){one=>-1}") + assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", nil, 18)).to_s, "(1,1;1,201;101,201;101,1){one=>17};(0,0;0,200;100,200;100,0){one=>-1}") + assert_equal(r.filtered(RBA::PolygonFilter::property_glob("one", "1*")).to_s, "(1,1;1,201;101,201;101,1){one=>17}") + assert_equal(r.filtered(RBA::PolygonFilter::property_glob("one", "1*", true)).to_s, "(2,2;2,202;102,202;102,2){one=>42};(0,0;0,200;100,200;100,0){one=>-1}") + + ly = RBA::Layout::new + top = ly.create_cell("TOP") + l1 = ly.layer(1, 0) + + s = top.shapes(l1) + s.insert(RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { "one" => -1 })) + s.insert(RBA::PolygonWithProperties::new(RBA::Box::new(1, 1, 101, 201), { "one" => 17 })) + s.insert(RBA::PolygonWithProperties::new(RBA::Box::new(2, 2, 102, 202), { "one" => 42 })) + + dss = RBA::DeepShapeStore::new + iter = top.begin_shapes_rec(l1) + iter.enable_properties() + r = RBA::Region::new(iter, dss) + + assert_equal(r.filtered(RBA::PolygonFilter::property_filter("one", 11)).to_s, "") + assert_equal(r.filtered(RBA::PolygonFilter::property_filter("two", 17)).to_s, "") + assert_equal(r.filtered(RBA::PolygonFilter::property_filter("one", 17)).to_s, "(1,1;1,201;101,201;101,1){one=>17}") + assert_equal(r.filtered(RBA::PolygonFilter::property_filter("one", 17, true)).to_s, "(0,0;0,200;100,200;100,0){one=>-1};(2,2;2,202;102,202;102,2){one=>42}") + assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, nil)).to_s, "(1,1;1,201;101,201;101,1){one=>17};(2,2;2,202;102,202;102,2){one=>42}") + assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, 18)).to_s, "(1,1;1,201;101,201;101,1){one=>17}") + assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, 18, true)).to_s, "(0,0;0,200;100,200;100,0){one=>-1};(2,2;2,202;102,202;102,2){one=>42}") + assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", nil, 18)).to_s, "(0,0;0,200;100,200;100,0){one=>-1};(1,1;1,201;101,201;101,1){one=>17}") + assert_equal(r.filtered(RBA::PolygonFilter::property_glob("one", "1*")).to_s, "(1,1;1,201;101,201;101,1){one=>17}") + assert_equal(r.filtered(RBA::PolygonFilter::property_glob("one", "1*", true)).to_s, "(0,0;0,200;100,200;100,0){one=>-1};(2,2;2,202;102,202;102,2){one=>42}") + + end + end load("test_epilogue.rb") From 1fed2767e8921e60f6f38db73ce1b9f4d7e88f04 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Feb 2025 13:35:49 +0100 Subject: [PATCH 018/392] Property filter for edges too. --- src/db/db/db.pro | 2 + src/db/db/dbCompoundOperation.h | 2 +- src/db/db/dbEdgesDelegate.h | 2 +- src/db/db/dbEdgesUtils.h | 29 +++- src/db/db/dbPropertiesFilter.cc | 82 ++++++++++++ src/db/db/dbPropertiesFilter.h | 132 ++++++++++++++++++ src/db/db/gsiDeclDbEdges.cc | 94 +++++++++---- src/db/db/gsiDeclDbRegion.cc | 134 +++---------------- src/db/unit_tests/dbPropertiesFilterTests.cc | 103 ++++++++++++++ src/db/unit_tests/unit_tests.pro | 1 + testdata/ruby/dbEdgesTest.rb | 54 +++++++- testdata/ruby/dbRegionTest.rb | 40 +++--- 12 files changed, 513 insertions(+), 162 deletions(-) create mode 100644 src/db/db/dbPropertiesFilter.cc create mode 100644 src/db/db/dbPropertiesFilter.h create mode 100644 src/db/unit_tests/dbPropertiesFilterTests.cc diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 1b0c205b2..499046cc7 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -79,6 +79,7 @@ SOURCES = \ dbPolygonNeighborhood.cc \ dbPolygonTools.cc \ dbPolygonGenerators.cc \ + dbPropertiesFilter.cc \ dbPropertiesRepository.cc \ dbReader.cc \ dbRecursiveInstanceIterator.cc \ @@ -315,6 +316,7 @@ HEADERS = \ dbPolygonNeighborhood.h \ dbPolygonTools.h \ dbPolygonGenerators.h \ + dbPropertiesFilter.h \ dbPropertiesRepository.h \ dbPropertyConstraint.h \ dbReader.h \ diff --git a/src/db/db/dbCompoundOperation.h b/src/db/db/dbCompoundOperation.h index ca42cf77e..4f109553a 100644 --- a/src/db/db/dbCompoundOperation.h +++ b/src/db/db/dbCompoundOperation.h @@ -1064,7 +1064,7 @@ private: child (0)->compute_local (cache, layout, cell, interactions, one, proc); if (m_sum_of) { - if (mp_filter->selected (one.front ())) { + if (mp_filter->selected_set (one.front ())) { results.front ().insert (one.front ().begin (), one.front ().end ()); } } else { diff --git a/src/db/db/dbEdgesDelegate.h b/src/db/db/dbEdgesDelegate.h index f3e8cab77..7424bfedf 100644 --- a/src/db/db/dbEdgesDelegate.h +++ b/src/db/db/dbEdgesDelegate.h @@ -62,7 +62,7 @@ public: * @brief Filters the edge set * If this method returns true, the edges are kept. Otherwise they are discarded. */ - virtual bool selected (const std::unordered_set &edge) const = 0; + virtual bool selected_set (const std::unordered_set &edge) const = 0; /** * @brief Returns the transformation reducer for building cell variants diff --git a/src/db/db/dbEdgesUtils.h b/src/db/db/dbEdgesUtils.h index 8daa404e8..d0082806d 100644 --- a/src/db/db/dbEdgesUtils.h +++ b/src/db/db/dbEdgesUtils.h @@ -79,7 +79,7 @@ struct DB_PUBLIC EdgeLengthFilter /** * @brief Returns true if the total edge length matches the criterion */ - bool selected (const std::unordered_set &edges) const + bool selected_set (const std::unordered_set &edges) const { length_type l = 0; for (std::unordered_set::const_iterator e = edges.begin (); e != edges.end (); ++e) { @@ -209,7 +209,7 @@ struct DB_PUBLIC EdgeOrientationFilter /** * @brief Returns true if all edge orientations match the criterion */ - virtual bool selected (const std::unordered_set &edges) const + virtual bool selected_set (const std::unordered_set &edges) const { for (std::unordered_set::const_iterator e = edges.begin (); e != edges.end (); ++e) { if (! selected (*e, e->properties_id ())) { @@ -279,7 +279,7 @@ struct DB_PUBLIC SpecialEdgeOrientationFilter /** * @brief Returns true if all edge orientations match the criterion */ - virtual bool selected (const std::unordered_set &edges) const + virtual bool selected_set (const std::unordered_set &edges) const { for (std::unordered_set::const_iterator e = edges.begin (); e != edges.end (); ++e) { if (! selected (*e, e->properties_id ())) { @@ -319,6 +319,29 @@ private: db::OrthogonalTransformationReducer m_vars; }; +/** + * @brief A filter implementation which implements the set filters through "all must match" + */ + +struct DB_PUBLIC AllEdgesMustMatchFilter + : public EdgeFilterBase +{ + /** + * @brief Constructor + */ + AllEdgesMustMatchFilter () { } + + virtual bool selected_set (const std::unordered_set &edges) const + { + for (std::unordered_set::const_iterator p = edges.begin (); p != edges.end (); ++p) { + if (! selected (*p, p->properties_id ())) { + return false; + } + } + return true; + } +}; + /** * @brief A predicate defining edge a interacts with b */ diff --git a/src/db/db/dbPropertiesFilter.cc b/src/db/db/dbPropertiesFilter.cc new file mode 100644 index 000000000..b2e065c5b --- /dev/null +++ b/src/db/db/dbPropertiesFilter.cc @@ -0,0 +1,82 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbPropertiesFilter.h" + +namespace db +{ + +PropertiesFilter::PropertiesFilter (const tl::Variant &name, const tl::Variant &value, bool inverse) + : m_name_id (db::property_names_id (name)), m_value_from (value), m_exact (true), m_glob (false), m_inverse (inverse) +{ + // .. nothing yet .. +} + +PropertiesFilter::PropertiesFilter (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse) + : m_name_id (db::property_names_id (name)), m_value_from (from), m_value_to (to), m_exact (false), m_glob (false), m_inverse (inverse) +{ + // .. nothing yet .. +} + +PropertiesFilter::PropertiesFilter (const tl::Variant &name, const tl::GlobPattern &pattern, bool inverse) + : m_name_id (db::property_names_id (name)), m_pattern (pattern), m_exact (true), m_glob (true), m_inverse (inverse) +{ + // .. nothing yet .. +} + +bool +PropertiesFilter::prop_selected (db::properties_id_type prop_id) const +{ + tl::MutexLocker locker (&m_lock); + + auto c = m_cache.find (prop_id); + if (c != m_cache.end ()) { + return c->second; + } + + bool res = prop_selected_impl (prop_id); + m_cache.insert (std::make_pair (prop_id, res)); + return res; +} + +bool +PropertiesFilter::prop_selected_impl (db::properties_id_type prop_id) const +{ + const db::PropertiesSet &ps = db::properties (prop_id); + if (ps.has_value (m_name_id)) { + + const tl::Variant &value = ps.value (m_name_id); + + if (m_glob) { + return m_pattern.match (value.to_string ()) != m_inverse; + } else if (m_exact) { + return (value == m_value_from) != m_inverse; + } else { + return ((m_value_from.is_nil () || ! (value < m_value_from)) && (m_value_to.is_nil () || value < m_value_to)) != m_inverse; + } + + } else { + return m_inverse; + } +} + +} diff --git a/src/db/db/dbPropertiesFilter.h b/src/db/db/dbPropertiesFilter.h new file mode 100644 index 000000000..4b3d71ea0 --- /dev/null +++ b/src/db/db/dbPropertiesFilter.h @@ -0,0 +1,132 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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_dbPropertiesFilter +#define HDR_dbPropertiesFilter + +#include "dbCommon.h" +#include "dbPropertiesRepository.h" +#include "dbPolygon.h" +#include "tlVariant.h" +#include "tlGlobPattern.h" +#include "tlThreads.h" + +#include + +namespace db +{ + +/** + * @brief A properties filter + * + * This is a base class for PolygonFilters, EdgeFilters etc. for selecting + * Polygons from Regions by property. + */ +class DB_PUBLIC PropertiesFilter +{ +public: + PropertiesFilter (const tl::Variant &name, const tl::Variant &value, bool inverse); + PropertiesFilter (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse); + PropertiesFilter (const tl::Variant &name, const tl::GlobPattern &pattern, bool inverse); + + bool prop_selected (db::properties_id_type prop_id) const; + +private: + bool prop_selected_impl (db::properties_id_type prop_id) const; + + mutable std::map m_cache; + db::property_names_id_type m_name_id; + tl::Variant m_value_from, m_value_to; + tl::GlobPattern m_pattern; + bool m_exact; + bool m_glob; + bool m_inverse; + mutable tl::Mutex m_lock; +}; + +template +class polygon_properties_filter + : public PolygonFilter, public PropertiesFilter +{ +public: + polygon_properties_filter (const tl::Variant &name, const tl::GlobPattern &pattern, bool inverse) + : PropertiesFilter (name, pattern, inverse) + { + // .. nothing yet .. + } + + polygon_properties_filter (const tl::Variant &name, const tl::Variant &value, bool inverse) + : PropertiesFilter (name, value, inverse) + { + // .. nothing yet .. + } + + polygon_properties_filter (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse) + : PropertiesFilter (name, from, to, inverse) + { + // .. nothing yet .. + } + + bool selected (const db::Polygon &, db::properties_id_type prop_id) const + { + return PropertiesFilter::prop_selected (prop_id); + } + + bool selected (const db::PolygonRef &, db::properties_id_type prop_id) const + { + return PropertiesFilter::prop_selected (prop_id); + } +}; + +template +class generic_properties_filter + : public BasicFilter, public PropertiesFilter +{ +public: + generic_properties_filter (const tl::Variant &name, const tl::GlobPattern &pattern, bool inverse) + : PropertiesFilter (name, pattern, inverse) + { + // .. nothing yet .. + } + + generic_properties_filter (const tl::Variant &name, const tl::Variant &value, bool inverse) + : PropertiesFilter (name, value, inverse) + { + // .. nothing yet .. + } + + generic_properties_filter (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse) + : PropertiesFilter (name, from, to, inverse) + { + // .. nothing yet .. + } + + bool selected (const ShapeType &, db::properties_id_type prop_id) const + { + return PropertiesFilter::prop_selected (prop_id); + } +}; + +} + +#endif diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 8339805b4..ec03fd2c1 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -31,6 +31,7 @@ #include "dbRegion.h" #include "dbOriginalLayerRegion.h" #include "dbLayoutUtils.h" +#include "dbPropertiesFilter.h" #include "gsiDeclDbContainerHelpers.h" @@ -40,8 +41,10 @@ namespace gsi // --------------------------------------------------------------------------------- // EdgeFilter binding +typedef shape_filter_impl EdgeFilterBase; + class EdgeFilterImpl - : public shape_filter_impl + : public gsi::EdgeFilterBase { public: EdgeFilterImpl () { } @@ -60,25 +63,6 @@ public: } } - // Returns true if all edges match the criterion - virtual bool selected (const std::unordered_set &edges) const - { - if (f_selected.can_issue ()) { - for (std::unordered_set::const_iterator e = edges.begin (); e != edges.end (); ++e) { - if (! f_selected.issue (&EdgeFilterImpl::issue_selected, *e)) { - return false; - } - } - } else { - for (std::unordered_set::const_iterator e = edges.begin (); e != edges.end (); ++e) { - if (! issue_selected (*e)) { - return false; - } - } - } - return true; - } - gsi::Callback f_selected; private: @@ -87,8 +71,70 @@ private: EdgeFilterImpl (const EdgeFilterImpl &); }; -Class decl_EdgeFilterImpl ("db", "EdgeFilter", - EdgeFilterImpl::method_decls (true) + +typedef db::generic_properties_filter EdgePropertiesFilter; + +static gsi::EdgeFilterBase *make_ppf1 (const tl::Variant &name, const tl::Variant &value, bool inverse) +{ + return new EdgePropertiesFilter (name, value, inverse); +} + +static gsi::EdgeFilterBase *make_ppf2 (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse) +{ + return new EdgePropertiesFilter (name, from, to, inverse); +} + +static gsi::EdgeFilterBase *make_pg (const tl::Variant &name, const std::string &glob, bool inverse, bool case_sensitive) +{ + tl::GlobPattern pattern (glob); + pattern.set_case_sensitive (case_sensitive); + return new EdgePropertiesFilter (name, pattern, inverse); +} + +Class decl_EdgeFilterBase ("db", "EdgeFilterBase", + gsi::EdgeFilterBase::method_decls (true) + + gsi::constructor ("property_glob", &make_pg, gsi::arg ("name"), gsi::arg ("pattern"), gsi::arg ("inverse", false), gsi::arg ("case_sensitive", true), + "@brief Creates a single-valued property filter\n" + "@param name The name of the property to use.\n" + "@param value The glob pattern to match the property value against.\n" + "@param inverse If true, inverts the selection - i.e. all edges without a matching property are selected.\n" + "@param case_sensitive If true, the match is case sensitive (the default), if false, the match is not case sensitive.\n" + "\n" + "Apply this filter with \\Edges#filtered:\n" + "\n" + "@code\n" + "# edges is a Edges object\n" + "# filtered_edges contains all edges where the 'net' property starts with 'C':\n" + "filtered_edges = edges.filtered(RBA::EdgeFilterBase::property_glob('net', 'C*'))\n" + "@/code\n" + "\n" + "This feature has been introduced in version 0.30." + ) + + gsi::constructor ("property_filter", &make_ppf1, gsi::arg ("name"), gsi::arg ("value"), gsi::arg ("inverse", false), + "@brief Creates a single-valued property filter\n" + "@param name The name of the property to use.\n" + "@param value The value against which the property is checked (exact match).\n" + "@param inverse If true, inverts the selection - i.e. all edges without a property with the given name and value are selected.\n" + "\n" + "Apply this filter with \\Edges#filtered. See \\property_glob for an example.\n" + "\n" + "This feature has been introduced in version 0.30." + ) + + gsi::constructor ("property_filter_bounded", &make_ppf2, gsi::arg ("name"), gsi::arg ("from"), gsi::arg ("to"), gsi::arg ("inverse", false), + "@brief Creates a single-valued property filter\n" + "@param name The name of the property to use.\n" + "@param from The lower value against which the property is checked or 'nil' if no lower bound shall be used.\n" + "@param to The upper value against which the property is checked or 'nil' if no upper bound shall be used.\n" + "@param inverse If true, inverts the selection - i.e. all edges without a property with the given name and value range are selected.\n" + "\n" + "This version does a bounded match. The value of the propery needs to be larger or equal to 'from' and less than 'to'.\n" + "Apply this filter with \\Edges#filtered. See \\property_glob for an example.\n" + "\n" + "This feature has been introduced in version 0.30." + ), + "@hide" +); + +Class decl_EdgeFilterImpl (decl_EdgeFilterBase, "db", "EdgeFilter", callback ("selected", &EdgeFilterImpl::issue_selected, &EdgeFilterImpl::f_selected, gsi::arg ("edge"), "@brief Selects an edge\n" "This method is the actual payload. It needs to be reimplemented in a derived class.\n" @@ -455,12 +501,12 @@ static db::Edges moved_xy (const db::Edges *r, db::Coord x, db::Coord y) return r->transformed (db::Disp (db::Vector (x, y))); } -static db::Edges filtered (const db::Edges *r, const EdgeFilterImpl *f) +static db::Edges filtered (const db::Edges *r, const gsi::EdgeFilterBase *f) { return r->filtered (*f); } -static void filter (db::Edges *r, const EdgeFilterImpl *f) +static void filter (db::Edges *r, const gsi::EdgeFilterBase *f) { r->filter (*f); } diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index b01ffd261..86eb77c6d 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -37,6 +37,7 @@ #include "dbCompoundOperation.h" #include "dbLayoutToNetlist.h" #include "dbPropertiesRepository.h" +#include "dbPropertiesFilter.h" #include "tlGlobPattern.h" #include "gsiDeclDbContainerHelpers.h" @@ -51,12 +52,7 @@ namespace gsi // --------------------------------------------------------------------------------- // PolygonFilter binding -class PolygonFilterBase - : public shape_filter_impl -{ -public: - PolygonFilterBase () { } -}; +typedef shape_filter_impl PolygonFilterBase; class PolygonFilterImpl : public PolygonFilterBase @@ -93,128 +89,41 @@ private: PolygonFilterImpl (const PolygonFilterImpl &); }; -/** - * @brief A properties filter - */ -class PropertiesFilter -{ -public: - PropertiesFilter (const tl::Variant &name, const tl::Variant &value, bool inverse) - : m_name_id (db::property_names_id (name)), m_value_from (value), m_exact (true), m_glob (false), m_inverse (inverse) - { - // .. nothing yet .. - } +typedef db::polygon_properties_filter PolygonPropertiesFilter; - PropertiesFilter (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse) - : m_name_id (db::property_names_id (name)), m_value_from (from), m_value_to (to), m_exact (false), m_glob (false), m_inverse (inverse) - { - // .. nothing yet .. - } - - PropertiesFilter (const tl::Variant &name, const std::string &pattern, bool inverse) - : m_name_id (db::property_names_id (name)), m_pattern (pattern), m_exact (true), m_glob (true), m_inverse (inverse) - { - // .. nothing yet .. - } - - bool prop_selected (db::properties_id_type prop_id) const - { - auto c = m_cache.find (prop_id); - if (c != m_cache.end ()) { - return c->second; - } - - bool res = prop_selected_impl (prop_id); - m_cache.insert (std::make_pair (prop_id, res)); - return res; - } - -private: - bool prop_selected_impl (db::properties_id_type prop_id) const - { - const db::PropertiesSet &ps = db::properties (prop_id); - if (ps.has_value (m_name_id)) { - - const tl::Variant &value = ps.value (m_name_id); - - if (m_glob) { - return m_pattern.match (value.to_string ()) != m_inverse; - } else if (m_exact) { - return (value == m_value_from) != m_inverse; - } else { - return ((m_value_from.is_nil () || ! (value < m_value_from)) && (m_value_to.is_nil () || value < m_value_to)) != m_inverse; - } - - } else { - return m_inverse; - } - } - - mutable std::map m_cache; - db::property_names_id_type m_name_id; - tl::Variant m_value_from, m_value_to; - tl::GlobPattern m_pattern; - bool m_exact; - bool m_glob; - bool m_inverse; -}; - -class PolygonPropertiesFilter - : public PolygonFilterBase, public PropertiesFilter -{ -public: - PolygonPropertiesFilter (const tl::Variant &name, const std::string &pattern, bool inverse) - : PropertiesFilter (name, pattern, inverse) - { - // .. nothing yet .. - } - - PolygonPropertiesFilter (const tl::Variant &name, const tl::Variant &value, bool inverse) - : PropertiesFilter (name, value, inverse) - { - // .. nothing yet .. - } - - PolygonPropertiesFilter (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse) - : PropertiesFilter (name, from, to, inverse) - { - // .. nothing yet .. - } - - bool selected (const db::Polygon &, db::properties_id_type prop_id) const - { - return PropertiesFilter::prop_selected (prop_id); - } - - bool selected (const db::PolygonRef &, db::properties_id_type prop_id) const - { - return PropertiesFilter::prop_selected (prop_id); - } -}; - -static PolygonFilterBase *make_ppf1 (const tl::Variant &name, const tl::Variant &value, bool inverse) +static gsi::PolygonFilterBase *make_ppf1 (const tl::Variant &name, const tl::Variant &value, bool inverse) { return new PolygonPropertiesFilter (name, value, inverse); } -static PolygonFilterBase *make_ppf2 (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse) +static gsi::PolygonFilterBase *make_ppf2 (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse) { return new PolygonPropertiesFilter (name, from, to, inverse); } -static PolygonFilterBase *make_pg (const tl::Variant &name, const std::string &glob, bool inverse) +static gsi::PolygonFilterBase *make_pg (const tl::Variant &name, const std::string &glob, bool inverse, bool case_sensitive) { - return new PolygonPropertiesFilter (name, glob, inverse); + tl::GlobPattern pattern (glob); + pattern.set_case_sensitive (case_sensitive); + return new PolygonPropertiesFilter (name, pattern, inverse); } Class decl_PolygonFilterBase ("db", "PolygonFilterBase", - gsi::constructor ("property_glob", &make_pg, gsi::arg ("name"), gsi::arg ("pattern"), gsi::arg ("inverse", false), + gsi::PolygonFilterBase::method_decls (true) + + gsi::constructor ("property_glob", &make_pg, gsi::arg ("name"), gsi::arg ("pattern"), gsi::arg ("inverse", false), gsi::arg ("case_sensitive", true), "@brief Creates a single-valued property filter\n" "@param name The name of the property to use.\n" "@param value The glob pattern to match the property value against.\n" "@param inverse If true, inverts the selection - i.e. all polygons without a matching property are selected.\n" + "@param case_sensitive If true, the match is case sensitive (the default), if false, the match is not case sensitive.\n" "\n" - "Apply this filter with \\Region#filtered.\n" + "Apply this filter with \\Region#filtered:\n" + "\n" + "@code\n" + "# region is a Region object\n" + "# filtered_region contains all polygons where the 'net' property starts with 'C':\n" + "filtered_region = region.filtered(RBA::PolygonFilterBase::property_glob('net', 'C*'))\n" + "@/code\n" "\n" "This feature has been introduced in version 0.30." ) + @@ -224,7 +133,7 @@ Class decl_PolygonFilterBase ("db", "PolygonFilterBase", "@param value The value against which the property is checked (exact match).\n" "@param inverse If true, inverts the selection - i.e. all polygons without a property with the given name and value are selected.\n" "\n" - "Apply this filter with \\Region#filtered.\n" + "Apply this filter with \\Region#filtered. See \\property_glob for an example.\n" "\n" "This feature has been introduced in version 0.30." ) + @@ -236,7 +145,7 @@ Class decl_PolygonFilterBase ("db", "PolygonFilterBase", "@param inverse If true, inverts the selection - i.e. all polygons without a property with the given name and value range are selected.\n" "\n" "This version does a bounded match. The value of the propery needs to be larger or equal to 'from' and less than 'to'.\n" - "Apply this filter with \\Region#filtered.\n" + "Apply this filter with \\Region#filtered. See \\property_glob for an example.\n" "\n" "This feature has been introduced in version 0.30." ), @@ -244,7 +153,6 @@ Class decl_PolygonFilterBase ("db", "PolygonFilterBase", ); Class decl_PolygonFilterImpl (decl_PolygonFilterBase, "db", "PolygonFilter", - PolygonFilterImpl::method_decls (true) + callback ("selected", &PolygonFilterImpl::issue_selected, &PolygonFilterImpl::f_selected, gsi::arg ("polygon"), "@brief Selects a polygon\n" "This method is the actual payload. It needs to be reimplemented in a derived class.\n" diff --git a/src/db/unit_tests/dbPropertiesFilterTests.cc b/src/db/unit_tests/dbPropertiesFilterTests.cc new file mode 100644 index 000000000..208c2894f --- /dev/null +++ b/src/db/unit_tests/dbPropertiesFilterTests.cc @@ -0,0 +1,103 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbPropertiesFilter.h" +#include "tlUnitTest.h" + +namespace { + +/** + * @brief Installs a temporary repository instance for testing + * + * By using a temp instance, we do not disturb other tests. + */ +class TempPropertiesRepository +{ +public: + TempPropertiesRepository () + { + db::PropertiesRepository::replace_instance_temporarily (&m_temp); + } + + ~TempPropertiesRepository () + { + db::PropertiesRepository::replace_instance_temporarily (0); + } + +private: + db::PropertiesRepository m_temp; +}; + +} + +TEST(1) +{ + TempPropertiesRepository temp_pr; + + db::PropertiesSet ps; + ps.insert ("net", 17); + auto net17 = properties_id (ps); + + ps.clear (); + ps.insert ("net", 1); + auto net1 = properties_id (ps); + + ps.clear (); + ps.insert ("net", 42); + ps.insert ("not", "never"); + auto net42 = properties_id (ps); + + EXPECT_EQ (db::PropertiesFilter ("not", tl::Variant ("never"), false).prop_selected (net17), false); + EXPECT_EQ (db::PropertiesFilter ("not", tl::Variant ("never"), true).prop_selected (net17), true); + EXPECT_EQ (db::PropertiesFilter ("not", tl::Variant ("never"), false).prop_selected (net1), false); + EXPECT_EQ (db::PropertiesFilter ("not", tl::Variant ("never"), true).prop_selected (net1), true); + EXPECT_EQ (db::PropertiesFilter ("not", tl::Variant ("never"), false).prop_selected (net42), true); + EXPECT_EQ (db::PropertiesFilter ("not", tl::Variant ("never"), true).prop_selected (net42), false); + EXPECT_EQ (db::PropertiesFilter ("doesnotexist", tl::Variant ("never"), false).prop_selected (net42), false); + EXPECT_EQ (db::PropertiesFilter ("doesnotexist", tl::Variant ("never"), true).prop_selected (net42), true); + EXPECT_EQ (db::PropertiesFilter ("net", 17, false).prop_selected (net17), true); + EXPECT_EQ (db::PropertiesFilter ("net", 17, true).prop_selected (net17), false); + EXPECT_EQ (db::PropertiesFilter ("net", 17, false).prop_selected (net1), false); + EXPECT_EQ (db::PropertiesFilter ("net", 17, true).prop_selected (net1), true); + EXPECT_EQ (db::PropertiesFilter ("net", tl::Variant (), 17, false).prop_selected (net1), true); + EXPECT_EQ (db::PropertiesFilter ("net", tl::Variant (), 17, true).prop_selected (net1), false); + EXPECT_EQ (db::PropertiesFilter ("net", tl::Variant (), 1, false).prop_selected (net1), false); + EXPECT_EQ (db::PropertiesFilter ("net", tl::Variant (), 1, true).prop_selected (net1), true); + EXPECT_EQ (db::PropertiesFilter ("net", 0, 2, false).prop_selected (net1), true); + EXPECT_EQ (db::PropertiesFilter ("net", 0, 2, true).prop_selected (net1), false); + EXPECT_EQ (db::PropertiesFilter ("net", 0, 1, false).prop_selected (net1), false); + EXPECT_EQ (db::PropertiesFilter ("net", 0, 1, true).prop_selected (net1), true); + EXPECT_EQ (db::PropertiesFilter ("net", 1, 2, false).prop_selected (net1), true); + EXPECT_EQ (db::PropertiesFilter ("net", 1, 2, true).prop_selected (net1), false); + EXPECT_EQ (db::PropertiesFilter ("net", -1, tl::Variant (), false).prop_selected (net1), true); + EXPECT_EQ (db::PropertiesFilter ("net", -1, tl::Variant (), true).prop_selected (net1), false); + EXPECT_EQ (db::PropertiesFilter ("net", 2, tl::Variant (), false).prop_selected (net1), false); + EXPECT_EQ (db::PropertiesFilter ("net", 2, tl::Variant (), true).prop_selected (net1), true); + EXPECT_EQ (db::PropertiesFilter ("net", tl::GlobPattern ("1*"), false).prop_selected (net1), true); + EXPECT_EQ (db::PropertiesFilter ("net", tl::GlobPattern ("1*"), true).prop_selected (net1), false); + EXPECT_EQ (db::PropertiesFilter ("not", tl::GlobPattern ("1*"), false).prop_selected (net1), false); + EXPECT_EQ (db::PropertiesFilter ("not", tl::GlobPattern ("1*"), true).prop_selected (net1), true); + EXPECT_EQ (db::PropertiesFilter ("net", tl::GlobPattern ("1*"), false).prop_selected (net17), true); + EXPECT_EQ (db::PropertiesFilter ("net", tl::GlobPattern ("1*"), true).prop_selected (net17), false); + EXPECT_EQ (db::PropertiesFilter ("net", tl::GlobPattern ("1*"), false).prop_selected (net42), false); + EXPECT_EQ (db::PropertiesFilter ("net", tl::GlobPattern ("1*"), true).prop_selected (net42), true); +} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 5183520f5..a24390237 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -13,6 +13,7 @@ SOURCES = \ dbLogTests.cc \ dbObjectWithPropertiesTests.cc \ dbPolygonNeighborhoodTests.cc \ + dbPropertiesFilterTests.cc \ dbRecursiveInstanceIteratorTests.cc \ dbRegionCheckUtilsTests.cc \ dbTriangleTests.cc \ diff --git a/testdata/ruby/dbEdgesTest.rb b/testdata/ruby/dbEdgesTest.rb index 590c247a9..5f7db1bb7 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -26,8 +26,8 @@ load("test_prologue.rb") # normalizes a specification string for region, edges etc. # such that the order of the objects becomes irrelevant def csort(s) - # splits at ");(" without consuming the brackets - s.split(/(?<=\));(?=\()/).sort.join(";") + # splits at ");(" or "};(" without consuming the brackets + s.split(/(?<=[\)\}]);(?=\()/).sort.join(";") end class ParallelFilter < RBA::EdgeFilter @@ -1037,6 +1037,56 @@ class DBEdges_TestClass < TestBase end + # properties + def test_prop_filters + + r = RBA::Edges::new + r.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 200), { "one" => -1 })) + r.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(1, 1, 101, 201), { "one" => 17 })) + r.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(2, 2, 102, 202), { "one" => 42 })) + + assert_equal(r.filtered(RBA::EdgeFilter::property_filter("one", 11)).to_s, "") + assert_equal(r.filtered(RBA::EdgeFilter::property_filter("two", 17)).to_s, "") + assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter("one", 17)).to_s), csort("(1,1;101,201){one=>17}")) + assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter("one", 17, true)).to_s), csort("(0,0;100,200){one=>-1};(2,2;102,202){one=>42}")) + assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter_bounded("one", 17, nil)).to_s), csort("(2,2;102,202){one=>42};(1,1;101,201){one=>17}")) + assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter_bounded("one", 17, 18)).to_s), csort("(1,1;101,201){one=>17}")) + assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter_bounded("one", 17, 18, true)).to_s), csort("(2,2;102,202){one=>42};(0,0;100,200){one=>-1}")) + assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter_bounded("one", nil, 18)).to_s), csort("(1,1;101,201){one=>17};(0,0;100,200){one=>-1}")) + assert_equal(csort(r.filtered(RBA::EdgeFilter::property_glob("one", "1*")).to_s), csort("(1,1;101,201){one=>17}")) + assert_equal(csort(r.filtered(RBA::EdgeFilter::property_glob("one", "1*", true)).to_s), csort("(2,2;102,202){one=>42};(0,0;100,200){one=>-1}")) + + ly = RBA::Layout::new + top = ly.create_cell("TOP") + l1 = ly.layer(1, 0) + + s = top.shapes(l1) + s.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 200), { "one" => -1 })) + s.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(1, 1, 101, 201), { "one" => 17 })) + s.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(2, 2, 102, 202), { "one" => 42 })) + + dss = RBA::DeepShapeStore::new + iter = top.begin_shapes_rec(l1) + iter.enable_properties() + r = RBA::Edges::new(iter, dss) + + assert_equal(r.filtered(RBA::EdgeFilter::property_filter("one", 11)).to_s, "") + assert_equal(r.filtered(RBA::EdgeFilter::property_filter("two", 17)).to_s, "") + assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter("one", 17)).to_s), csort("(1,1;101,201){one=>17}")) + assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter("one", 17, true)).to_s), csort("(0,0;100,200){one=>-1};(2,2;102,202){one=>42}")) + assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter_bounded("one", 17, nil)).to_s), csort("(2,2;102,202){one=>42};(1,1;101,201){one=>17}")) + assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter_bounded("one", 17, 18)).to_s), csort("(1,1;101,201){one=>17}")) + assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter_bounded("one", 17, 18, true)).to_s), csort("(2,2;102,202){one=>42};(0,0;100,200){one=>-1}")) + assert_equal(csort(r.filtered(RBA::EdgeFilter::property_filter_bounded("one", nil, 18)).to_s), csort("(1,1;101,201){one=>17};(0,0;100,200){one=>-1}")) + assert_equal(csort(r.filtered(RBA::EdgeFilter::property_glob("one", "1*")).to_s), csort("(1,1;101,201){one=>17}")) + assert_equal(csort(r.filtered(RBA::EdgeFilter::property_glob("one", "1*", true)).to_s), csort("(2,2;102,202){one=>42};(0,0;100,200){one=>-1}")) + + rr = r.dup + rr.filter(RBA::EdgeFilter::property_filter("one", 17)) + assert_equal(csort(rr.to_s), csort("(1,1;101,201){one=>17}")) + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index e60250ee8..663232c33 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -26,8 +26,8 @@ load("test_prologue.rb") # normalizes a specification string for region, edges etc. # such that the order of the objects becomes irrelevant def csort(s) - # splits at ");(" without consuming the brackets - s.split(/(?<=\));(?=\()/).sort.join(";") + # splits at ");(" or "};(" without consuming the brackets + s.split(/(?<=[\)\}]);(?=\()/).sort.join(";") end class TriangleFilter < RBA::PolygonFilter @@ -1593,14 +1593,14 @@ class DBRegion_TestClass < TestBase assert_equal(r.filtered(RBA::PolygonFilter::property_filter("one", 11)).to_s, "") assert_equal(r.filtered(RBA::PolygonFilter::property_filter("two", 17)).to_s, "") - assert_equal(r.filtered(RBA::PolygonFilter::property_filter("one", 17)).to_s, "(1,1;1,201;101,201;101,1){one=>17}") - assert_equal(r.filtered(RBA::PolygonFilter::property_filter("one", 17, true)).to_s, "(2,2;2,202;102,202;102,2){one=>42};(0,0;0,200;100,200;100,0){one=>-1}") - assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, nil)).to_s, "(1,1;1,201;101,201;101,1){one=>17};(2,2;2,202;102,202;102,2){one=>42}") - assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, 18)).to_s, "(1,1;1,201;101,201;101,1){one=>17}") - assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, 18, true)).to_s, "(2,2;2,202;102,202;102,2){one=>42};(0,0;0,200;100,200;100,0){one=>-1}") - assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", nil, 18)).to_s, "(1,1;1,201;101,201;101,1){one=>17};(0,0;0,200;100,200;100,0){one=>-1}") - assert_equal(r.filtered(RBA::PolygonFilter::property_glob("one", "1*")).to_s, "(1,1;1,201;101,201;101,1){one=>17}") - assert_equal(r.filtered(RBA::PolygonFilter::property_glob("one", "1*", true)).to_s, "(2,2;2,202;102,202;102,2){one=>42};(0,0;0,200;100,200;100,0){one=>-1}") + assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter("one", 17)).to_s), csort("(1,1;1,201;101,201;101,1){one=>17}")) + assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter("one", 17, true)).to_s), csort("(2,2;2,202;102,202;102,2){one=>42};(0,0;0,200;100,200;100,0){one=>-1}")) + assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, nil)).to_s), csort("(1,1;1,201;101,201;101,1){one=>17};(2,2;2,202;102,202;102,2){one=>42}")) + assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, 18)).to_s), csort("(1,1;1,201;101,201;101,1){one=>17}")) + assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, 18, true)).to_s), csort("(2,2;2,202;102,202;102,2){one=>42};(0,0;0,200;100,200;100,0){one=>-1}")) + assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", nil, 18)).to_s), csort("(1,1;1,201;101,201;101,1){one=>17};(0,0;0,200;100,200;100,0){one=>-1}")) + assert_equal(csort(r.filtered(RBA::PolygonFilter::property_glob("one", "1*")).to_s), csort("(1,1;1,201;101,201;101,1){one=>17}")) + assert_equal(csort(r.filtered(RBA::PolygonFilter::property_glob("one", "1*", true)).to_s), csort("(2,2;2,202;102,202;102,2){one=>42};(0,0;0,200;100,200;100,0){one=>-1}")) ly = RBA::Layout::new top = ly.create_cell("TOP") @@ -1618,14 +1618,18 @@ class DBRegion_TestClass < TestBase assert_equal(r.filtered(RBA::PolygonFilter::property_filter("one", 11)).to_s, "") assert_equal(r.filtered(RBA::PolygonFilter::property_filter("two", 17)).to_s, "") - assert_equal(r.filtered(RBA::PolygonFilter::property_filter("one", 17)).to_s, "(1,1;1,201;101,201;101,1){one=>17}") - assert_equal(r.filtered(RBA::PolygonFilter::property_filter("one", 17, true)).to_s, "(0,0;0,200;100,200;100,0){one=>-1};(2,2;2,202;102,202;102,2){one=>42}") - assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, nil)).to_s, "(1,1;1,201;101,201;101,1){one=>17};(2,2;2,202;102,202;102,2){one=>42}") - assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, 18)).to_s, "(1,1;1,201;101,201;101,1){one=>17}") - assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, 18, true)).to_s, "(0,0;0,200;100,200;100,0){one=>-1};(2,2;2,202;102,202;102,2){one=>42}") - assert_equal(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", nil, 18)).to_s, "(0,0;0,200;100,200;100,0){one=>-1};(1,1;1,201;101,201;101,1){one=>17}") - assert_equal(r.filtered(RBA::PolygonFilter::property_glob("one", "1*")).to_s, "(1,1;1,201;101,201;101,1){one=>17}") - assert_equal(r.filtered(RBA::PolygonFilter::property_glob("one", "1*", true)).to_s, "(0,0;0,200;100,200;100,0){one=>-1};(2,2;2,202;102,202;102,2){one=>42}") + assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter("one", 17)).to_s), csort("(1,1;1,201;101,201;101,1){one=>17}")) + assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter("one", 17, true)).to_s), csort("(0,0;0,200;100,200;100,0){one=>-1};(2,2;2,202;102,202;102,2){one=>42}")) + assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, nil)).to_s), csort("(1,1;1,201;101,201;101,1){one=>17};(2,2;2,202;102,202;102,2){one=>42}")) + assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, 18)).to_s), csort("(1,1;1,201;101,201;101,1){one=>17}")) + assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", 17, 18, true)).to_s), csort("(0,0;0,200;100,200;100,0){one=>-1};(2,2;2,202;102,202;102,2){one=>42}")) + assert_equal(csort(r.filtered(RBA::PolygonFilter::property_filter_bounded("one", nil, 18)).to_s), csort("(0,0;0,200;100,200;100,0){one=>-1};(1,1;1,201;101,201;101,1){one=>17}")) + assert_equal(csort(r.filtered(RBA::PolygonFilter::property_glob("one", "1*")).to_s), csort("(1,1;1,201;101,201;101,1){one=>17}")) + assert_equal(csort(r.filtered(RBA::PolygonFilter::property_glob("one", "1*", true)).to_s), csort("(0,0;0,200;100,200;100,0){one=>-1};(2,2;2,202;102,202;102,2){one=>42}")) + + rr = r.dup + rr.filter(RBA::PolygonFilter::property_filter("one", 17)) + assert_equal(csort(rr.to_s), csort("(1,1;1,201;101,201;101,1){one=>17}")) end From ae5ae25000f1a97d2feda7396d8bf92b02920040 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Feb 2025 13:59:50 +0100 Subject: [PATCH 019/392] Properties filters for EdgePairs and Texts too. --- src/db/db/gsiDeclDbEdgePairs.cc | 75 +++++++++++++++++++++++++++++--- src/db/db/gsiDeclDbTexts.cc | 75 +++++++++++++++++++++++++++++--- testdata/ruby/dbEdgePairsTest.rb | 54 ++++++++++++++++++++++- testdata/ruby/dbTextsTest.rb | 54 ++++++++++++++++++++++- 4 files changed, 244 insertions(+), 14 deletions(-) diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index aec39b3eb..64c01d5b1 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -30,6 +30,7 @@ #include "dbDeepEdgePairs.h" #include "dbEdgesUtils.h" #include "dbEdgePairFilters.h" +#include "dbPropertiesFilter.h" #include "gsiDeclDbContainerHelpers.h" @@ -39,8 +40,10 @@ namespace gsi // --------------------------------------------------------------------------------- // EdgePairFilter binding +typedef shape_filter_impl EdgePairFilterBase; + class EdgePairFilterImpl - : public shape_filter_impl + : public EdgePairFilterBase { public: EdgePairFilterImpl () { } @@ -67,8 +70,70 @@ private: EdgePairFilterImpl (const EdgePairFilterImpl &); }; -Class decl_EdgePairFilterImpl ("db", "EdgePairFilter", - EdgePairFilterImpl::method_decls (false) + +typedef db::generic_properties_filter EdgePairPropertiesFilter; + +static gsi::EdgePairFilterBase *make_ppf1 (const tl::Variant &name, const tl::Variant &value, bool inverse) +{ + return new EdgePairPropertiesFilter (name, value, inverse); +} + +static gsi::EdgePairFilterBase *make_ppf2 (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse) +{ + return new EdgePairPropertiesFilter (name, from, to, inverse); +} + +static gsi::EdgePairFilterBase *make_pg (const tl::Variant &name, const std::string &glob, bool inverse, bool case_sensitive) +{ + tl::GlobPattern pattern (glob); + pattern.set_case_sensitive (case_sensitive); + return new EdgePairPropertiesFilter (name, pattern, inverse); +} + +Class decl_EdgePairFilterBase ("db", "EdgePairFilterBase", + gsi::EdgePairFilterBase::method_decls (true) + + gsi::constructor ("property_glob", &make_pg, gsi::arg ("name"), gsi::arg ("pattern"), gsi::arg ("inverse", false), gsi::arg ("case_sensitive", true), + "@brief Creates a single-valued property filter\n" + "@param name The name of the property to use.\n" + "@param value The glob pattern to match the property value against.\n" + "@param inverse If true, inverts the selection - i.e. all edge pairs without a matching property are selected.\n" + "@param case_sensitive If true, the match is case sensitive (the default), if false, the match is not case sensitive.\n" + "\n" + "Apply this filter with \\EdgePairs#filtered:\n" + "\n" + "@code\n" + "# edge_pairs is a EdgePairs object\n" + "# filtered_edge_pairs contains all edge pairs where the 'net' property starts with 'C':\n" + "filtered_edge_pairs = edge_pairs.filtered(RBA::EdgePairFilterBase::property_glob('net', 'C*'))\n" + "@/code\n" + "\n" + "This feature has been introduced in version 0.30." + ) + + gsi::constructor ("property_filter", &make_ppf1, gsi::arg ("name"), gsi::arg ("value"), gsi::arg ("inverse", false), + "@brief Creates a single-valued property filter\n" + "@param name The name of the property to use.\n" + "@param value The value against which the property is checked (exact match).\n" + "@param inverse If true, inverts the selection - i.e. all edge pairs without a property with the given name and value are selected.\n" + "\n" + "Apply this filter with \\EdgePairs#filtered. See \\property_glob for an example.\n" + "\n" + "This feature has been introduced in version 0.30." + ) + + gsi::constructor ("property_filter_bounded", &make_ppf2, gsi::arg ("name"), gsi::arg ("from"), gsi::arg ("to"), gsi::arg ("inverse", false), + "@brief Creates a single-valued property filter\n" + "@param name The name of the property to use.\n" + "@param from The lower value against which the property is checked or 'nil' if no lower bound shall be used.\n" + "@param to The upper value against which the property is checked or 'nil' if no upper bound shall be used.\n" + "@param inverse If true, inverts the selection - i.e. all edge pairs without a property with the given name and value range are selected.\n" + "\n" + "This version does a bounded match. The value of the propery needs to be larger or equal to 'from' and less than 'to'.\n" + "Apply this filter with \\EdgePairs#filtered. See \\property_glob for an example.\n" + "\n" + "This feature has been introduced in version 0.30." + ), + "@hide" +); + +Class decl_EdgePairFilterImpl (decl_EdgePairFilterBase, "db", "EdgePairFilter", callback ("selected", &EdgePairFilterImpl::issue_selected, &EdgePairFilterImpl::f_selected, gsi::arg ("text"), "@brief Selects an edge pair\n" "This method is the actual payload. It needs to be reimplemented in a derived class.\n" @@ -411,12 +476,12 @@ static size_t id (const db::EdgePairs *ep) return tl::id_of (ep->delegate ()); } -static db::EdgePairs filtered (const db::EdgePairs *r, const EdgePairFilterImpl *f) +static db::EdgePairs filtered (const db::EdgePairs *r, const gsi::EdgePairFilterBase *f) { return r->filtered (*f); } -static void filter (db::EdgePairs *r, const EdgePairFilterImpl *f) +static void filter (db::EdgePairs *r, const gsi::EdgePairFilterBase *f) { r->filter (*f); } diff --git a/src/db/db/gsiDeclDbTexts.cc b/src/db/db/gsiDeclDbTexts.cc index 46c516123..549b9d17b 100644 --- a/src/db/db/gsiDeclDbTexts.cc +++ b/src/db/db/gsiDeclDbTexts.cc @@ -27,6 +27,7 @@ #include "dbRegion.h" #include "dbDeepTexts.h" #include "dbTextsUtils.h" +#include "dbPropertiesFilter.h" #include "gsiDeclDbContainerHelpers.h" @@ -36,8 +37,10 @@ namespace gsi // --------------------------------------------------------------------------------- // TextFilter binding +typedef shape_filter_impl TextFilterBase; + class TextFilterImpl - : public shape_filter_impl + : public TextFilterBase { public: TextFilterImpl () { } @@ -64,8 +67,70 @@ private: TextFilterImpl (const TextFilterImpl &); }; -Class decl_TextFilterImpl ("db", "TextFilter", - TextFilterImpl::method_decls (false) + +typedef db::generic_properties_filter TextPropertiesFilter; + +static gsi::TextFilterBase *make_ppf1 (const tl::Variant &name, const tl::Variant &value, bool inverse) +{ + return new TextPropertiesFilter (name, value, inverse); +} + +static gsi::TextFilterBase *make_ppf2 (const tl::Variant &name, const tl::Variant &from, const tl::Variant &to, bool inverse) +{ + return new TextPropertiesFilter (name, from, to, inverse); +} + +static gsi::TextFilterBase *make_pg (const tl::Variant &name, const std::string &glob, bool inverse, bool case_sensitive) +{ + tl::GlobPattern pattern (glob); + pattern.set_case_sensitive (case_sensitive); + return new TextPropertiesFilter (name, pattern, inverse); +} + +Class decl_TextFilterBase ("db", "TextFilterBase", + gsi::TextFilterBase::method_decls (false) + + gsi::constructor ("property_glob", &make_pg, gsi::arg ("name"), gsi::arg ("pattern"), gsi::arg ("inverse", false), gsi::arg ("case_sensitive", true), + "@brief Creates a single-valued property filter\n" + "@param name The name of the property to use.\n" + "@param value The glob pattern to match the property value against.\n" + "@param inverse If true, inverts the selection - i.e. all texts without a matching property are selected.\n" + "@param case_sensitive If true, the match is case sensitive (the default), if false, the match is not case sensitive.\n" + "\n" + "Apply this filter with \\Texts#filtered:\n" + "\n" + "@code\n" + "# texts is a Texts object\n" + "# filtered_texts contains all texts where the 'net' property starts with 'C':\n" + "filtered_texts = texts.filtered(RBA::TextFilterBase::property_glob('net', 'C*'))\n" + "@/code\n" + "\n" + "This feature has been introduced in version 0.30." + ) + + gsi::constructor ("property_filter", &make_ppf1, gsi::arg ("name"), gsi::arg ("value"), gsi::arg ("inverse", false), + "@brief Creates a single-valued property filter\n" + "@param name The name of the property to use.\n" + "@param value The value against which the property is checked (exact match).\n" + "@param inverse If true, inverts the selection - i.e. all texts without a property with the given name and value are selected.\n" + "\n" + "Apply this filter with \\Texts#filtered. See \\property_glob for an example.\n" + "\n" + "This feature has been introduced in version 0.30." + ) + + gsi::constructor ("property_filter_bounded", &make_ppf2, gsi::arg ("name"), gsi::arg ("from"), gsi::arg ("to"), gsi::arg ("inverse", false), + "@brief Creates a single-valued property filter\n" + "@param name The name of the property to use.\n" + "@param from The lower value against which the property is checked or 'nil' if no lower bound shall be used.\n" + "@param to The upper value against which the property is checked or 'nil' if no upper bound shall be used.\n" + "@param inverse If true, inverts the selection - i.e. all texts without a property with the given name and value range are selected.\n" + "\n" + "This version does a bounded match. The value of the propery needs to be larger or equal to 'from' and less than 'to'.\n" + "Apply this filter with \\Texts#filtered. See \\property_glob for an example.\n" + "\n" + "This feature has been introduced in version 0.30." + ), + "@hide" +); + +Class decl_TextFilterImpl (decl_TextFilterBase, "db", "TextFilter", callback ("selected", &TextFilterImpl::issue_selected, &TextFilterImpl::f_selected, gsi::arg ("text"), "@brief Selects a text\n" "This method is the actual payload. It needs to be reimplemented in a derived class.\n" @@ -318,12 +383,12 @@ static size_t id (const db::Texts *t) return tl::id_of (t->delegate ()); } -static db::Texts filtered (const db::Texts *r, const TextFilterImpl *f) +static db::Texts filtered (const db::Texts *r, const gsi::TextFilterBase *f) { return r->filtered (*f); } -static void filter (db::Texts *r, const TextFilterImpl *f) +static void filter (db::Texts *r, const gsi::TextFilterBase *f) { r->filter (*f); } diff --git a/testdata/ruby/dbEdgePairsTest.rb b/testdata/ruby/dbEdgePairsTest.rb index 829b28d95..dfae93bf8 100644 --- a/testdata/ruby/dbEdgePairsTest.rb +++ b/testdata/ruby/dbEdgePairsTest.rb @@ -26,8 +26,8 @@ load("test_prologue.rb") # normalizes a specification string for region, edges etc. # such that the order of the objects becomes irrelevant def csort(s) - # splits at ");(" without consuming the brackets - s.split(/(?<=\));(?=\()/).sort.join(";") + # splits at ");(" or "};(" without consuming the brackets + s.split(/(?<=[\)\}]);(?=\()/).sort.join(";") end class PerpendicularEdgesFilter < RBA::EdgePairFilter @@ -598,6 +598,56 @@ class DBEdgePairs_TestClass < TestBase end + # properties + def test_prop_filters + + r = RBA::EdgePairs::new + r.insert(RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 200), RBA::Edge::new(0, 10, 100, 210)), { "one" => -1 })) + r.insert(RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(1, 1, 101, 201), RBA::Edge::new(1, 11, 101, 211)), { "one" => 17 })) + r.insert(RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(2, 2, 102, 202), RBA::Edge::new(2, 12, 102, 212)), { "one" => 42 })) + + assert_equal(r.filtered(RBA::EdgePairFilter::property_filter("one", 11)).to_s, "") + assert_equal(r.filtered(RBA::EdgePairFilter::property_filter("two", 17)).to_s, "") + assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter("one", 17)).to_s), csort("(1,1;101,201)/(1,11;101,211){one=>17}")) + assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter("one", 17, true)).to_s), csort("(0,0;100,200)/(0,10;100,210){one=>-1};(2,2;102,202)/(2,12;102,212){one=>42}")) + assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter_bounded("one", 17, nil)).to_s), csort("(2,2;102,202)/(2,12;102,212){one=>42};(1,1;101,201)/(1,11;101,211){one=>17}")) + assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter_bounded("one", 17, 18)).to_s), csort("(1,1;101,201)/(1,11;101,211){one=>17}")) + assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter_bounded("one", 17, 18, true)).to_s), csort("(2,2;102,202)/(2,12;102,212){one=>42};(0,0;100,200)/(0,10;100,210){one=>-1}")) + assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter_bounded("one", nil, 18)).to_s), csort("(1,1;101,201)/(1,11;101,211){one=>17};(0,0;100,200)/(0,10;100,210){one=>-1}")) + assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_glob("one", "1*")).to_s), csort("(1,1;101,201)/(1,11;101,211){one=>17}")) + assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_glob("one", "1*", true)).to_s), csort("(2,2;102,202)/(2,12;102,212){one=>42};(0,0;100,200)/(0,10;100,210){one=>-1}")) + + ly = RBA::Layout::new + top = ly.create_cell("TOP") + l1 = ly.layer(1, 0) + + s = top.shapes(l1) + s.insert(RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 200), RBA::Edge::new(0, 10, 100, 210)), { "one" => -1 })) + s.insert(RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(1, 1, 101, 201), RBA::Edge::new(1, 11, 101, 211)), { "one" => 17 })) + s.insert(RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(2, 2, 102, 202), RBA::Edge::new(2, 12, 102, 212)), { "one" => 42 })) + + dss = RBA::DeepShapeStore::new + iter = top.begin_shapes_rec(l1) + iter.enable_properties() + r = RBA::EdgePairs::new(iter, dss) + + assert_equal(r.filtered(RBA::EdgePairFilter::property_filter("one", 11)).to_s, "") + assert_equal(r.filtered(RBA::EdgePairFilter::property_filter("two", 17)).to_s, "") + assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter("one", 17)).to_s), csort("(1,1;101,201)/(1,11;101,211){one=>17}")) + assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter("one", 17, true)).to_s), csort("(0,0;100,200)/(0,10;100,210){one=>-1};(2,2;102,202)/(2,12;102,212){one=>42}")) + assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter_bounded("one", 17, nil)).to_s), csort("(2,2;102,202)/(2,12;102,212){one=>42};(1,1;101,201)/(1,11;101,211){one=>17}")) + assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter_bounded("one", 17, 18)).to_s), csort("(1,1;101,201)/(1,11;101,211){one=>17}")) + assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter_bounded("one", 17, 18, true)).to_s), csort("(2,2;102,202)/(2,12;102,212){one=>42};(0,0;100,200)/(0,10;100,210){one=>-1}")) + assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_filter_bounded("one", nil, 18)).to_s), csort("(1,1;101,201)/(1,11;101,211){one=>17};(0,0;100,200)/(0,10;100,210){one=>-1}")) + assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_glob("one", "1*")).to_s), csort("(1,1;101,201)/(1,11;101,211){one=>17}")) + assert_equal(csort(r.filtered(RBA::EdgePairFilter::property_glob("one", "1*", true)).to_s), csort("(2,2;102,202)/(2,12;102,212){one=>42};(0,0;100,200)/(0,10;100,210){one=>-1}")) + + rr = r.dup + rr.filter(RBA::EdgePairFilter::property_filter("one", 17)) + assert_equal(csort(rr.to_s), csort("(1,1;101,201)/(1,11;101,211){one=>17}")) + + end + end diff --git a/testdata/ruby/dbTextsTest.rb b/testdata/ruby/dbTextsTest.rb index 0ddfabc02..bc7b70875 100644 --- a/testdata/ruby/dbTextsTest.rb +++ b/testdata/ruby/dbTextsTest.rb @@ -26,8 +26,8 @@ load("test_prologue.rb") # normalizes a specification string for region, edges etc. # such that the order of the objects becomes irrelevant def csort(s) - # splits at ");(" without consuming the brackets - s.split(/(?<=\));(?=\()/).sort.join(";") + # splits at ");(" or "};(" without consuming the brackets + s.split(/(?<=[\)\}]);(?=\()/).sort.join(";") end class TextStringLengthFilter < RBA::TextFilter @@ -467,6 +467,56 @@ class DBTexts_TestClass < TestBase end + # properties + def test_prop_filters + + r = RBA::Texts::new + r.insert(RBA::TextWithProperties::new(RBA::Text::new("abc", RBA::Trans::R0), { "one" => -1 })) + r.insert(RBA::TextWithProperties::new(RBA::Text::new("uvw", RBA::Trans::R0), { "one" => 17 })) + r.insert(RBA::TextWithProperties::new(RBA::Text::new("xyz", RBA::Trans::R0), { "one" => 42 })) + + assert_equal(r.filtered(RBA::TextFilter::property_filter("one", 11)).to_s, "") + assert_equal(r.filtered(RBA::TextFilter::property_filter("two", 17)).to_s, "") + assert_equal(csort(r.filtered(RBA::TextFilter::property_filter("one", 17)).to_s), csort("('uvw',r0 0,0){one=>17}")) + assert_equal(csort(r.filtered(RBA::TextFilter::property_filter("one", 17, true)).to_s), csort("('abc',r0 0,0){one=>-1};('xyz',r0 0,0){one=>42}")) + assert_equal(csort(r.filtered(RBA::TextFilter::property_filter_bounded("one", 17, nil)).to_s), csort("('xyz',r0 0,0){one=>42};('uvw',r0 0,0){one=>17}")) + assert_equal(csort(r.filtered(RBA::TextFilter::property_filter_bounded("one", 17, 18)).to_s), csort("('uvw',r0 0,0){one=>17}")) + assert_equal(csort(r.filtered(RBA::TextFilter::property_filter_bounded("one", 17, 18, true)).to_s), csort("('xyz',r0 0,0){one=>42};('abc',r0 0,0){one=>-1}")) + assert_equal(csort(r.filtered(RBA::TextFilter::property_filter_bounded("one", nil, 18)).to_s), csort("('uvw',r0 0,0){one=>17};('abc',r0 0,0){one=>-1}")) + assert_equal(csort(r.filtered(RBA::TextFilter::property_glob("one", "1*")).to_s), csort("('uvw',r0 0,0){one=>17}")) + assert_equal(csort(r.filtered(RBA::TextFilter::property_glob("one", "1*", true)).to_s), csort("('xyz',r0 0,0){one=>42};('abc',r0 0,0){one=>-1}")) + + ly = RBA::Layout::new + top = ly.create_cell("TOP") + l1 = ly.layer(1, 0) + + s = top.shapes(l1) + s.insert(RBA::TextWithProperties::new(RBA::Text::new("abc", RBA::Trans::R0), { "one" => -1 })) + s.insert(RBA::TextWithProperties::new(RBA::Text::new("uvw", RBA::Trans::R0), { "one" => 17 })) + s.insert(RBA::TextWithProperties::new(RBA::Text::new("xyz", RBA::Trans::R0), { "one" => 42 })) + + dss = RBA::DeepShapeStore::new + iter = top.begin_shapes_rec(l1) + iter.enable_properties() + r = RBA::Texts::new(iter, dss) + + assert_equal(r.filtered(RBA::TextFilter::property_filter("one", 11)).to_s, "") + assert_equal(r.filtered(RBA::TextFilter::property_filter("two", 17)).to_s, "") + assert_equal(csort(r.filtered(RBA::TextFilter::property_filter("one", 17)).to_s), csort("('uvw',r0 0,0){one=>17}")) + assert_equal(csort(r.filtered(RBA::TextFilter::property_filter("one", 17, true)).to_s), csort("('abc',r0 0,0){one=>-1};('xyz',r0 0,0){one=>42}")) + assert_equal(csort(r.filtered(RBA::TextFilter::property_filter_bounded("one", 17, nil)).to_s), csort("('xyz',r0 0,0){one=>42};('uvw',r0 0,0){one=>17}")) + assert_equal(csort(r.filtered(RBA::TextFilter::property_filter_bounded("one", 17, 18)).to_s), csort("('uvw',r0 0,0){one=>17}")) + assert_equal(csort(r.filtered(RBA::TextFilter::property_filter_bounded("one", 17, 18, true)).to_s), csort("('xyz',r0 0,0){one=>42};('abc',r0 0,0){one=>-1}")) + assert_equal(csort(r.filtered(RBA::TextFilter::property_filter_bounded("one", nil, 18)).to_s), csort("('uvw',r0 0,0){one=>17};('abc',r0 0,0){one=>-1}")) + assert_equal(csort(r.filtered(RBA::TextFilter::property_glob("one", "1*")).to_s), csort("('uvw',r0 0,0){one=>17}")) + assert_equal(csort(r.filtered(RBA::TextFilter::property_glob("one", "1*", true)).to_s), csort("('xyz',r0 0,0){one=>42};('abc',r0 0,0){one=>-1}")) + + rr = r.dup + rr.filter(RBA::TextFilter::property_filter("one", 17)) + assert_equal(csort(rr.to_s), csort("('uvw',r0 0,0){one=>17}")) + + end + end From 094fccce741055e5a6ba452e1e644050458c5f41 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Feb 2025 15:07:37 +0100 Subject: [PATCH 020/392] Made unit tests more robust --- testdata/python/dbEdgeNeighborhoodWithNets.py | 4 ++-- testdata/ruby/dbEdgePairsTest.rb | 6 ++++++ testdata/ruby/dbEdgesTest.rb | 4 ++++ testdata/ruby/dbRegionTest.rb | 8 ++++++++ testdata/ruby/dbTextsTest.rb | 6 ++++++ 5 files changed, 26 insertions(+), 2 deletions(-) diff --git a/testdata/python/dbEdgeNeighborhoodWithNets.py b/testdata/python/dbEdgeNeighborhoodWithNets.py index e5f986108..6456786e2 100644 --- a/testdata/python/dbEdgeNeighborhoodWithNets.py +++ b/testdata/python/dbEdgeNeighborhoodWithNets.py @@ -41,7 +41,7 @@ class EdgeNeighborhoodWithNetsVisitor(pya.EdgeNeighborhoodVisitor): self.poly[str(edge)] = edge_data for (x1, x2), polygons in neighborhood: for inp, poly in polygons.items(): - poly_str = "/".join([ str(p) for p in poly ]) + poly_str = "/".join(sorted([ str(p) for p in poly ])) edge_data.append(f"{x1},{x2} -> {inp}: {poly_str}") def dump(self): @@ -282,7 +282,7 @@ Polygon: (-9500,0;-9500,15000;0,15000;0,0;-5000,0;-5000,12000;-6500,12000;-6500, 0.0,2500.0 -> 3: (0,-11;0,0;2500,0;2500,-11) props={net=>net1} 2500.0,3000.0 -> 0: (2500,-11;2500,0;3000,0;3000,-11) props={net=>net1} 2500.0,3000.0 -> 1: (2500,1500;2500,5000;3000,5000;3000,1500) props={net=>net3} - 2500.0,3000.0 -> 3: (2500,1500;2500,5000;3000,5000;3000,1500) props={net=>net2}/(2500,-11;2500,0;3000,0;3000,-11) props={net=>net1} + 2500.0,3000.0 -> 3: (2500,-11;2500,0;3000,0;3000,-11) props={net=>net1}/(2500,1500;2500,5000;3000,5000;3000,1500) props={net=>net2} 3000.0,9500.0 -> 0: (3000,-11;3000,0;9500,0;9500,-11) props={net=>net1} 3000.0,9500.0 -> 1: (3000,1500;3000,5000;9500,5000;9500,1500) props={net=>net3} 3000.0,9500.0 -> 3: (3000,1500;3000,5000;9500,5000;9500,1500) props={net=>net2} diff --git a/testdata/ruby/dbEdgePairsTest.rb b/testdata/ruby/dbEdgePairsTest.rb index dfae93bf8..56d853517 100644 --- a/testdata/ruby/dbEdgePairsTest.rb +++ b/testdata/ruby/dbEdgePairsTest.rb @@ -244,6 +244,8 @@ class DBEdgePairs_TestClass < TestBase assert_equal(r.is_deep?, true) + dss._destroy + end def test_4 @@ -282,6 +284,8 @@ class DBEdgePairs_TestClass < TestBase 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)") + dss._destroy + end def test_5 @@ -646,6 +650,8 @@ class DBEdgePairs_TestClass < TestBase rr.filter(RBA::EdgePairFilter::property_filter("one", 17)) assert_equal(csort(rr.to_s), csort("(1,1;101,201)/(1,11;101,211){one=>17}")) + dss._destroy + end end diff --git a/testdata/ruby/dbEdgesTest.rb b/testdata/ruby/dbEdgesTest.rb index 5f7db1bb7..0f385893e 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -780,6 +780,8 @@ class DBEdges_TestClass < TestBase assert_equal(r.hier_count, 12) assert_equal(r.to_s, "(-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)...") + dss._destroy + end # inside @@ -1085,6 +1087,8 @@ class DBEdges_TestClass < TestBase rr.filter(RBA::EdgeFilter::property_filter("one", 17)) assert_equal(csort(rr.to_s), csort("(1,1;101,201){one=>17}")) + dss._destroy + end end diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 663232c33..7c3c04eb7 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -1049,6 +1049,8 @@ class DBRegion_TestClass < TestBase assert_equal(t.to_s, "(99,199;99,201;101,201;101,199)") assert_equal(t.is_deep?, true) + dss._destroy + r = RBA::Region::new(top.begin_shapes_rec(l1)) t = r.texts_dots("*", true) assert_equal(t.to_s, "(100,200;100,200)") @@ -1060,6 +1062,8 @@ class DBRegion_TestClass < TestBase assert_equal(t.to_s, "(100,200;100,200)") assert_equal(t.is_deep?, true) + dss._destroy + end # regions from Shapes @@ -1522,6 +1526,8 @@ class DBRegion_TestClass < TestBase assert_equal(r.to_s, "(0,0;0,1000;1000,1000;1000,0);(1000,1000;1000,2000;2000,2000;2000,1000)") assert_equal(r.bbox.to_s, "(0,0;2000,2000)") + dss._destroy + end # properties @@ -1631,6 +1637,8 @@ class DBRegion_TestClass < TestBase rr.filter(RBA::PolygonFilter::property_filter("one", 17)) assert_equal(csort(rr.to_s), csort("(1,1;1,201;101,201;101,1){one=>17}")) + dss._destroy + end end diff --git a/testdata/ruby/dbTextsTest.rb b/testdata/ruby/dbTextsTest.rb index bc7b70875..5ed722793 100644 --- a/testdata/ruby/dbTextsTest.rb +++ b/testdata/ruby/dbTextsTest.rb @@ -242,6 +242,8 @@ class DBTexts_TestClass < TestBase assert_equal(r.is_deep?, true) + dss._destroy + end def test_4 @@ -342,6 +344,8 @@ class DBTexts_TestClass < TestBase assert_equal(RBA::Texts::new(target.cell("C2").shapes(target_li)).to_s, "('abc',r0 100,-200)") assert_equal(RBA::Region::new(target.cell("C2").shapes(target_li)).to_s, "") + dss._destroy + end # interact @@ -515,6 +519,8 @@ class DBTexts_TestClass < TestBase rr.filter(RBA::TextFilter::property_filter("one", 17)) assert_equal(csort(rr.to_s), csort("('uvw',r0 0,0){one=>17}")) + dss._destroy + end end From c3fe77acd05d14602999f9271ef070fd50d084e1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Feb 2025 20:28:22 +0100 Subject: [PATCH 021/392] Fixing issue #1987 (build failure against Qt6.8) --- scripts/mkqtdecl6/mkqtdecl.conf | 26 +++++++++++ src/gsiqt/qt6/QtCore/gsiDeclQEasingCurve.cc | 49 ++++----------------- src/gsiqt/qt6/QtCore/gsiDeclQTimeZone.cc | 9 ++++ 3 files changed, 44 insertions(+), 40 deletions(-) diff --git a/scripts/mkqtdecl6/mkqtdecl.conf b/scripts/mkqtdecl6/mkqtdecl.conf index ef5ebbcd7..1ec889db0 100644 --- a/scripts/mkqtdecl6/mkqtdecl.conf +++ b/scripts/mkqtdecl6/mkqtdecl.conf @@ -368,6 +368,8 @@ drop_method "QColormap", /QColormap::initialize/ # Must not hide Ruby's initiali drop_method "QAccessible", /QAccessible::initialize/ # Must not hide Ruby's initialize and is not public drop_method "QEasingCurve", /QEasingCurve::setCustomType/ # requires function * drop_method "QEasingCurve", /QEasingCurve::customType/ # requires function * +drop_method "QEasingCurve", /::operator\s*==/ # no longer supported on Qt 6.8 +drop_method "QEasingCurve", /::operator\s*!=/ # no longer supported on Qt 6.8 drop_method "QLibrary", /QLibrary::resolve/ # requires function * drop_method "QLoggingCategory", /QLoggingCategory::installFilter/ # requires function * drop_method "QMetaObject", /QMetaObject::cast\(/ # internal @@ -473,6 +475,30 @@ drop_method "QDebug", /QDebug::operator\s*<<\((?!const\s+QString\s*&)/ # don't m drop_method "", /::operator\s*<<\(QDebug\s*\w*\s*,\s*(?!const\s+QString\s*&)/ # don't map the others right now - too many (TODO: how to map?) drop_method "QNoDebug", /QNoDebug::operator<", "", "", "" ] include "QThread", [ "", "" ] diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQEasingCurve.cc b/src/gsiqt/qt6/QtCore/gsiDeclQEasingCurve.cc index bfad06037..db6218522 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQEasingCurve.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQEasingCurve.cc @@ -35,6 +35,12 @@ // ----------------------------------------------------------------------- // class QEasingCurve + static bool QEasingCurve_operator_eq(const QEasingCurve *a, const QEasingCurve &b) { + return *a == b; + } + static bool QEasingCurve_operator_ne(const QEasingCurve *a, const QEasingCurve &b) { + return !(*a == b); + } // Constructor QEasingCurve::QEasingCurve(QEasingCurve::Type type) @@ -144,25 +150,6 @@ static void _call_f_amplitude_c0 (const qt_gsi::GenericMethod * /*decl*/, void * } -// bool QEasingCurve::operator!=(const QEasingCurve &other) - - -static void _init_f_operator_excl__eq__c2510 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c2510 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QEasingCurve &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QEasingCurve *)cls)->operator!= (arg1)); -} - - // QEasingCurve &QEasingCurve::operator=(const QEasingCurve &other) @@ -182,25 +169,6 @@ static void _call_f_operator_eq__2510 (const qt_gsi::GenericMethod * /*decl*/, v } -// bool QEasingCurve::operator==(const QEasingCurve &other) - - -static void _init_f_operator_eq__eq__c2510 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c2510 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QEasingCurve &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QEasingCurve *)cls)->operator== (arg1)); -} - - // double QEasingCurve::overshoot() @@ -391,9 +359,7 @@ static gsi::Methods methods_QEasingCurve () { methods += new qt_gsi::GenericMethod ("addCubicBezierSegment", "@brief Method void QEasingCurve::addCubicBezierSegment(const QPointF &c1, const QPointF &c2, const QPointF &endPoint)\n", false, &_init_f_addCubicBezierSegment_5742, &_call_f_addCubicBezierSegment_5742); methods += new qt_gsi::GenericMethod ("addTCBSegment", "@brief Method void QEasingCurve::addTCBSegment(const QPointF &nextPoint, double t, double c, double b)\n", false, &_init_f_addTCBSegment_4875, &_call_f_addTCBSegment_4875); methods += new qt_gsi::GenericMethod (":amplitude", "@brief Method double QEasingCurve::amplitude()\n", true, &_init_f_amplitude_c0, &_call_f_amplitude_c0); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QEasingCurve::operator!=(const QEasingCurve &other)\n", true, &_init_f_operator_excl__eq__c2510, &_call_f_operator_excl__eq__c2510); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QEasingCurve &QEasingCurve::operator=(const QEasingCurve &other)\n", false, &_init_f_operator_eq__2510, &_call_f_operator_eq__2510); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QEasingCurve::operator==(const QEasingCurve &other)\n", true, &_init_f_operator_eq__eq__c2510, &_call_f_operator_eq__eq__c2510); methods += new qt_gsi::GenericMethod (":overshoot", "@brief Method double QEasingCurve::overshoot()\n", true, &_init_f_overshoot_c0, &_call_f_overshoot_c0); methods += new qt_gsi::GenericMethod (":period", "@brief Method double QEasingCurve::period()\n", true, &_init_f_period_c0, &_call_f_period_c0); methods += new qt_gsi::GenericMethod ("setAmplitude|amplitude=", "@brief Method void QEasingCurve::setAmplitude(double amplitude)\n", false, &_init_f_setAmplitude_1071, &_call_f_setAmplitude_1071); @@ -408,6 +374,9 @@ static gsi::Methods methods_QEasingCurve () { } gsi::Class decl_QEasingCurve ("QtCore", "QEasingCurve", + gsi::method_ext("==", &QEasingCurve_operator_eq, gsi::arg ("other"), "@brief Method bool QEasingCurve::operator==(const QEasingCurve &) const") + + gsi::method_ext("!=", &QEasingCurve_operator_ne, gsi::arg ("other"), "@brief Method bool QEasingCurve::operator!=(const QEasingCurve &) const") ++ methods_QEasingCurve (), "@qt\n@brief Binding of QEasingCurve"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQTimeZone.cc b/src/gsiqt/qt6/QtCore/gsiDeclQTimeZone.cc index 4e89b5465..d0b2645f9 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQTimeZone.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQTimeZone.cc @@ -36,6 +36,12 @@ // ----------------------------------------------------------------------- // class QTimeZone + static bool QTimeZone_operator_eq(const QTimeZone *a, const QTimeZone &b) { + return *a == b; + } + static bool QTimeZone_operator_ne(const QTimeZone *a, const QTimeZone &b) { + return !(*a == b); + } // Constructor QTimeZone::QTimeZone() @@ -776,6 +782,9 @@ static gsi::Methods methods_QTimeZone () { } gsi::Class decl_QTimeZone ("QtCore", "QTimeZone", + gsi::method_ext("==", &QTimeZone_operator_eq, gsi::arg ("other"), "@brief Method bool QTimeZone::operator==(const QTimeZone &) const") + + gsi::method_ext("!=", &QTimeZone_operator_ne, gsi::arg ("other"), "@brief Method bool QTimeZone::operator!=(const QTimeZone &) const") ++ methods_QTimeZone (), "@qt\n@brief Binding of QTimeZone"); From f6616dfaf4b8ba67145027acd9578600f01dcca3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Feb 2025 21:49:05 +0100 Subject: [PATCH 022/392] WIP: more fixes for Qt 6.8 compatibility --- scripts/mkqtdecl6/mkqtdecl.conf | 70 +++++++++------ src/gsiqt/qt6/QtCore/gsiDeclQDir.cc | 49 ++-------- src/gsiqt/qt6/QtCore/gsiDeclQFileInfo.cc | 49 ++-------- .../qt6/QtCore/gsiDeclQItemSelectionRange.cc | 49 ++-------- src/gsiqt/qt6/QtCore/gsiDeclQJsonArray.cc | 49 ++-------- .../qt6/QtCore/gsiDeclQJsonArray_Iterator.cc | 49 ++-------- src/gsiqt/qt6/QtCore/gsiDeclQJsonDocument.cc | 49 ++-------- src/gsiqt/qt6/QtCore/gsiDeclQJsonObject.cc | 49 ++-------- .../qt6/QtCore/gsiDeclQJsonObject_Iterator.cc | 49 ++-------- src/gsiqt/qt6/QtCore/gsiDeclQJsonValue.cc | 49 ++-------- src/gsiqt/qt6/QtCore/gsiDeclQJsonValueRef.cc | 49 ++-------- src/gsiqt/qt6/QtCore/gsiDeclQLine.cc | 49 ++-------- src/gsiqt/qt6/QtCore/gsiDeclQLineF.cc | 49 ++-------- src/gsiqt/qt6/QtCore/gsiDeclQMimeType.cc | 49 ++-------- src/gsiqt/qt6/QtCore/gsiDeclQModelIndex.cc | 49 ++-------- .../QtCore/gsiDeclQPersistentModelIndex.cc | 89 ++----------------- .../qt6/QtCore/gsiDeclQProcessEnvironment.cc | 49 ++-------- .../qt6/QtCore/gsiDeclQRegularExpression.cc | 49 ++-------- src/gsiqt/qt6/QtCore/gsiDeclQUrl.cc | 49 ++-------- src/gsiqt/qt6/QtCore/gsiDeclQUrlQuery.cc | 49 ++-------- .../qt6/QtCore/gsiDeclQXmlStreamAttribute.cc | 49 ++-------- .../gsiDeclQXmlStreamEntityDeclaration.cc | 49 ++-------- .../gsiDeclQXmlStreamNamespaceDeclaration.cc | 49 ++-------- .../gsiDeclQXmlStreamNotationDeclaration.cc | 49 ++-------- 24 files changed, 250 insertions(+), 987 deletions(-) diff --git a/scripts/mkqtdecl6/mkqtdecl.conf b/scripts/mkqtdecl6/mkqtdecl.conf index 1ec889db0..37672b679 100644 --- a/scripts/mkqtdecl6/mkqtdecl.conf +++ b/scripts/mkqtdecl6/mkqtdecl.conf @@ -18,6 +18,23 @@ load(File.join(File.dirname(__FILE__), "common.conf")) +def add_native_operator_neq(engine, cls) + cls_id = cls.gsub("::", "_") + engine.drop_method cls, /::operator\s*==/ + engine.drop_method cls, /::operator\s*!=/ + engine.add_native_impl(cls, <<"CODE", <<"DECL") + static bool #{cls_id}_operator_eq(const #{cls} *a, const #{cls} &b) { + return *a == b; + } + static bool #{cls_id}_operator_ne(const #{cls} *a, const #{cls} &b) { + return !(*a == b); + } +CODE + gsi::method_ext("==", &#{cls_id}_operator_eq, gsi::arg ("other"), "@brief Method bool #{cls}::operator==(const #{cls} &) const") + + gsi::method_ext("!=", &#{cls_id}_operator_ne, gsi::arg ("other"), "@brief Method bool #{cls}::operator!=(const #{cls} &) const") +DECL +end + # -------------------------------------------------------------- # all modules @@ -368,8 +385,6 @@ drop_method "QColormap", /QColormap::initialize/ # Must not hide Ruby's initiali drop_method "QAccessible", /QAccessible::initialize/ # Must not hide Ruby's initialize and is not public drop_method "QEasingCurve", /QEasingCurve::setCustomType/ # requires function * drop_method "QEasingCurve", /QEasingCurve::customType/ # requires function * -drop_method "QEasingCurve", /::operator\s*==/ # no longer supported on Qt 6.8 -drop_method "QEasingCurve", /::operator\s*!=/ # no longer supported on Qt 6.8 drop_method "QLibrary", /QLibrary::resolve/ # requires function * drop_method "QLoggingCategory", /QLoggingCategory::installFilter/ # requires function * drop_method "QMetaObject", /QMetaObject::cast\(/ # internal @@ -462,8 +477,6 @@ drop_method "QTextCodec", /QTextCodec::codecForName\(const\s+QByteArray/ # clash drop_method "QTextCodec", /QTextCodec::toUnicode\(const\s+QByteArray/ # clashes with const char * variant drop_method "QTextCodec", /QTextCodec::fromUnicode\(const\s+QChar\s+\*/ # requires real QChar * drop_method "QTextEncoder", /QTextEncoder::fromUnicode\(const\s+QChar\s+\*/ # requires real QChar * -drop_method "QTimeZone", /::operator\s*==/ # no longer supported on Qt 6.7 -drop_method "QTimeZone", /::operator\s*!=/ # no longer supported on Qt 6.7 drop_method "", /::operator\s*==\(const\s+QVariant\s*&\w+,\s*const\s+QVariantComparisonHelper/ # requires QVariantComparisonHelper drop_method "", /::operator\s*!=\(const\s+QVariant\s*&\w+,\s*const\s+QVariantComparisonHelper/ # requires QVariantComparisonHelper drop_method "QByteArrayMatcher", /QByteArrayMatcher::indexIn\(const\s+QByteArray/ # clashes with const char * variant @@ -475,29 +488,32 @@ drop_method "QDebug", /QDebug::operator\s*<<\((?!const\s+QString\s*&)/ # don't m drop_method "", /::operator\s*<<\(QDebug\s*\w*\s*,\s*(?!const\s+QString\s*&)/ # don't map the others right now - too many (TODO: how to map?) drop_method "QNoDebug", /QNoDebug::operator<", "", "", "" ] include "QThread", [ "", "" ] diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQDir.cc b/src/gsiqt/qt6/QtCore/gsiDeclQDir.cc index 701ccffe4..cc0a01ddf 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQDir.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQDir.cc @@ -35,6 +35,12 @@ // ----------------------------------------------------------------------- // class QDir + static bool QDir_operator_eq(const QDir *a, const QDir &b) { + return *a == b; + } + static bool QDir_operator_ne(const QDir *a, const QDir &b) { + return !(*a == b); + } // Constructor QDir::QDir(const QDir &) @@ -524,25 +530,6 @@ static void _call_f_nameFilters_c0 (const qt_gsi::GenericMethod * /*decl*/, void } -// bool QDir::operator!=(const QDir &dir) - - -static void _init_f_operator_excl__eq__c1681 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("dir"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c1681 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QDir &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QDir *)cls)->operator!= (arg1)); -} - - // QDir &QDir::operator=(const QDir &) @@ -562,25 +549,6 @@ static void _call_f_operator_eq__1681 (const qt_gsi::GenericMethod * /*decl*/, v } -// bool QDir::operator==(const QDir &dir) - - -static void _init_f_operator_eq__eq__c1681 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("dir"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c1681 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QDir &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QDir *)cls)->operator== (arg1)); -} - - // QString QDir::operator[](int) @@ -1299,9 +1267,7 @@ static gsi::Methods methods_QDir () { methods += new qt_gsi::GenericMethod ("mkdir", "@brief Method bool QDir::mkdir(const QString &dirName)\n", true, &_init_f_mkdir_c2025, &_call_f_mkdir_c2025); methods += new qt_gsi::GenericMethod ("mkpath", "@brief Method bool QDir::mkpath(const QString &dirPath)\n", true, &_init_f_mkpath_c2025, &_call_f_mkpath_c2025); methods += new qt_gsi::GenericMethod (":nameFilters", "@brief Method QStringList QDir::nameFilters()\n", true, &_init_f_nameFilters_c0, &_call_f_nameFilters_c0); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QDir::operator!=(const QDir &dir)\n", true, &_init_f_operator_excl__eq__c1681, &_call_f_operator_excl__eq__c1681); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QDir &QDir::operator=(const QDir &)\n", false, &_init_f_operator_eq__1681, &_call_f_operator_eq__1681); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QDir::operator==(const QDir &dir)\n", true, &_init_f_operator_eq__eq__c1681, &_call_f_operator_eq__eq__c1681); methods += new qt_gsi::GenericMethod ("[]", "@brief Method QString QDir::operator[](int)\n", true, &_init_f_operator_index__c767, &_call_f_operator_index__c767); methods += new qt_gsi::GenericMethod (":path", "@brief Method QString QDir::path()\n", true, &_init_f_path_c0, &_call_f_path_c0); methods += new qt_gsi::GenericMethod ("refresh", "@brief Method void QDir::refresh()\n", true, &_init_f_refresh_c0, &_call_f_refresh_c0); @@ -1344,6 +1310,9 @@ static gsi::Methods methods_QDir () { } gsi::Class decl_QDir ("QtCore", "QDir", + gsi::method_ext("==", &QDir_operator_eq, gsi::arg ("other"), "@brief Method bool QDir::operator==(const QDir &) const") + + gsi::method_ext("!=", &QDir_operator_ne, gsi::arg ("other"), "@brief Method bool QDir::operator!=(const QDir &) const") ++ methods_QDir (), "@qt\n@brief Binding of QDir"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQFileInfo.cc b/src/gsiqt/qt6/QtCore/gsiDeclQFileInfo.cc index 7b7e5c6b9..8624b0a19 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQFileInfo.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQFileInfo.cc @@ -37,6 +37,12 @@ // ----------------------------------------------------------------------- // class QFileInfo + static bool QFileInfo_operator_eq(const QFileInfo *a, const QFileInfo &b) { + return *a == b; + } + static bool QFileInfo_operator_ne(const QFileInfo *a, const QFileInfo &b) { + return !(*a == b); + } // Constructor QFileInfo::QFileInfo() @@ -706,25 +712,6 @@ static void _call_f_metadataChangeTime_c0 (const qt_gsi::GenericMethod * /*decl* } -// bool QFileInfo::operator!=(const QFileInfo &fileinfo) - - -static void _init_f_operator_excl__eq__c2174 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("fileinfo"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c2174 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QFileInfo &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QFileInfo *)cls)->operator!= (arg1)); -} - - // QFileInfo &QFileInfo::operator=(const QFileInfo &fileinfo) @@ -744,25 +731,6 @@ static void _call_f_operator_eq__2174 (const qt_gsi::GenericMethod * /*decl*/, v } -// bool QFileInfo::operator==(const QFileInfo &fileinfo) - - -static void _init_f_operator_eq__eq__c2174 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("fileinfo"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c2174 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QFileInfo &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QFileInfo *)cls)->operator== (arg1)); -} - - // QString QFileInfo::owner() @@ -1090,9 +1058,7 @@ static gsi::Methods methods_QFileInfo () { methods += new qt_gsi::GenericMethod ("lastRead", "@brief Method QDateTime QFileInfo::lastRead()\n", true, &_init_f_lastRead_c0, &_call_f_lastRead_c0); methods += new qt_gsi::GenericMethod ("makeAbsolute", "@brief Method bool QFileInfo::makeAbsolute()\n", false, &_init_f_makeAbsolute_0, &_call_f_makeAbsolute_0); methods += new qt_gsi::GenericMethod ("metadataChangeTime", "@brief Method QDateTime QFileInfo::metadataChangeTime()\n", true, &_init_f_metadataChangeTime_c0, &_call_f_metadataChangeTime_c0); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QFileInfo::operator!=(const QFileInfo &fileinfo)\n", true, &_init_f_operator_excl__eq__c2174, &_call_f_operator_excl__eq__c2174); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QFileInfo &QFileInfo::operator=(const QFileInfo &fileinfo)\n", false, &_init_f_operator_eq__2174, &_call_f_operator_eq__2174); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QFileInfo::operator==(const QFileInfo &fileinfo)\n", true, &_init_f_operator_eq__eq__c2174, &_call_f_operator_eq__eq__c2174); methods += new qt_gsi::GenericMethod ("owner", "@brief Method QString QFileInfo::owner()\n", true, &_init_f_owner_c0, &_call_f_owner_c0); methods += new qt_gsi::GenericMethod ("ownerId", "@brief Method unsigned int QFileInfo::ownerId()\n", true, &_init_f_ownerId_c0, &_call_f_ownerId_c0); methods += new qt_gsi::GenericMethod ("path", "@brief Method QString QFileInfo::path()\n", true, &_init_f_path_c0, &_call_f_path_c0); @@ -1113,6 +1079,9 @@ static gsi::Methods methods_QFileInfo () { } gsi::Class decl_QFileInfo ("QtCore", "QFileInfo", + gsi::method_ext("==", &QFileInfo_operator_eq, gsi::arg ("other"), "@brief Method bool QFileInfo::operator==(const QFileInfo &) const") + + gsi::method_ext("!=", &QFileInfo_operator_ne, gsi::arg ("other"), "@brief Method bool QFileInfo::operator!=(const QFileInfo &) const") ++ methods_QFileInfo (), "@qt\n@brief Binding of QFileInfo"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQItemSelectionRange.cc b/src/gsiqt/qt6/QtCore/gsiDeclQItemSelectionRange.cc index 6b1541529..121d2a25e 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQItemSelectionRange.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQItemSelectionRange.cc @@ -37,6 +37,12 @@ // ----------------------------------------------------------------------- // class QItemSelectionRange + static bool QItemSelectionRange_operator_eq(const QItemSelectionRange *a, const QItemSelectionRange &b) { + return *a == b; + } + static bool QItemSelectionRange_operator_ne(const QItemSelectionRange *a, const QItemSelectionRange &b) { + return !(*a == b); + } // Constructor QItemSelectionRange::QItemSelectionRange() @@ -296,44 +302,6 @@ static void _call_f_model_c0 (const qt_gsi::GenericMethod * /*decl*/, void *cls, } -// bool QItemSelectionRange::operator!=(const QItemSelectionRange &other) - - -static void _init_f_operator_excl__eq__c3220 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c3220 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QItemSelectionRange &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QItemSelectionRange *)cls)->operator!= (arg1)); -} - - -// bool QItemSelectionRange::operator==(const QItemSelectionRange &other) - - -static void _init_f_operator_eq__eq__c3220 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c3220 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QItemSelectionRange &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QItemSelectionRange *)cls)->operator== (arg1)); -} - - // QModelIndex QItemSelectionRange::parent() @@ -450,8 +418,6 @@ static gsi::Methods methods_QItemSelectionRange () { methods += new qt_gsi::GenericMethod ("isValid?", "@brief Method bool QItemSelectionRange::isValid()\n", true, &_init_f_isValid_c0, &_call_f_isValid_c0); methods += new qt_gsi::GenericMethod ("left", "@brief Method int QItemSelectionRange::left()\n", true, &_init_f_left_c0, &_call_f_left_c0); methods += new qt_gsi::GenericMethod ("model", "@brief Method const QAbstractItemModel *QItemSelectionRange::model()\n", true, &_init_f_model_c0, &_call_f_model_c0); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QItemSelectionRange::operator!=(const QItemSelectionRange &other)\n", true, &_init_f_operator_excl__eq__c3220, &_call_f_operator_excl__eq__c3220); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QItemSelectionRange::operator==(const QItemSelectionRange &other)\n", true, &_init_f_operator_eq__eq__c3220, &_call_f_operator_eq__eq__c3220); methods += new qt_gsi::GenericMethod ("parent", "@brief Method QModelIndex QItemSelectionRange::parent()\n", true, &_init_f_parent_c0, &_call_f_parent_c0); methods += new qt_gsi::GenericMethod ("right", "@brief Method int QItemSelectionRange::right()\n", true, &_init_f_right_c0, &_call_f_right_c0); methods += new qt_gsi::GenericMethod ("swap", "@brief Method void QItemSelectionRange::swap(QItemSelectionRange &other)\n", false, &_init_f_swap_2525, &_call_f_swap_2525); @@ -462,6 +428,9 @@ static gsi::Methods methods_QItemSelectionRange () { } gsi::Class decl_QItemSelectionRange ("QtCore", "QItemSelectionRange", + gsi::method_ext("==", &QItemSelectionRange_operator_eq, gsi::arg ("other"), "@brief Method bool QItemSelectionRange::operator==(const QItemSelectionRange &) const") + + gsi::method_ext("!=", &QItemSelectionRange_operator_ne, gsi::arg ("other"), "@brief Method bool QItemSelectionRange::operator!=(const QItemSelectionRange &) const") ++ methods_QItemSelectionRange (), "@qt\n@brief Binding of QItemSelectionRange"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQJsonArray.cc b/src/gsiqt/qt6/QtCore/gsiDeclQJsonArray.cc index f68234648..01bd46047 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQJsonArray.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQJsonArray.cc @@ -36,6 +36,12 @@ // ----------------------------------------------------------------------- // class QJsonArray + static bool QJsonArray_operator_eq(const QJsonArray *a, const QJsonArray &b) { + return *a == b; + } + static bool QJsonArray_operator_ne(const QJsonArray *a, const QJsonArray &b) { + return !(*a == b); + } // Constructor QJsonArray::QJsonArray() @@ -317,25 +323,6 @@ static void _call_f_operator_eq__2315 (const qt_gsi::GenericMethod * /*decl*/, v } -// bool QJsonArray::operator!=(const QJsonArray &other) - - -static void _init_f_operator_excl__eq__c2315 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c2315 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonArray &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonArray *)cls)->operator!= (arg1)); -} - - // QJsonArray QJsonArray::operator+(const QJsonValue &v) @@ -393,25 +380,6 @@ static void _call_f_operator_lt__lt__2313 (const qt_gsi::GenericMethod * /*decl* } -// bool QJsonArray::operator==(const QJsonArray &other) - - -static void _init_f_operator_eq__eq__c2315 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c2315 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonArray &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonArray *)cls)->operator== (arg1)); -} - - // QJsonValueRef QJsonArray::operator[](qsizetype i) @@ -727,11 +695,9 @@ static gsi::Methods methods_QJsonArray () { methods += new qt_gsi::GenericMethod ("isEmpty?", "@brief Method bool QJsonArray::isEmpty()\n", true, &_init_f_isEmpty_c0, &_call_f_isEmpty_c0); methods += new qt_gsi::GenericMethod ("last", "@brief Method QJsonValue QJsonArray::last()\n", true, &_init_f_last_c0, &_call_f_last_c0); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QJsonArray &QJsonArray::operator =(const QJsonArray &other)\n", false, &_init_f_operator_eq__2315, &_call_f_operator_eq__2315); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QJsonArray::operator!=(const QJsonArray &other)\n", true, &_init_f_operator_excl__eq__c2315, &_call_f_operator_excl__eq__c2315); methods += new qt_gsi::GenericMethod ("+", "@brief Method QJsonArray QJsonArray::operator+(const QJsonValue &v)\n", true, &_init_f_operator_plus__c2313, &_call_f_operator_plus__c2313); methods += new qt_gsi::GenericMethod ("+=", "@brief Method QJsonArray &QJsonArray::operator+=(const QJsonValue &v)\n", false, &_init_f_operator_plus__eq__2313, &_call_f_operator_plus__eq__2313); methods += new qt_gsi::GenericMethod ("<<", "@brief Method QJsonArray &QJsonArray::operator<<(const QJsonValue &v)\n", false, &_init_f_operator_lt__lt__2313, &_call_f_operator_lt__lt__2313); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QJsonArray::operator==(const QJsonArray &other)\n", true, &_init_f_operator_eq__eq__c2315, &_call_f_operator_eq__eq__c2315); methods += new qt_gsi::GenericMethod ("[]", "@brief Method QJsonValueRef QJsonArray::operator[](qsizetype i)\n", false, &_init_f_operator_index__1442, &_call_f_operator_index__1442); methods += new qt_gsi::GenericMethod ("pop_back", "@brief Method void QJsonArray::pop_back()\n", false, &_init_f_pop_back_0, &_call_f_pop_back_0); methods += new qt_gsi::GenericMethod ("pop_front", "@brief Method void QJsonArray::pop_front()\n", false, &_init_f_pop_front_0, &_call_f_pop_front_0); @@ -752,6 +718,9 @@ static gsi::Methods methods_QJsonArray () { } gsi::Class decl_QJsonArray ("QtCore", "QJsonArray", + gsi::method_ext("==", &QJsonArray_operator_eq, gsi::arg ("other"), "@brief Method bool QJsonArray::operator==(const QJsonArray &) const") + + gsi::method_ext("!=", &QJsonArray_operator_ne, gsi::arg ("other"), "@brief Method bool QJsonArray::operator!=(const QJsonArray &) const") ++ methods_QJsonArray (), "@qt\n@brief Binding of QJsonArray"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQJsonArray_Iterator.cc b/src/gsiqt/qt6/QtCore/gsiDeclQJsonArray_Iterator.cc index f4e50f553..f164892b4 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQJsonArray_Iterator.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQJsonArray_Iterator.cc @@ -36,6 +36,12 @@ // ----------------------------------------------------------------------- // class QJsonArray::iterator + static bool QJsonArray_iterator_operator_eq(const QJsonArray::iterator *a, const QJsonArray::iterator &b) { + return *a == b; + } + static bool QJsonArray_iterator_operator_ne(const QJsonArray::iterator *a, const QJsonArray::iterator &b) { + return !(*a == b); + } // Constructor QJsonArray::iterator::iterator() @@ -93,25 +99,6 @@ static void _call_ctor_QJsonArray_Iterator_3305 (const qt_gsi::GenericStaticMeth } -// bool QJsonArray::iterator::operator!=(const QJsonArray::iterator &o) - - -static void _init_f_operator_excl__eq__c3305 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("o"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c3305 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonArray::iterator &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonArray::iterator *)cls)->operator!= (arg1)); -} - - // QJsonValueRef QJsonArray::iterator::operator*() @@ -362,25 +349,6 @@ static void _call_f_operator_eq__3305 (const qt_gsi::GenericMethod * /*decl*/, v } -// bool QJsonArray::iterator::operator==(const QJsonArray::iterator &o) - - -static void _init_f_operator_eq__eq__c3305 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("o"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c3305 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonArray::iterator &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonArray::iterator *)cls)->operator== (arg1)); -} - - // bool QJsonArray::iterator::operator>(const QJsonArray::iterator &other) @@ -447,7 +415,6 @@ static gsi::Methods methods_QJsonArray_Iterator () { methods += new qt_gsi::GenericStaticMethod ("new", "@brief Constructor QJsonArray::iterator::iterator()\nThis method creates an object of class QJsonArray::iterator.", &_init_ctor_QJsonArray_Iterator_0, &_call_ctor_QJsonArray_Iterator_0); methods += new qt_gsi::GenericStaticMethod ("new", "@brief Constructor QJsonArray::iterator::iterator(QJsonArray *array, qsizetype index)\nThis method creates an object of class QJsonArray::iterator.", &_init_ctor_QJsonArray_Iterator_2958, &_call_ctor_QJsonArray_Iterator_2958); methods += new qt_gsi::GenericStaticMethod ("new", "@brief Constructor QJsonArray::iterator::iterator(const QJsonArray::iterator &other)\nThis method creates an object of class QJsonArray::iterator.", &_init_ctor_QJsonArray_Iterator_3305, &_call_ctor_QJsonArray_Iterator_3305); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QJsonArray::iterator::operator!=(const QJsonArray::iterator &o)\n", true, &_init_f_operator_excl__eq__c3305, &_call_f_operator_excl__eq__c3305); methods += new qt_gsi::GenericMethod ("*", "@brief Method QJsonValueRef QJsonArray::iterator::operator*()\n", true, &_init_f_operator_star__c0, &_call_f_operator_star__c0); methods += new qt_gsi::GenericMethod ("+", "@brief Method QJsonArray::iterator QJsonArray::iterator::operator+(qsizetype j)\n", true, &_init_f_operator_plus__c1442, &_call_f_operator_plus__c1442); methods += new qt_gsi::GenericMethod ("++", "@brief Method QJsonArray::iterator &QJsonArray::iterator::operator++()\n", false, &_init_f_operator_plus__plus__0, &_call_f_operator_plus__plus__0); @@ -462,7 +429,6 @@ static gsi::Methods methods_QJsonArray_Iterator () { methods += new qt_gsi::GenericMethod ("<", "@brief Method bool QJsonArray::iterator::operator<(const QJsonArray::iterator &other)\n", true, &_init_f_operator_lt__c3305, &_call_f_operator_lt__c3305); methods += new qt_gsi::GenericMethod ("<=", "@brief Method bool QJsonArray::iterator::operator<=(const QJsonArray::iterator &other)\n", true, &_init_f_operator_lt__eq__c3305, &_call_f_operator_lt__eq__c3305); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QJsonArray::iterator &QJsonArray::iterator::operator=(const QJsonArray::iterator &other)\n", false, &_init_f_operator_eq__3305, &_call_f_operator_eq__3305); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QJsonArray::iterator::operator==(const QJsonArray::iterator &o)\n", true, &_init_f_operator_eq__eq__c3305, &_call_f_operator_eq__eq__c3305); methods += new qt_gsi::GenericMethod (">", "@brief Method bool QJsonArray::iterator::operator>(const QJsonArray::iterator &other)\n", true, &_init_f_operator_gt__c3305, &_call_f_operator_gt__c3305); methods += new qt_gsi::GenericMethod (">=", "@brief Method bool QJsonArray::iterator::operator>=(const QJsonArray::iterator &other)\n", true, &_init_f_operator_gt__eq__c3305, &_call_f_operator_gt__eq__c3305); methods += new qt_gsi::GenericMethod ("[]", "@brief Method QJsonValueRef QJsonArray::iterator::operator[](qsizetype j)\n", true, &_init_f_operator_index__c1442, &_call_f_operator_index__c1442); @@ -470,6 +436,9 @@ static gsi::Methods methods_QJsonArray_Iterator () { } gsi::Class decl_QJsonArray_Iterator ("QtCore", "QJsonArray_Iterator", + gsi::method_ext("==", &QJsonArray_iterator_operator_eq, gsi::arg ("other"), "@brief Method bool QJsonArray::iterator::operator==(const QJsonArray::iterator &) const") + + gsi::method_ext("!=", &QJsonArray_iterator_operator_ne, gsi::arg ("other"), "@brief Method bool QJsonArray::iterator::operator!=(const QJsonArray::iterator &) const") ++ methods_QJsonArray_Iterator (), "@qt\n@brief Binding of QJsonArray::iterator"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQJsonDocument.cc b/src/gsiqt/qt6/QtCore/gsiDeclQJsonDocument.cc index 8a1c93d1b..9639f2f45 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQJsonDocument.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQJsonDocument.cc @@ -38,6 +38,12 @@ // ----------------------------------------------------------------------- // class QJsonDocument + static bool QJsonDocument_operator_eq(const QJsonDocument *a, const QJsonDocument &b) { + return *a == b; + } + static bool QJsonDocument_operator_ne(const QJsonDocument *a, const QJsonDocument &b) { + return !(*a == b); + } // Constructor QJsonDocument::QJsonDocument() @@ -220,44 +226,6 @@ static void _call_f_operator_eq__2635 (const qt_gsi::GenericMethod * /*decl*/, v } -// bool QJsonDocument::operator!=(const QJsonDocument &other) - - -static void _init_f_operator_excl__eq__c2635 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c2635 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonDocument &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonDocument *)cls)->operator!= (arg1)); -} - - -// bool QJsonDocument::operator==(const QJsonDocument &other) - - -static void _init_f_operator_eq__eq__c2635 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c2635 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonDocument &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonDocument *)cls)->operator== (arg1)); -} - - // const QJsonValue QJsonDocument::operator[](const QString &key) @@ -448,8 +416,6 @@ static gsi::Methods methods_QJsonDocument () { methods += new qt_gsi::GenericMethod ("isObject?", "@brief Method bool QJsonDocument::isObject()\n", true, &_init_f_isObject_c0, &_call_f_isObject_c0); methods += new qt_gsi::GenericMethod ("object", "@brief Method QJsonObject QJsonDocument::object()\n", true, &_init_f_object_c0, &_call_f_object_c0); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QJsonDocument &QJsonDocument::operator =(const QJsonDocument &other)\n", false, &_init_f_operator_eq__2635, &_call_f_operator_eq__2635); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QJsonDocument::operator!=(const QJsonDocument &other)\n", true, &_init_f_operator_excl__eq__c2635, &_call_f_operator_excl__eq__c2635); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QJsonDocument::operator==(const QJsonDocument &other)\n", true, &_init_f_operator_eq__eq__c2635, &_call_f_operator_eq__eq__c2635); methods += new qt_gsi::GenericMethod ("[]", "@brief Method const QJsonValue QJsonDocument::operator[](const QString &key)\n", true, &_init_f_operator_index__c2025, &_call_f_operator_index__c2025); methods += new qt_gsi::GenericMethod ("[]", "@brief Method const QJsonValue QJsonDocument::operator[](qsizetype i)\n", true, &_init_f_operator_index__c1442, &_call_f_operator_index__c1442); methods += new qt_gsi::GenericMethod ("setArray|array=", "@brief Method void QJsonDocument::setArray(const QJsonArray &array)\n", false, &_init_f_setArray_2315, &_call_f_setArray_2315); @@ -463,6 +429,9 @@ static gsi::Methods methods_QJsonDocument () { } gsi::Class decl_QJsonDocument ("QtCore", "QJsonDocument", + gsi::method_ext("==", &QJsonDocument_operator_eq, gsi::arg ("other"), "@brief Method bool QJsonDocument::operator==(const QJsonDocument &) const") + + gsi::method_ext("!=", &QJsonDocument_operator_ne, gsi::arg ("other"), "@brief Method bool QJsonDocument::operator!=(const QJsonDocument &) const") ++ methods_QJsonDocument (), "@qt\n@brief Binding of QJsonDocument"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQJsonObject.cc b/src/gsiqt/qt6/QtCore/gsiDeclQJsonObject.cc index 4522462f6..9a38d3306 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQJsonObject.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQJsonObject.cc @@ -36,6 +36,12 @@ // ----------------------------------------------------------------------- // class QJsonObject + static bool QJsonObject_operator_eq(const QJsonObject *a, const QJsonObject &b) { + return *a == b; + } + static bool QJsonObject_operator_ne(const QJsonObject *a, const QJsonObject &b) { + return !(*a == b); + } // Constructor QJsonObject::QJsonObject() @@ -274,44 +280,6 @@ static void _call_f_operator_eq__2403 (const qt_gsi::GenericMethod * /*decl*/, v } -// bool QJsonObject::operator!=(const QJsonObject &other) - - -static void _init_f_operator_excl__eq__c2403 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c2403 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonObject &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonObject *)cls)->operator!= (arg1)); -} - - -// bool QJsonObject::operator==(const QJsonObject &other) - - -static void _init_f_operator_eq__eq__c2403 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c2403 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonObject &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonObject *)cls)->operator== (arg1)); -} - - // QJsonValueRef QJsonObject::operator[](const QString &key) @@ -512,8 +480,6 @@ static gsi::Methods methods_QJsonObject () { methods += new qt_gsi::GenericMethod ("keys", "@brief Method QStringList QJsonObject::keys()\n", true, &_init_f_keys_c0, &_call_f_keys_c0); methods += new qt_gsi::GenericMethod ("length", "@brief Method qsizetype QJsonObject::length()\n", true, &_init_f_length_c0, &_call_f_length_c0); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QJsonObject &QJsonObject::operator =(const QJsonObject &other)\n", false, &_init_f_operator_eq__2403, &_call_f_operator_eq__2403); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QJsonObject::operator!=(const QJsonObject &other)\n", true, &_init_f_operator_excl__eq__c2403, &_call_f_operator_excl__eq__c2403); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QJsonObject::operator==(const QJsonObject &other)\n", true, &_init_f_operator_eq__eq__c2403, &_call_f_operator_eq__eq__c2403); methods += new qt_gsi::GenericMethod ("[]", "@brief Method QJsonValueRef QJsonObject::operator[](const QString &key)\n", false, &_init_f_operator_index__2025, &_call_f_operator_index__2025); methods += new qt_gsi::GenericMethod ("remove", "@brief Method void QJsonObject::remove(const QString &key)\n", false, &_init_f_remove_2025, &_call_f_remove_2025); methods += new qt_gsi::GenericMethod ("size", "@brief Method qsizetype QJsonObject::size()\n", true, &_init_f_size_c0, &_call_f_size_c0); @@ -528,6 +494,9 @@ static gsi::Methods methods_QJsonObject () { } gsi::Class decl_QJsonObject ("QtCore", "QJsonObject", + gsi::method_ext("==", &QJsonObject_operator_eq, gsi::arg ("other"), "@brief Method bool QJsonObject::operator==(const QJsonObject &) const") + + gsi::method_ext("!=", &QJsonObject_operator_ne, gsi::arg ("other"), "@brief Method bool QJsonObject::operator!=(const QJsonObject &) const") ++ methods_QJsonObject (), "@qt\n@brief Binding of QJsonObject"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQJsonObject_Iterator.cc b/src/gsiqt/qt6/QtCore/gsiDeclQJsonObject_Iterator.cc index 9579197ae..83388aca0 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQJsonObject_Iterator.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQJsonObject_Iterator.cc @@ -36,6 +36,12 @@ // ----------------------------------------------------------------------- // class QJsonObject::iterator + static bool QJsonObject_iterator_operator_eq(const QJsonObject::iterator *a, const QJsonObject::iterator &b) { + return *a == b; + } + static bool QJsonObject_iterator_operator_ne(const QJsonObject::iterator *a, const QJsonObject::iterator &b) { + return !(*a == b); + } // Constructor QJsonObject::iterator::iterator() @@ -108,25 +114,6 @@ static void _call_f_key_c0 (const qt_gsi::GenericMethod * /*decl*/, void *cls, g } -// bool QJsonObject::iterator::operator!=(const QJsonObject::iterator &other) - - -static void _init_f_operator_excl__eq__c3393 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c3393 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonObject::iterator &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonObject::iterator *)cls)->operator!= (arg1)); -} - - // QJsonValueRef QJsonObject::iterator::operator*() @@ -377,25 +364,6 @@ static void _call_f_operator_eq__3393 (const qt_gsi::GenericMethod * /*decl*/, v } -// bool QJsonObject::iterator::operator==(const QJsonObject::iterator &other) - - -static void _init_f_operator_eq__eq__c3393 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c3393 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonObject::iterator &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonObject::iterator *)cls)->operator== (arg1)); -} - - // bool QJsonObject::iterator::operator>(const QJsonObject::iterator &other) @@ -478,7 +446,6 @@ static gsi::Methods methods_QJsonObject_Iterator () { methods += new qt_gsi::GenericStaticMethod ("new", "@brief Constructor QJsonObject::iterator::iterator(QJsonObject *obj, qsizetype index)\nThis method creates an object of class QJsonObject::iterator.", &_init_ctor_QJsonObject_Iterator_3046, &_call_ctor_QJsonObject_Iterator_3046); methods += new qt_gsi::GenericStaticMethod ("new", "@brief Constructor QJsonObject::iterator::iterator(const QJsonObject::iterator &other)\nThis method creates an object of class QJsonObject::iterator.", &_init_ctor_QJsonObject_Iterator_3393, &_call_ctor_QJsonObject_Iterator_3393); methods += new qt_gsi::GenericMethod ("key", "@brief Method QString QJsonObject::iterator::key()\n", true, &_init_f_key_c0, &_call_f_key_c0); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QJsonObject::iterator::operator!=(const QJsonObject::iterator &other)\n", true, &_init_f_operator_excl__eq__c3393, &_call_f_operator_excl__eq__c3393); methods += new qt_gsi::GenericMethod ("*", "@brief Method QJsonValueRef QJsonObject::iterator::operator*()\n", true, &_init_f_operator_star__c0, &_call_f_operator_star__c0); methods += new qt_gsi::GenericMethod ("+", "@brief Method QJsonObject::iterator QJsonObject::iterator::operator+(qsizetype j)\n", true, &_init_f_operator_plus__c1442, &_call_f_operator_plus__c1442); methods += new qt_gsi::GenericMethod ("++", "@brief Method QJsonObject::iterator &QJsonObject::iterator::operator++()\n", false, &_init_f_operator_plus__plus__0, &_call_f_operator_plus__plus__0); @@ -493,7 +460,6 @@ static gsi::Methods methods_QJsonObject_Iterator () { methods += new qt_gsi::GenericMethod ("<", "@brief Method bool QJsonObject::iterator::operator<(const QJsonObject::iterator &other)\n", true, &_init_f_operator_lt__c3393, &_call_f_operator_lt__c3393); methods += new qt_gsi::GenericMethod ("<=", "@brief Method bool QJsonObject::iterator::operator<=(const QJsonObject::iterator &other)\n", true, &_init_f_operator_lt__eq__c3393, &_call_f_operator_lt__eq__c3393); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QJsonObject::iterator &QJsonObject::iterator::operator=(const QJsonObject::iterator &other)\n", false, &_init_f_operator_eq__3393, &_call_f_operator_eq__3393); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QJsonObject::iterator::operator==(const QJsonObject::iterator &other)\n", true, &_init_f_operator_eq__eq__c3393, &_call_f_operator_eq__eq__c3393); methods += new qt_gsi::GenericMethod (">", "@brief Method bool QJsonObject::iterator::operator>(const QJsonObject::iterator &other)\n", true, &_init_f_operator_gt__c3393, &_call_f_operator_gt__c3393); methods += new qt_gsi::GenericMethod (">=", "@brief Method bool QJsonObject::iterator::operator>=(const QJsonObject::iterator &other)\n", true, &_init_f_operator_gt__eq__c3393, &_call_f_operator_gt__eq__c3393); methods += new qt_gsi::GenericMethod ("[]", "@brief Method const QJsonValueRef QJsonObject::iterator::operator[](qsizetype j)\n", false, &_init_f_operator_index__1442, &_call_f_operator_index__1442); @@ -502,6 +468,9 @@ static gsi::Methods methods_QJsonObject_Iterator () { } gsi::Class decl_QJsonObject_Iterator ("QtCore", "QJsonObject_Iterator", + gsi::method_ext("==", &QJsonObject_iterator_operator_eq, gsi::arg ("other"), "@brief Method bool QJsonObject::iterator::operator==(const QJsonObject::iterator &) const") + + gsi::method_ext("!=", &QJsonObject_iterator_operator_ne, gsi::arg ("other"), "@brief Method bool QJsonObject::iterator::operator!=(const QJsonObject::iterator &) const") ++ methods_QJsonObject_Iterator (), "@qt\n@brief Binding of QJsonObject::iterator"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQJsonValue.cc b/src/gsiqt/qt6/QtCore/gsiDeclQJsonValue.cc index 8b2c09647..77aa26ee5 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQJsonValue.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQJsonValue.cc @@ -36,6 +36,12 @@ // ----------------------------------------------------------------------- // class QJsonValue + static bool QJsonValue_operator_eq(const QJsonValue *a, const QJsonValue &b) { + return *a == b; + } + static bool QJsonValue_operator_ne(const QJsonValue *a, const QJsonValue &b) { + return !(*a == b); + } // Constructor QJsonValue::QJsonValue(QJsonValue::Type) @@ -313,44 +319,6 @@ static void _call_f_operator_eq__2313 (const qt_gsi::GenericMethod * /*decl*/, v } -// bool QJsonValue::operator!=(const QJsonValue &other) - - -static void _init_f_operator_excl__eq__c2313 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c2313 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonValue &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonValue *)cls)->operator!= (arg1)); -} - - -// bool QJsonValue::operator==(const QJsonValue &other) - - -static void _init_f_operator_eq__eq__c2313 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c2313 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonValue &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonValue *)cls)->operator== (arg1)); -} - - // const QJsonValue QJsonValue::operator[](const QString &key) @@ -658,8 +626,6 @@ static gsi::Methods methods_QJsonValue () { methods += new qt_gsi::GenericMethod ("isString?", "@brief Method bool QJsonValue::isString()\n", true, &_init_f_isString_c0, &_call_f_isString_c0); methods += new qt_gsi::GenericMethod ("isUndefined?", "@brief Method bool QJsonValue::isUndefined()\n", true, &_init_f_isUndefined_c0, &_call_f_isUndefined_c0); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QJsonValue &QJsonValue::operator =(const QJsonValue &other)\n", false, &_init_f_operator_eq__2313, &_call_f_operator_eq__2313); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QJsonValue::operator!=(const QJsonValue &other)\n", true, &_init_f_operator_excl__eq__c2313, &_call_f_operator_excl__eq__c2313); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QJsonValue::operator==(const QJsonValue &other)\n", true, &_init_f_operator_eq__eq__c2313, &_call_f_operator_eq__eq__c2313); methods += new qt_gsi::GenericMethod ("[]", "@brief Method const QJsonValue QJsonValue::operator[](const QString &key)\n", true, &_init_f_operator_index__c2025, &_call_f_operator_index__c2025); methods += new qt_gsi::GenericMethod ("[]", "@brief Method const QJsonValue QJsonValue::operator[](qsizetype i)\n", true, &_init_f_operator_index__c1442, &_call_f_operator_index__c1442); methods += new qt_gsi::GenericMethod ("swap", "@brief Method void QJsonValue::swap(QJsonValue &other)\n", false, &_init_f_swap_1618, &_call_f_swap_1618); @@ -680,6 +646,9 @@ static gsi::Methods methods_QJsonValue () { } gsi::Class decl_QJsonValue ("QtCore", "QJsonValue", + gsi::method_ext("==", &QJsonValue_operator_eq, gsi::arg ("other"), "@brief Method bool QJsonValue::operator==(const QJsonValue &) const") + + gsi::method_ext("!=", &QJsonValue_operator_ne, gsi::arg ("other"), "@brief Method bool QJsonValue::operator!=(const QJsonValue &) const") ++ methods_QJsonValue (), "@qt\n@brief Binding of QJsonValue"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQJsonValueRef.cc b/src/gsiqt/qt6/QtCore/gsiDeclQJsonValueRef.cc index c5cde8a58..0d3972f42 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQJsonValueRef.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQJsonValueRef.cc @@ -37,6 +37,12 @@ // ----------------------------------------------------------------------- // class QJsonValueRef + static bool QJsonValueRef_operator_eq(const QJsonValueRef *a, const QJsonValueRef &b) { + return *a == b; + } + static bool QJsonValueRef_operator_ne(const QJsonValueRef *a, const QJsonValueRef &b) { + return !(*a == b); + } // Constructor QJsonValueRef::QJsonValueRef(QJsonArray *array, qsizetype idx) @@ -244,44 +250,6 @@ static void _call_f_operator_eq__2598 (const qt_gsi::GenericMethod * /*decl*/, v } -// bool QJsonValueRef::operator!=(const QJsonValue &other) - - -static void _init_f_operator_excl__eq__c2313 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c2313 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonValue &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonValueRef *)cls)->operator!= (arg1)); -} - - -// bool QJsonValueRef::operator==(const QJsonValue &other) - - -static void _init_f_operator_eq__eq__c2313 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c2313 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonValue &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonValueRef *)cls)->operator== (arg1)); -} - - // const QJsonValue QJsonValueRef::operator[](qsizetype i) @@ -474,8 +442,6 @@ static gsi::Methods methods_QJsonValueRef () { methods += new qt_gsi::GenericMethod ("isUndefined?", "@brief Method bool QJsonValueRef::isUndefined()\n", true, &_init_f_isUndefined_c0, &_call_f_isUndefined_c0); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QJsonValueRef &QJsonValueRef::operator =(const QJsonValue &val)\n", false, &_init_f_operator_eq__2313, &_call_f_operator_eq__2313); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QJsonValueRef &QJsonValueRef::operator =(const QJsonValueRef &val)\n", false, &_init_f_operator_eq__2598, &_call_f_operator_eq__2598); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QJsonValueRef::operator!=(const QJsonValue &other)\n", true, &_init_f_operator_excl__eq__c2313, &_call_f_operator_excl__eq__c2313); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QJsonValueRef::operator==(const QJsonValue &other)\n", true, &_init_f_operator_eq__eq__c2313, &_call_f_operator_eq__eq__c2313); methods += new qt_gsi::GenericMethod ("[]", "@brief Method const QJsonValue QJsonValueRef::operator[](qsizetype i)\n", true, &_init_f_operator_index__c1442, &_call_f_operator_index__c1442); methods += new qt_gsi::GenericMethod ("toArray", "@brief Method QJsonArray QJsonValueRef::toArray()\n", true, &_init_f_toArray_c0, &_call_f_toArray_c0); methods += new qt_gsi::GenericMethod ("toBool", "@brief Method bool QJsonValueRef::toBool(bool defaultValue)\n", true, &_init_f_toBool_c864, &_call_f_toBool_c864); @@ -490,6 +456,9 @@ static gsi::Methods methods_QJsonValueRef () { } gsi::Class decl_QJsonValueRef ("QtCore", "QJsonValueRef", + gsi::method_ext("==", &QJsonValueRef_operator_eq, gsi::arg ("other"), "@brief Method bool QJsonValueRef::operator==(const QJsonValueRef &) const") + + gsi::method_ext("!=", &QJsonValueRef_operator_ne, gsi::arg ("other"), "@brief Method bool QJsonValueRef::operator!=(const QJsonValueRef &) const") ++ methods_QJsonValueRef (), "@qt\n@brief Binding of QJsonValueRef"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQLine.cc b/src/gsiqt/qt6/QtCore/gsiDeclQLine.cc index 08a875889..0bf783a6a 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQLine.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQLine.cc @@ -35,6 +35,12 @@ // ----------------------------------------------------------------------- // class QLine + static bool QLine_operator_eq(const QLine *a, const QLine &b) { + return *a == b; + } + static bool QLine_operator_ne(const QLine *a, const QLine &b) { + return !(*a == b); + } // Constructor QLine::QLine() @@ -161,44 +167,6 @@ static void _call_f_isNull_c0 (const qt_gsi::GenericMethod * /*decl*/, void *cls } -// bool QLine::operator!=(const QLine &d) - - -static void _init_f_operator_excl__eq__c1786 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("d"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c1786 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QLine &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QLine *)cls)->operator!= (arg1)); -} - - -// bool QLine::operator==(const QLine &d) - - -static void _init_f_operator_eq__eq__c1786 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("d"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c1786 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QLine &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QLine *)cls)->operator== (arg1)); -} - - // QPoint QLine::p1() @@ -478,8 +446,6 @@ static gsi::Methods methods_QLine () { methods += new qt_gsi::GenericMethod ("dx", "@brief Method int QLine::dx()\n", true, &_init_f_dx_c0, &_call_f_dx_c0); methods += new qt_gsi::GenericMethod ("dy", "@brief Method int QLine::dy()\n", true, &_init_f_dy_c0, &_call_f_dy_c0); methods += new qt_gsi::GenericMethod ("isNull?", "@brief Method bool QLine::isNull()\n", true, &_init_f_isNull_c0, &_call_f_isNull_c0); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QLine::operator!=(const QLine &d)\n", true, &_init_f_operator_excl__eq__c1786, &_call_f_operator_excl__eq__c1786); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QLine::operator==(const QLine &d)\n", true, &_init_f_operator_eq__eq__c1786, &_call_f_operator_eq__eq__c1786); methods += new qt_gsi::GenericMethod (":p1", "@brief Method QPoint QLine::p1()\n", true, &_init_f_p1_c0, &_call_f_p1_c0); methods += new qt_gsi::GenericMethod (":p2", "@brief Method QPoint QLine::p2()\n", true, &_init_f_p2_c0, &_call_f_p2_c0); methods += new qt_gsi::GenericMethod ("setLine", "@brief Method void QLine::setLine(int x1, int y1, int x2, int y2)\n", false, &_init_f_setLine_2744, &_call_f_setLine_2744); @@ -498,6 +464,9 @@ static gsi::Methods methods_QLine () { } gsi::Class decl_QLine ("QtCore", "QLine", + gsi::method_ext("==", &QLine_operator_eq, gsi::arg ("other"), "@brief Method bool QLine::operator==(const QLine &) const") + + gsi::method_ext("!=", &QLine_operator_ne, gsi::arg ("other"), "@brief Method bool QLine::operator!=(const QLine &) const") ++ methods_QLine (), "@qt\n@brief Binding of QLine"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQLineF.cc b/src/gsiqt/qt6/QtCore/gsiDeclQLineF.cc index 20d9915b6..c285add81 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQLineF.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQLineF.cc @@ -36,6 +36,12 @@ // ----------------------------------------------------------------------- // class QLineF + static bool QLineF_operator_eq(const QLineF *a, const QLineF &b) { + return *a == b; + } + static bool QLineF_operator_ne(const QLineF *a, const QLineF &b) { + return !(*a == b); + } // Constructor QLineF::QLineF() @@ -267,44 +273,6 @@ static void _call_f_normalVector_c0 (const qt_gsi::GenericMethod * /*decl*/, voi } -// bool QLineF::operator!=(const QLineF &d) - - -static void _init_f_operator_excl__eq__c1856 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("d"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c1856 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QLineF &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QLineF *)cls)->operator!= (arg1)); -} - - -// bool QLineF::operator==(const QLineF &d) - - -static void _init_f_operator_eq__eq__c1856 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("d"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c1856 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QLineF &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QLineF *)cls)->operator== (arg1)); -} - - // QPointF QLineF::p1() @@ -701,8 +669,6 @@ static gsi::Methods methods_QLineF () { methods += new qt_gsi::GenericMethod ("isNull?", "@brief Method bool QLineF::isNull()\n", true, &_init_f_isNull_c0, &_call_f_isNull_c0); methods += new qt_gsi::GenericMethod (":length", "@brief Method double QLineF::length()\n", true, &_init_f_length_c0, &_call_f_length_c0); methods += new qt_gsi::GenericMethod ("normalVector", "@brief Method QLineF QLineF::normalVector()\n", true, &_init_f_normalVector_c0, &_call_f_normalVector_c0); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QLineF::operator!=(const QLineF &d)\n", true, &_init_f_operator_excl__eq__c1856, &_call_f_operator_excl__eq__c1856); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QLineF::operator==(const QLineF &d)\n", true, &_init_f_operator_eq__eq__c1856, &_call_f_operator_eq__eq__c1856); methods += new qt_gsi::GenericMethod (":p1", "@brief Method QPointF QLineF::p1()\n", true, &_init_f_p1_c0, &_call_f_p1_c0); methods += new qt_gsi::GenericMethod (":p2", "@brief Method QPointF QLineF::p2()\n", true, &_init_f_p2_c0, &_call_f_p2_c0); methods += new qt_gsi::GenericMethod ("pointAt", "@brief Method QPointF QLineF::pointAt(double t)\n", true, &_init_f_pointAt_c1071, &_call_f_pointAt_c1071); @@ -727,6 +693,9 @@ static gsi::Methods methods_QLineF () { } gsi::Class decl_QLineF ("QtCore", "QLineF", + gsi::method_ext("==", &QLineF_operator_eq, gsi::arg ("other"), "@brief Method bool QLineF::operator==(const QLineF &) const") + + gsi::method_ext("!=", &QLineF_operator_ne, gsi::arg ("other"), "@brief Method bool QLineF::operator!=(const QLineF &) const") ++ methods_QLineF (), "@qt\n@brief Binding of QLineF"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQMimeType.cc b/src/gsiqt/qt6/QtCore/gsiDeclQMimeType.cc index f4c3df8c2..2378859fc 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQMimeType.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQMimeType.cc @@ -34,6 +34,12 @@ // ----------------------------------------------------------------------- // class QMimeType + static bool QMimeType_operator_eq(const QMimeType *a, const QMimeType &b) { + return *a == b; + } + static bool QMimeType_operator_ne(const QMimeType *a, const QMimeType &b) { + return !(*a == b); + } // Constructor QMimeType::QMimeType() @@ -238,25 +244,6 @@ static void _call_f_name_c0 (const qt_gsi::GenericMethod * /*decl*/, void *cls, } -// bool QMimeType::operator!=(const QMimeType &other) - - -static void _init_f_operator_excl__eq__c2204 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c2204 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QMimeType &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QMimeType *)cls)->operator!= (arg1)); -} - - // QMimeType &QMimeType::operator=(const QMimeType &other) @@ -276,25 +263,6 @@ static void _call_f_operator_eq__2204 (const qt_gsi::GenericMethod * /*decl*/, v } -// bool QMimeType::operator==(const QMimeType &other) - - -static void _init_f_operator_eq__eq__c2204 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c2204 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QMimeType &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QMimeType *)cls)->operator== (arg1)); -} - - // QStringList QMimeType::parentMimeTypes() @@ -379,9 +347,7 @@ static gsi::Methods methods_QMimeType () { methods += new qt_gsi::GenericMethod ("isDefault?", "@brief Method bool QMimeType::isDefault()\n", true, &_init_f_isDefault_c0, &_call_f_isDefault_c0); methods += new qt_gsi::GenericMethod ("isValid?", "@brief Method bool QMimeType::isValid()\n", true, &_init_f_isValid_c0, &_call_f_isValid_c0); methods += new qt_gsi::GenericMethod ("name", "@brief Method QString QMimeType::name()\n", true, &_init_f_name_c0, &_call_f_name_c0); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QMimeType::operator!=(const QMimeType &other)\n", true, &_init_f_operator_excl__eq__c2204, &_call_f_operator_excl__eq__c2204); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QMimeType &QMimeType::operator=(const QMimeType &other)\n", false, &_init_f_operator_eq__2204, &_call_f_operator_eq__2204); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QMimeType::operator==(const QMimeType &other)\n", true, &_init_f_operator_eq__eq__c2204, &_call_f_operator_eq__eq__c2204); methods += new qt_gsi::GenericMethod ("parentMimeTypes", "@brief Method QStringList QMimeType::parentMimeTypes()\n", true, &_init_f_parentMimeTypes_c0, &_call_f_parentMimeTypes_c0); methods += new qt_gsi::GenericMethod ("preferredSuffix", "@brief Method QString QMimeType::preferredSuffix()\n", true, &_init_f_preferredSuffix_c0, &_call_f_preferredSuffix_c0); methods += new qt_gsi::GenericMethod ("suffixes", "@brief Method QStringList QMimeType::suffixes()\n", true, &_init_f_suffixes_c0, &_call_f_suffixes_c0); @@ -390,6 +356,9 @@ static gsi::Methods methods_QMimeType () { } gsi::Class decl_QMimeType ("QtCore", "QMimeType", + gsi::method_ext("==", &QMimeType_operator_eq, gsi::arg ("other"), "@brief Method bool QMimeType::operator==(const QMimeType &) const") + + gsi::method_ext("!=", &QMimeType_operator_ne, gsi::arg ("other"), "@brief Method bool QMimeType::operator!=(const QMimeType &) const") ++ methods_QMimeType (), "@qt\n@brief Binding of QMimeType"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQModelIndex.cc b/src/gsiqt/qt6/QtCore/gsiDeclQModelIndex.cc index 338220643..2c4dea35f 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQModelIndex.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQModelIndex.cc @@ -36,6 +36,12 @@ // ----------------------------------------------------------------------- // class QModelIndex + static bool QModelIndex_operator_eq(const QModelIndex *a, const QModelIndex &b) { + return *a == b; + } + static bool QModelIndex_operator_ne(const QModelIndex *a, const QModelIndex &b) { + return !(*a == b); + } // Constructor QModelIndex::QModelIndex() @@ -196,25 +202,6 @@ static void _call_f_multiData_c2196 (const qt_gsi::GenericMethod * /*decl*/, voi } -// bool QModelIndex::operator!=(const QModelIndex &other) - - -static void _init_f_operator_excl__eq__c2395 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c2395 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QModelIndex &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QModelIndex *)cls)->operator!= (arg1)); -} - - // bool QModelIndex::operator<(const QModelIndex &other) @@ -234,25 +221,6 @@ static void _call_f_operator_lt__c2395 (const qt_gsi::GenericMethod * /*decl*/, } -// bool QModelIndex::operator==(const QModelIndex &other) - - -static void _init_f_operator_eq__eq__c2395 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c2395 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QModelIndex &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QModelIndex *)cls)->operator== (arg1)); -} - - // QModelIndex QModelIndex::parent() @@ -359,9 +327,7 @@ static gsi::Methods methods_QModelIndex () { methods += new qt_gsi::GenericMethod ("isValid?", "@brief Method bool QModelIndex::isValid()\n", true, &_init_f_isValid_c0, &_call_f_isValid_c0); methods += new qt_gsi::GenericMethod ("model", "@brief Method const QAbstractItemModel *QModelIndex::model()\n", true, &_init_f_model_c0, &_call_f_model_c0); methods += new qt_gsi::GenericMethod ("multiData", "@brief Method void QModelIndex::multiData(QModelRoleDataSpan roleDataSpan)\n", true, &_init_f_multiData_c2196, &_call_f_multiData_c2196); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QModelIndex::operator!=(const QModelIndex &other)\n", true, &_init_f_operator_excl__eq__c2395, &_call_f_operator_excl__eq__c2395); methods += new qt_gsi::GenericMethod ("<", "@brief Method bool QModelIndex::operator<(const QModelIndex &other)\n", true, &_init_f_operator_lt__c2395, &_call_f_operator_lt__c2395); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QModelIndex::operator==(const QModelIndex &other)\n", true, &_init_f_operator_eq__eq__c2395, &_call_f_operator_eq__eq__c2395); methods += new qt_gsi::GenericMethod ("parent", "@brief Method QModelIndex QModelIndex::parent()\n", true, &_init_f_parent_c0, &_call_f_parent_c0); methods += new qt_gsi::GenericMethod ("row", "@brief Method int QModelIndex::row()\n", true, &_init_f_row_c0, &_call_f_row_c0); methods += new qt_gsi::GenericMethod ("sibling", "@brief Method QModelIndex QModelIndex::sibling(int row, int column)\n", true, &_init_f_sibling_c1426, &_call_f_sibling_c1426); @@ -371,6 +337,9 @@ static gsi::Methods methods_QModelIndex () { } gsi::Class decl_QModelIndex ("QtCore", "QModelIndex", + gsi::method_ext("==", &QModelIndex_operator_eq, gsi::arg ("other"), "@brief Method bool QModelIndex::operator==(const QModelIndex &) const") + + gsi::method_ext("!=", &QModelIndex_operator_ne, gsi::arg ("other"), "@brief Method bool QModelIndex::operator!=(const QModelIndex &) const") ++ methods_QModelIndex (), "@qt\n@brief Binding of QModelIndex"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQPersistentModelIndex.cc b/src/gsiqt/qt6/QtCore/gsiDeclQPersistentModelIndex.cc index 35b7a6ba2..7fecee04c 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQPersistentModelIndex.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQPersistentModelIndex.cc @@ -37,6 +37,12 @@ // ----------------------------------------------------------------------- // class QPersistentModelIndex + static bool QPersistentModelIndex_operator_eq(const QPersistentModelIndex *a, const QPersistentModelIndex &b) { + return *a == b; + } + static bool QPersistentModelIndex_operator_ne(const QPersistentModelIndex *a, const QPersistentModelIndex &b) { + return !(*a == b); + } #if QT_VERSION < 0x60000 static const QModelIndex &castToQModelIndex(const QPersistentModelIndex *m) { @@ -246,44 +252,6 @@ static void _call_f_multiData_c2196 (const qt_gsi::GenericMethod * /*decl*/, voi } -// bool QPersistentModelIndex::operator!=(const QPersistentModelIndex &other) - - -static void _init_f_operator_excl__eq__c3468 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c3468 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QPersistentModelIndex &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QPersistentModelIndex *)cls)->operator!= (arg1)); -} - - -// bool QPersistentModelIndex::operator!=(const QModelIndex &other) - - -static void _init_f_operator_excl__eq__c2395 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c2395 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QModelIndex &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QPersistentModelIndex *)cls)->operator!= (arg1)); -} - - // bool QPersistentModelIndex::operator<(const QPersistentModelIndex &other) @@ -341,44 +309,6 @@ static void _call_f_operator_eq__2395 (const qt_gsi::GenericMethod * /*decl*/, v } -// bool QPersistentModelIndex::operator==(const QPersistentModelIndex &other) - - -static void _init_f_operator_eq__eq__c3468 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c3468 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QPersistentModelIndex &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QPersistentModelIndex *)cls)->operator== (arg1)); -} - - -// bool QPersistentModelIndex::operator==(const QModelIndex &other) - - -static void _init_f_operator_eq__eq__c2395 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c2395 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QModelIndex &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QPersistentModelIndex *)cls)->operator== (arg1)); -} - - // QModelIndex QPersistentModelIndex::parent() @@ -469,13 +399,9 @@ static gsi::Methods methods_QPersistentModelIndex () { methods += new qt_gsi::GenericMethod ("isValid?", "@brief Method bool QPersistentModelIndex::isValid()\n", true, &_init_f_isValid_c0, &_call_f_isValid_c0); methods += new qt_gsi::GenericMethod ("model", "@brief Method const QAbstractItemModel *QPersistentModelIndex::model()\n", true, &_init_f_model_c0, &_call_f_model_c0); methods += new qt_gsi::GenericMethod ("multiData", "@brief Method void QPersistentModelIndex::multiData(QModelRoleDataSpan roleDataSpan)\n", true, &_init_f_multiData_c2196, &_call_f_multiData_c2196); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QPersistentModelIndex::operator!=(const QPersistentModelIndex &other)\n", true, &_init_f_operator_excl__eq__c3468, &_call_f_operator_excl__eq__c3468); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QPersistentModelIndex::operator!=(const QModelIndex &other)\n", true, &_init_f_operator_excl__eq__c2395, &_call_f_operator_excl__eq__c2395); methods += new qt_gsi::GenericMethod ("<", "@brief Method bool QPersistentModelIndex::operator<(const QPersistentModelIndex &other)\n", true, &_init_f_operator_lt__c3468, &_call_f_operator_lt__c3468); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QPersistentModelIndex &QPersistentModelIndex::operator=(const QPersistentModelIndex &other)\n", false, &_init_f_operator_eq__3468, &_call_f_operator_eq__3468); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QPersistentModelIndex &QPersistentModelIndex::operator=(const QModelIndex &other)\n", false, &_init_f_operator_eq__2395, &_call_f_operator_eq__2395); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QPersistentModelIndex::operator==(const QPersistentModelIndex &other)\n", true, &_init_f_operator_eq__eq__c3468, &_call_f_operator_eq__eq__c3468); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QPersistentModelIndex::operator==(const QModelIndex &other)\n", true, &_init_f_operator_eq__eq__c2395, &_call_f_operator_eq__eq__c2395); methods += new qt_gsi::GenericMethod ("parent", "@brief Method QModelIndex QPersistentModelIndex::parent()\n", true, &_init_f_parent_c0, &_call_f_parent_c0); methods += new qt_gsi::GenericMethod ("row", "@brief Method int QPersistentModelIndex::row()\n", true, &_init_f_row_c0, &_call_f_row_c0); methods += new qt_gsi::GenericMethod ("sibling", "@brief Method QModelIndex QPersistentModelIndex::sibling(int row, int column)\n", true, &_init_f_sibling_c1426, &_call_f_sibling_c1426); @@ -484,6 +410,9 @@ static gsi::Methods methods_QPersistentModelIndex () { } gsi::Class decl_QPersistentModelIndex ("QtCore", "QPersistentModelIndex", + gsi::method_ext("==", &QPersistentModelIndex_operator_eq, gsi::arg ("other"), "@brief Method bool QPersistentModelIndex::operator==(const QPersistentModelIndex &) const") + + gsi::method_ext("!=", &QPersistentModelIndex_operator_ne, gsi::arg ("other"), "@brief Method bool QPersistentModelIndex::operator!=(const QPersistentModelIndex &) const") ++ gsi::method_ext("castToQModelIndex", &castToQModelIndex, "@brief Binding for \"operator const QModelIndex &\".") + methods_QPersistentModelIndex (), diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQProcessEnvironment.cc b/src/gsiqt/qt6/QtCore/gsiDeclQProcessEnvironment.cc index 5706f3c89..d1505a386 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQProcessEnvironment.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQProcessEnvironment.cc @@ -34,6 +34,12 @@ // ----------------------------------------------------------------------- // class QProcessEnvironment + static bool QProcessEnvironment_operator_eq(const QProcessEnvironment *a, const QProcessEnvironment &b) { + return *a == b; + } + static bool QProcessEnvironment_operator_ne(const QProcessEnvironment *a, const QProcessEnvironment &b) { + return !(*a == b); + } // Constructor QProcessEnvironment::QProcessEnvironment() @@ -177,25 +183,6 @@ static void _call_f_keys_c0 (const qt_gsi::GenericMethod * /*decl*/, void *cls, } -// bool QProcessEnvironment::operator!=(const QProcessEnvironment &other) - - -static void _init_f_operator_excl__eq__c3302 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c3302 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QProcessEnvironment &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QProcessEnvironment *)cls)->operator!= (arg1)); -} - - // QProcessEnvironment &QProcessEnvironment::operator=(const QProcessEnvironment &other) @@ -215,25 +202,6 @@ static void _call_f_operator_eq__3302 (const qt_gsi::GenericMethod * /*decl*/, v } -// bool QProcessEnvironment::operator==(const QProcessEnvironment &other) - - -static void _init_f_operator_eq__eq__c3302 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c3302 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QProcessEnvironment &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QProcessEnvironment *)cls)->operator== (arg1)); -} - - // void QProcessEnvironment::remove(const QString &name) @@ -340,9 +308,7 @@ static gsi::Methods methods_QProcessEnvironment () { methods += new qt_gsi::GenericMethod ("insert", "@brief Method void QProcessEnvironment::insert(const QProcessEnvironment &e)\n", false, &_init_f_insert_3302, &_call_f_insert_3302); methods += new qt_gsi::GenericMethod ("isEmpty?", "@brief Method bool QProcessEnvironment::isEmpty()\n", true, &_init_f_isEmpty_c0, &_call_f_isEmpty_c0); methods += new qt_gsi::GenericMethod ("keys", "@brief Method QStringList QProcessEnvironment::keys()\n", true, &_init_f_keys_c0, &_call_f_keys_c0); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QProcessEnvironment::operator!=(const QProcessEnvironment &other)\n", true, &_init_f_operator_excl__eq__c3302, &_call_f_operator_excl__eq__c3302); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QProcessEnvironment &QProcessEnvironment::operator=(const QProcessEnvironment &other)\n", false, &_init_f_operator_eq__3302, &_call_f_operator_eq__3302); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QProcessEnvironment::operator==(const QProcessEnvironment &other)\n", true, &_init_f_operator_eq__eq__c3302, &_call_f_operator_eq__eq__c3302); methods += new qt_gsi::GenericMethod ("remove", "@brief Method void QProcessEnvironment::remove(const QString &name)\n", false, &_init_f_remove_2025, &_call_f_remove_2025); methods += new qt_gsi::GenericMethod ("swap", "@brief Method void QProcessEnvironment::swap(QProcessEnvironment &other)\n", false, &_init_f_swap_2607, &_call_f_swap_2607); methods += new qt_gsi::GenericMethod ("toStringList", "@brief Method QStringList QProcessEnvironment::toStringList()\n", true, &_init_f_toStringList_c0, &_call_f_toStringList_c0); @@ -352,6 +318,9 @@ static gsi::Methods methods_QProcessEnvironment () { } gsi::Class decl_QProcessEnvironment ("QtCore", "QProcessEnvironment", + gsi::method_ext("==", &QProcessEnvironment_operator_eq, gsi::arg ("other"), "@brief Method bool QProcessEnvironment::operator==(const QProcessEnvironment &) const") + + gsi::method_ext("!=", &QProcessEnvironment_operator_ne, gsi::arg ("other"), "@brief Method bool QProcessEnvironment::operator!=(const QProcessEnvironment &) const") ++ methods_QProcessEnvironment (), "@qt\n@brief Binding of QProcessEnvironment"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQRegularExpression.cc b/src/gsiqt/qt6/QtCore/gsiDeclQRegularExpression.cc index 23d4d3e43..cd8779126 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQRegularExpression.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQRegularExpression.cc @@ -36,6 +36,12 @@ // ----------------------------------------------------------------------- // class QRegularExpression + static bool QRegularExpression_operator_eq(const QRegularExpression *a, const QRegularExpression &b) { + return *a == b; + } + static bool QRegularExpression_operator_ne(const QRegularExpression *a, const QRegularExpression &b) { + return !(*a == b); + } // Constructor QRegularExpression::QRegularExpression() @@ -209,25 +215,6 @@ static void _call_f_namedCaptureGroups_c0 (const qt_gsi::GenericMethod * /*decl* } -// bool QRegularExpression::operator!=(const QRegularExpression &re) - - -static void _init_f_operator_excl__eq__c3188 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("re"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c3188 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QRegularExpression &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QRegularExpression *)cls)->operator!= (arg1)); -} - - // QRegularExpression &QRegularExpression::operator=(const QRegularExpression &re) @@ -247,25 +234,6 @@ static void _call_f_operator_eq__3188 (const qt_gsi::GenericMethod * /*decl*/, v } -// bool QRegularExpression::operator==(const QRegularExpression &re) - - -static void _init_f_operator_eq__eq__c3188 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("re"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c3188 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QRegularExpression &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QRegularExpression *)cls)->operator== (arg1)); -} - - // void QRegularExpression::optimize() @@ -462,9 +430,7 @@ static gsi::Methods methods_QRegularExpression () { methods += new qt_gsi::GenericMethod ("isValid?", "@brief Method bool QRegularExpression::isValid()\n", true, &_init_f_isValid_c0, &_call_f_isValid_c0); methods += new qt_gsi::GenericMethod ("match", "@brief Method QRegularExpressionMatch QRegularExpression::match(const QString &subject, qsizetype offset, QRegularExpression::MatchType matchType, QFlags matchOptions)\n", true, &_init_f_match_c10730, &_call_f_match_c10730); methods += new qt_gsi::GenericMethod ("namedCaptureGroups", "@brief Method QStringList QRegularExpression::namedCaptureGroups()\n", true, &_init_f_namedCaptureGroups_c0, &_call_f_namedCaptureGroups_c0); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QRegularExpression::operator!=(const QRegularExpression &re)\n", true, &_init_f_operator_excl__eq__c3188, &_call_f_operator_excl__eq__c3188); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QRegularExpression &QRegularExpression::operator=(const QRegularExpression &re)\n", false, &_init_f_operator_eq__3188, &_call_f_operator_eq__3188); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QRegularExpression::operator==(const QRegularExpression &re)\n", true, &_init_f_operator_eq__eq__c3188, &_call_f_operator_eq__eq__c3188); methods += new qt_gsi::GenericMethod ("optimize", "@brief Method void QRegularExpression::optimize()\n", true, &_init_f_optimize_c0, &_call_f_optimize_c0); methods += new qt_gsi::GenericMethod (":pattern", "@brief Method QString QRegularExpression::pattern()\n", true, &_init_f_pattern_c0, &_call_f_pattern_c0); methods += new qt_gsi::GenericMethod ("patternErrorOffset", "@brief Method qsizetype QRegularExpression::patternErrorOffset()\n", true, &_init_f_patternErrorOffset_c0, &_call_f_patternErrorOffset_c0); @@ -479,6 +445,9 @@ static gsi::Methods methods_QRegularExpression () { } gsi::Class decl_QRegularExpression ("QtCore", "QRegularExpression", + gsi::method_ext("==", &QRegularExpression_operator_eq, gsi::arg ("other"), "@brief Method bool QRegularExpression::operator==(const QRegularExpression &) const") + + gsi::method_ext("!=", &QRegularExpression_operator_ne, gsi::arg ("other"), "@brief Method bool QRegularExpression::operator!=(const QRegularExpression &) const") ++ methods_QRegularExpression (), "@qt\n@brief Binding of QRegularExpression"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQUrl.cc b/src/gsiqt/qt6/QtCore/gsiDeclQUrl.cc index 6a754e6b6..8f0302a8b 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQUrl.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQUrl.cc @@ -35,6 +35,12 @@ // ----------------------------------------------------------------------- // class QUrl + static bool QUrl_operator_eq(const QUrl *a, const QUrl &b) { + return *a == b; + } + static bool QUrl_operator_ne(const QUrl *a, const QUrl &b) { + return !(*a == b); + } // Constructor QUrl::QUrl() @@ -380,25 +386,6 @@ static void _call_f_matches_c9164 (const qt_gsi::GenericMethod * /*decl*/, void } -// bool QUrl::operator !=(const QUrl &url) - - -static void _init_f_operator_excl__eq__c1701 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("url"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c1701 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QUrl &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QUrl *)cls)->operator != (arg1)); -} - - // bool QUrl::operator <(const QUrl &url) @@ -437,25 +424,6 @@ static void _call_f_operator_eq__1701 (const qt_gsi::GenericMethod * /*decl*/, v } -// bool QUrl::operator ==(const QUrl &url) - - -static void _init_f_operator_eq__eq__c1701 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("url"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c1701 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QUrl &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QUrl *)cls)->operator == (arg1)); -} - - // QUrl &QUrl::operator=(const QString &url) @@ -1254,10 +1222,8 @@ static gsi::Methods methods_QUrl () { methods += new qt_gsi::GenericMethod ("isRelative?", "@brief Method bool QUrl::isRelative()\n", true, &_init_f_isRelative_c0, &_call_f_isRelative_c0); methods += new qt_gsi::GenericMethod ("isValid?", "@brief Method bool QUrl::isValid()\n", true, &_init_f_isValid_c0, &_call_f_isValid_c0); methods += new qt_gsi::GenericMethod ("matches", "@brief Method bool QUrl::matches(const QUrl &url, QUrlTwoFlags options)\n", true, &_init_f_matches_c9164, &_call_f_matches_c9164); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QUrl::operator !=(const QUrl &url)\n", true, &_init_f_operator_excl__eq__c1701, &_call_f_operator_excl__eq__c1701); methods += new qt_gsi::GenericMethod ("<", "@brief Method bool QUrl::operator <(const QUrl &url)\n", true, &_init_f_operator_lt__c1701, &_call_f_operator_lt__c1701); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QUrl &QUrl::operator =(const QUrl ©)\n", false, &_init_f_operator_eq__1701, &_call_f_operator_eq__1701); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QUrl::operator ==(const QUrl &url)\n", true, &_init_f_operator_eq__eq__c1701, &_call_f_operator_eq__eq__c1701); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QUrl &QUrl::operator=(const QString &url)\n", false, &_init_f_operator_eq__2025, &_call_f_operator_eq__2025); methods += new qt_gsi::GenericMethod ("password", "@brief Method QString QUrl::password(QFlags)\n", true, &_init_f_password_c4267, &_call_f_password_c4267); methods += new qt_gsi::GenericMethod ("path", "@brief Method QString QUrl::path(QFlags options)\n", true, &_init_f_path_c4267, &_call_f_path_c4267); @@ -1300,6 +1266,9 @@ static gsi::Methods methods_QUrl () { } gsi::Class decl_QUrl ("QtCore", "QUrl", + gsi::method_ext("==", &QUrl_operator_eq, gsi::arg ("other"), "@brief Method bool QUrl::operator==(const QUrl &) const") + + gsi::method_ext("!=", &QUrl_operator_ne, gsi::arg ("other"), "@brief Method bool QUrl::operator!=(const QUrl &) const") ++ methods_QUrl (), "@qt\n@brief Binding of QUrl"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQUrlQuery.cc b/src/gsiqt/qt6/QtCore/gsiDeclQUrlQuery.cc index d91da6006..623676e7c 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQUrlQuery.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQUrlQuery.cc @@ -35,6 +35,12 @@ // ----------------------------------------------------------------------- // class QUrlQuery + static bool QUrlQuery_operator_eq(const QUrlQuery *a, const QUrlQuery &b) { + return *a == b; + } + static bool QUrlQuery_operator_ne(const QUrlQuery *a, const QUrlQuery &b) { + return !(*a == b); + } // Constructor QUrlQuery::QUrlQuery() @@ -218,25 +224,6 @@ static void _call_f_isEmpty_c0 (const qt_gsi::GenericMethod * /*decl*/, void *cl } -// bool QUrlQuery::operator!=(const QUrlQuery &other) - - -static void _init_f_operator_excl__eq__c2235 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c2235 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QUrlQuery &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QUrlQuery *)cls)->operator!= (arg1)); -} - - // QUrlQuery &QUrlQuery::operator=(const QUrlQuery &other) @@ -256,25 +243,6 @@ static void _call_f_operator_eq__2235 (const qt_gsi::GenericMethod * /*decl*/, v } -// bool QUrlQuery::operator==(const QUrlQuery &other) - - -static void _init_f_operator_eq__eq__c2235 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c2235 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QUrlQuery &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QUrlQuery *)cls)->operator== (arg1)); -} - - // QString QUrlQuery::query(QFlags encoding) @@ -553,9 +521,7 @@ static gsi::Methods methods_QUrlQuery () { methods += new qt_gsi::GenericMethod ("hasQueryItem", "@brief Method bool QUrlQuery::hasQueryItem(const QString &key)\n", true, &_init_f_hasQueryItem_c2025, &_call_f_hasQueryItem_c2025); methods += new qt_gsi::GenericMethod ("isDetached?", "@brief Method bool QUrlQuery::isDetached()\n", true, &_init_f_isDetached_c0, &_call_f_isDetached_c0); methods += new qt_gsi::GenericMethod ("isEmpty?", "@brief Method bool QUrlQuery::isEmpty()\n", true, &_init_f_isEmpty_c0, &_call_f_isEmpty_c0); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QUrlQuery::operator!=(const QUrlQuery &other)\n", true, &_init_f_operator_excl__eq__c2235, &_call_f_operator_excl__eq__c2235); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QUrlQuery &QUrlQuery::operator=(const QUrlQuery &other)\n", false, &_init_f_operator_eq__2235, &_call_f_operator_eq__2235); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QUrlQuery::operator==(const QUrlQuery &other)\n", true, &_init_f_operator_eq__eq__c2235, &_call_f_operator_eq__eq__c2235); methods += new qt_gsi::GenericMethod ("query", "@brief Method QString QUrlQuery::query(QFlags encoding)\n", true, &_init_f_query_c4267, &_call_f_query_c4267); methods += new qt_gsi::GenericMethod ("queryItemValue", "@brief Method QString QUrlQuery::queryItemValue(const QString &key, QFlags encoding)\n", true, &_init_f_queryItemValue_c6184, &_call_f_queryItemValue_c6184); methods += new qt_gsi::GenericMethod ("queryItems", "@brief Method QList > QUrlQuery::queryItems(QFlags encoding)\n", true, &_init_f_queryItems_c4267, &_call_f_queryItems_c4267); @@ -574,6 +540,9 @@ static gsi::Methods methods_QUrlQuery () { } gsi::Class decl_QUrlQuery ("QtCore", "QUrlQuery", + gsi::method_ext("==", &QUrlQuery_operator_eq, gsi::arg ("other"), "@brief Method bool QUrlQuery::operator==(const QUrlQuery &) const") + + gsi::method_ext("!=", &QUrlQuery_operator_ne, gsi::arg ("other"), "@brief Method bool QUrlQuery::operator!=(const QUrlQuery &) const") ++ methods_QUrlQuery (), "@qt\n@brief Binding of QUrlQuery"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQXmlStreamAttribute.cc b/src/gsiqt/qt6/QtCore/gsiDeclQXmlStreamAttribute.cc index 7e6802ad6..eebc44941 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQXmlStreamAttribute.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQXmlStreamAttribute.cc @@ -34,6 +34,12 @@ // ----------------------------------------------------------------------- // class QXmlStreamAttribute + static bool QXmlStreamAttribute_operator_eq(const QXmlStreamAttribute *a, const QXmlStreamAttribute &b) { + return *a == b; + } + static bool QXmlStreamAttribute_operator_ne(const QXmlStreamAttribute *a, const QXmlStreamAttribute &b) { + return !(*a == b); + } // Constructor QXmlStreamAttribute::QXmlStreamAttribute() @@ -112,44 +118,6 @@ static void _call_f_isDefault_c0 (const qt_gsi::GenericMethod * /*decl*/, void * } -// bool QXmlStreamAttribute::operator!=(const QXmlStreamAttribute &other) - - -static void _init_f_operator_excl__eq__c3267 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c3267 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QXmlStreamAttribute &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QXmlStreamAttribute *)cls)->operator!= (arg1)); -} - - -// bool QXmlStreamAttribute::operator==(const QXmlStreamAttribute &other) - - -static void _init_f_operator_eq__eq__c3267 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c3267 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QXmlStreamAttribute &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QXmlStreamAttribute *)cls)->operator== (arg1)); -} - - namespace gsi { @@ -160,12 +128,13 @@ static gsi::Methods methods_QXmlStreamAttribute () { methods += new qt_gsi::GenericStaticMethod ("new", "@brief Constructor QXmlStreamAttribute::QXmlStreamAttribute(const QString &qualifiedName, const QString &value)\nThis method creates an object of class QXmlStreamAttribute.", &_init_ctor_QXmlStreamAttribute_3942, &_call_ctor_QXmlStreamAttribute_3942); methods += new qt_gsi::GenericStaticMethod ("new", "@brief Constructor QXmlStreamAttribute::QXmlStreamAttribute(const QString &namespaceUri, const QString &name, const QString &value)\nThis method creates an object of class QXmlStreamAttribute.", &_init_ctor_QXmlStreamAttribute_5859, &_call_ctor_QXmlStreamAttribute_5859); methods += new qt_gsi::GenericMethod ("isDefault?", "@brief Method bool QXmlStreamAttribute::isDefault()\n", true, &_init_f_isDefault_c0, &_call_f_isDefault_c0); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QXmlStreamAttribute::operator!=(const QXmlStreamAttribute &other)\n", true, &_init_f_operator_excl__eq__c3267, &_call_f_operator_excl__eq__c3267); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QXmlStreamAttribute::operator==(const QXmlStreamAttribute &other)\n", true, &_init_f_operator_eq__eq__c3267, &_call_f_operator_eq__eq__c3267); return methods; } gsi::Class decl_QXmlStreamAttribute ("QtCore", "QXmlStreamAttribute", + gsi::method_ext("==", &QXmlStreamAttribute_operator_eq, gsi::arg ("other"), "@brief Method bool QXmlStreamAttribute::operator==(const QXmlStreamAttribute &) const") + + gsi::method_ext("!=", &QXmlStreamAttribute_operator_ne, gsi::arg ("other"), "@brief Method bool QXmlStreamAttribute::operator!=(const QXmlStreamAttribute &) const") ++ methods_QXmlStreamAttribute (), "@qt\n@brief Binding of QXmlStreamAttribute"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQXmlStreamEntityDeclaration.cc b/src/gsiqt/qt6/QtCore/gsiDeclQXmlStreamEntityDeclaration.cc index 9d8b7ccef..aa60aefa9 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQXmlStreamEntityDeclaration.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQXmlStreamEntityDeclaration.cc @@ -34,6 +34,12 @@ // ----------------------------------------------------------------------- // class QXmlStreamEntityDeclaration + static bool QXmlStreamEntityDeclaration_operator_eq(const QXmlStreamEntityDeclaration *a, const QXmlStreamEntityDeclaration &b) { + return *a == b; + } + static bool QXmlStreamEntityDeclaration_operator_ne(const QXmlStreamEntityDeclaration *a, const QXmlStreamEntityDeclaration &b) { + return !(*a == b); + } // Constructor QXmlStreamEntityDeclaration::QXmlStreamEntityDeclaration() @@ -50,44 +56,6 @@ static void _call_ctor_QXmlStreamEntityDeclaration_0 (const qt_gsi::GenericStati } -// bool QXmlStreamEntityDeclaration::operator!=(const QXmlStreamEntityDeclaration &other) - - -static void _init_f_operator_excl__eq__c4082 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c4082 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QXmlStreamEntityDeclaration &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QXmlStreamEntityDeclaration *)cls)->operator!= (arg1)); -} - - -// bool QXmlStreamEntityDeclaration::operator==(const QXmlStreamEntityDeclaration &other) - - -static void _init_f_operator_eq__eq__c4082 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c4082 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QXmlStreamEntityDeclaration &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QXmlStreamEntityDeclaration *)cls)->operator== (arg1)); -} - - namespace gsi { @@ -95,12 +63,13 @@ namespace gsi static gsi::Methods methods_QXmlStreamEntityDeclaration () { gsi::Methods methods; methods += new qt_gsi::GenericStaticMethod ("new", "@brief Constructor QXmlStreamEntityDeclaration::QXmlStreamEntityDeclaration()\nThis method creates an object of class QXmlStreamEntityDeclaration.", &_init_ctor_QXmlStreamEntityDeclaration_0, &_call_ctor_QXmlStreamEntityDeclaration_0); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QXmlStreamEntityDeclaration::operator!=(const QXmlStreamEntityDeclaration &other)\n", true, &_init_f_operator_excl__eq__c4082, &_call_f_operator_excl__eq__c4082); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QXmlStreamEntityDeclaration::operator==(const QXmlStreamEntityDeclaration &other)\n", true, &_init_f_operator_eq__eq__c4082, &_call_f_operator_eq__eq__c4082); return methods; } gsi::Class decl_QXmlStreamEntityDeclaration ("QtCore", "QXmlStreamEntityDeclaration", + gsi::method_ext("==", &QXmlStreamEntityDeclaration_operator_eq, gsi::arg ("other"), "@brief Method bool QXmlStreamEntityDeclaration::operator==(const QXmlStreamEntityDeclaration &) const") + + gsi::method_ext("!=", &QXmlStreamEntityDeclaration_operator_ne, gsi::arg ("other"), "@brief Method bool QXmlStreamEntityDeclaration::operator!=(const QXmlStreamEntityDeclaration &) const") ++ methods_QXmlStreamEntityDeclaration (), "@qt\n@brief Binding of QXmlStreamEntityDeclaration"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQXmlStreamNamespaceDeclaration.cc b/src/gsiqt/qt6/QtCore/gsiDeclQXmlStreamNamespaceDeclaration.cc index d0a2feac0..d128d3318 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQXmlStreamNamespaceDeclaration.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQXmlStreamNamespaceDeclaration.cc @@ -34,6 +34,12 @@ // ----------------------------------------------------------------------- // class QXmlStreamNamespaceDeclaration + static bool QXmlStreamNamespaceDeclaration_operator_eq(const QXmlStreamNamespaceDeclaration *a, const QXmlStreamNamespaceDeclaration &b) { + return *a == b; + } + static bool QXmlStreamNamespaceDeclaration_operator_ne(const QXmlStreamNamespaceDeclaration *a, const QXmlStreamNamespaceDeclaration &b) { + return !(*a == b); + } // Constructor QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration() @@ -72,44 +78,6 @@ static void _call_ctor_QXmlStreamNamespaceDeclaration_3942 (const qt_gsi::Generi } -// bool QXmlStreamNamespaceDeclaration::operator!=(const QXmlStreamNamespaceDeclaration &other) - - -static void _init_f_operator_excl__eq__c4354 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c4354 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QXmlStreamNamespaceDeclaration &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QXmlStreamNamespaceDeclaration *)cls)->operator!= (arg1)); -} - - -// bool QXmlStreamNamespaceDeclaration::operator==(const QXmlStreamNamespaceDeclaration &other) - - -static void _init_f_operator_eq__eq__c4354 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c4354 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QXmlStreamNamespaceDeclaration &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QXmlStreamNamespaceDeclaration *)cls)->operator== (arg1)); -} - - namespace gsi { @@ -118,12 +86,13 @@ static gsi::Methods methods_QXmlStreamNamespaceDeclaration () { gsi::Methods methods; methods += new qt_gsi::GenericStaticMethod ("new", "@brief Constructor QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration()\nThis method creates an object of class QXmlStreamNamespaceDeclaration.", &_init_ctor_QXmlStreamNamespaceDeclaration_0, &_call_ctor_QXmlStreamNamespaceDeclaration_0); methods += new qt_gsi::GenericStaticMethod ("new", "@brief Constructor QXmlStreamNamespaceDeclaration::QXmlStreamNamespaceDeclaration(const QString &prefix, const QString &namespaceUri)\nThis method creates an object of class QXmlStreamNamespaceDeclaration.", &_init_ctor_QXmlStreamNamespaceDeclaration_3942, &_call_ctor_QXmlStreamNamespaceDeclaration_3942); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QXmlStreamNamespaceDeclaration::operator!=(const QXmlStreamNamespaceDeclaration &other)\n", true, &_init_f_operator_excl__eq__c4354, &_call_f_operator_excl__eq__c4354); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QXmlStreamNamespaceDeclaration::operator==(const QXmlStreamNamespaceDeclaration &other)\n", true, &_init_f_operator_eq__eq__c4354, &_call_f_operator_eq__eq__c4354); return methods; } gsi::Class decl_QXmlStreamNamespaceDeclaration ("QtCore", "QXmlStreamNamespaceDeclaration", + gsi::method_ext("==", &QXmlStreamNamespaceDeclaration_operator_eq, gsi::arg ("other"), "@brief Method bool QXmlStreamNamespaceDeclaration::operator==(const QXmlStreamNamespaceDeclaration &) const") + + gsi::method_ext("!=", &QXmlStreamNamespaceDeclaration_operator_ne, gsi::arg ("other"), "@brief Method bool QXmlStreamNamespaceDeclaration::operator!=(const QXmlStreamNamespaceDeclaration &) const") ++ methods_QXmlStreamNamespaceDeclaration (), "@qt\n@brief Binding of QXmlStreamNamespaceDeclaration"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQXmlStreamNotationDeclaration.cc b/src/gsiqt/qt6/QtCore/gsiDeclQXmlStreamNotationDeclaration.cc index eaced5dab..fa48b4194 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQXmlStreamNotationDeclaration.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQXmlStreamNotationDeclaration.cc @@ -34,6 +34,12 @@ // ----------------------------------------------------------------------- // class QXmlStreamNotationDeclaration + static bool QXmlStreamNotationDeclaration_operator_eq(const QXmlStreamNotationDeclaration *a, const QXmlStreamNotationDeclaration &b) { + return *a == b; + } + static bool QXmlStreamNotationDeclaration_operator_ne(const QXmlStreamNotationDeclaration *a, const QXmlStreamNotationDeclaration &b) { + return !(*a == b); + } // Constructor QXmlStreamNotationDeclaration::QXmlStreamNotationDeclaration() @@ -50,44 +56,6 @@ static void _call_ctor_QXmlStreamNotationDeclaration_0 (const qt_gsi::GenericSta } -// bool QXmlStreamNotationDeclaration::operator!=(const QXmlStreamNotationDeclaration &other) - - -static void _init_f_operator_excl__eq__c4289 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c4289 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QXmlStreamNotationDeclaration &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QXmlStreamNotationDeclaration *)cls)->operator!= (arg1)); -} - - -// bool QXmlStreamNotationDeclaration::operator==(const QXmlStreamNotationDeclaration &other) - - -static void _init_f_operator_eq__eq__c4289 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c4289 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QXmlStreamNotationDeclaration &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QXmlStreamNotationDeclaration *)cls)->operator== (arg1)); -} - - namespace gsi { @@ -95,12 +63,13 @@ namespace gsi static gsi::Methods methods_QXmlStreamNotationDeclaration () { gsi::Methods methods; methods += new qt_gsi::GenericStaticMethod ("new", "@brief Constructor QXmlStreamNotationDeclaration::QXmlStreamNotationDeclaration()\nThis method creates an object of class QXmlStreamNotationDeclaration.", &_init_ctor_QXmlStreamNotationDeclaration_0, &_call_ctor_QXmlStreamNotationDeclaration_0); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QXmlStreamNotationDeclaration::operator!=(const QXmlStreamNotationDeclaration &other)\n", true, &_init_f_operator_excl__eq__c4289, &_call_f_operator_excl__eq__c4289); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QXmlStreamNotationDeclaration::operator==(const QXmlStreamNotationDeclaration &other)\n", true, &_init_f_operator_eq__eq__c4289, &_call_f_operator_eq__eq__c4289); return methods; } gsi::Class decl_QXmlStreamNotationDeclaration ("QtCore", "QXmlStreamNotationDeclaration", + gsi::method_ext("==", &QXmlStreamNotationDeclaration_operator_eq, gsi::arg ("other"), "@brief Method bool QXmlStreamNotationDeclaration::operator==(const QXmlStreamNotationDeclaration &) const") + + gsi::method_ext("!=", &QXmlStreamNotationDeclaration_operator_ne, gsi::arg ("other"), "@brief Method bool QXmlStreamNotationDeclaration::operator!=(const QXmlStreamNotationDeclaration &) const") ++ methods_QXmlStreamNotationDeclaration (), "@qt\n@brief Binding of QXmlStreamNotationDeclaration"); From 7d1ddd95df66a77a5cc20afe041410e7fd084f56 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Feb 2025 22:38:29 +0100 Subject: [PATCH 023/392] More patches --- scripts/mkqtdecl6/mkqtdecl.conf | 70 +++++++++++- .../qt6/QtCore/gsiDeclQJsonArray_Iterator.cc | 100 ++++-------------- .../qt6/QtCore/gsiDeclQJsonObject_Iterator.cc | 100 ++++-------------- src/gsiqt/qt6/QtCore/gsiDeclQModelIndex.cc | 26 +---- .../QtCore/gsiDeclQPersistentModelIndex.cc | 26 +---- .../qt6/QtCore/gsiDeclQStringConverterBase.cc | 16 --- src/gsiqt/qt6/QtCore/gsiDeclQUrl.cc | 26 +---- 7 files changed, 116 insertions(+), 248 deletions(-) diff --git a/scripts/mkqtdecl6/mkqtdecl.conf b/scripts/mkqtdecl6/mkqtdecl.conf index 37672b679..427141e2b 100644 --- a/scripts/mkqtdecl6/mkqtdecl.conf +++ b/scripts/mkqtdecl6/mkqtdecl.conf @@ -35,6 +35,65 @@ CODE DECL end +def add_native_operator_neqlt(engine, cls) + cls_id = cls.gsub("::", "_") + engine.drop_method cls, /::operator\s*==/ + engine.drop_method cls, /::operator\s*!=/ + engine.drop_method cls, /::operator\s*/ + engine.drop_method cls, /::operator\s*>=/ + engine.add_native_impl(cls, <<"CODE", <<"DECL") + static bool #{cls_id}_operator_eq(const #{cls} *a, const #{cls} &b) { + return *a == b; + } + static bool #{cls_id}_operator_ne(const #{cls} *a, const #{cls} &b) { + return *a != b; + } + static bool #{cls_id}_operator_le(const #{cls} *a, const #{cls} &b) { + return *a <= b; + } + static bool #{cls_id}_operator_lt(const #{cls} *a, const #{cls} &b) { + return *a < b; + } + static bool #{cls_id}_operator_ge(const #{cls} *a, const #{cls} &b) { + return *a >= b; + } + static bool #{cls_id}_operator_gt(const #{cls} *a, const #{cls} &b) { + return *a > b; + } +CODE + gsi::method_ext("==", &#{cls_id}_operator_eq, gsi::arg ("other"), "@brief Method bool #{cls}::operator==(const #{cls} &) const") + + gsi::method_ext("!=", &#{cls_id}_operator_ne, gsi::arg ("other"), "@brief Method bool #{cls}::operator!=(const #{cls} &) const") + + gsi::method_ext("<=", &#{cls_id}_operator_le, gsi::arg ("other"), "@brief Method bool #{cls}::operator<=(const #{cls} &) const") + + gsi::method_ext("<", &#{cls_id}_operator_lt, gsi::arg ("other"), "@brief Method bool #{cls}::operator<(const #{cls} &) const") + + gsi::method_ext(">=", &#{cls_id}_operator_ge, gsi::arg ("other"), "@brief Method bool #{cls}::operator>=(const #{cls} &) const") + + gsi::method_ext(">", &#{cls_id}_operator_gt, gsi::arg ("other"), "@brief Method bool #{cls}::operator>(const #{cls} &) const") +DECL +end + # -------------------------------------------------------------- # all modules @@ -495,20 +554,20 @@ add_native_operator_neq(self, "QDir") add_native_operator_neq(self, "QFileInfo") add_native_operator_neq(self, "QItemSelectionRange") add_native_operator_neq(self, "QJsonArray") -add_native_operator_neq(self, "QJsonArray::iterator") +add_native_operator_cmp(self, "QJsonArray::iterator") add_native_operator_neq(self, "QJsonDocument") add_native_operator_neq(self, "QJsonObject") -add_native_operator_neq(self, "QJsonObject::iterator") +add_native_operator_cmp(self, "QJsonObject::iterator") add_native_operator_neq(self, "QJsonValue") add_native_operator_neq(self, "QJsonValueRef") add_native_operator_neq(self, "QLine") add_native_operator_neq(self, "QLineF") add_native_operator_neq(self, "QMimeType") -add_native_operator_neq(self, "QModelIndex") -add_native_operator_neq(self, "QPersistentModelIndex") +add_native_operator_neqlt(self, "QModelIndex") +add_native_operator_neqlt(self, "QPersistentModelIndex") add_native_operator_neq(self, "QProcessEnvironment") add_native_operator_neq(self, "QRegularExpression") -add_native_operator_neq(self, "QUrl") +add_native_operator_neqlt(self, "QUrl") add_native_operator_neq(self, "QUrlQuery") add_native_operator_neq(self, "QXmlStreamAttribute") add_native_operator_neq(self, "QXmlStreamEntityDeclaration") @@ -593,6 +652,7 @@ no_default_ctor "QModelRoleData" no_default_ctor "QPartialOrdering" no_default_ctor "QOperatingSystemVersion" no_default_ctor "QStringConverter" +no_default_ctor "QStringConverterBase" drop_method "QMessageLogger", /QMessageLogger::critical.*\.\.\./ # does not support ... drop_method "QMessageLogger", /QMessageLogger::debug.*\.\.\./ # does not support ... diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQJsonArray_Iterator.cc b/src/gsiqt/qt6/QtCore/gsiDeclQJsonArray_Iterator.cc index f164892b4..7ac46db3c 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQJsonArray_Iterator.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQJsonArray_Iterator.cc @@ -40,7 +40,19 @@ return *a == b; } static bool QJsonArray_iterator_operator_ne(const QJsonArray::iterator *a, const QJsonArray::iterator &b) { - return !(*a == b); + return *a != b; + } + static bool QJsonArray_iterator_operator_le(const QJsonArray::iterator *a, const QJsonArray::iterator &b) { + return *a <= b; + } + static bool QJsonArray_iterator_operator_lt(const QJsonArray::iterator *a, const QJsonArray::iterator &b) { + return *a < b; + } + static bool QJsonArray_iterator_operator_ge(const QJsonArray::iterator *a, const QJsonArray::iterator &b) { + return *a >= b; + } + static bool QJsonArray_iterator_operator_gt(const QJsonArray::iterator *a, const QJsonArray::iterator &b) { + return *a > b; } // Constructor QJsonArray::iterator::iterator() @@ -292,44 +304,6 @@ static void _call_f_operator_minus__gt__c0 (const qt_gsi::GenericMethod * /*decl } -// bool QJsonArray::iterator::operator<(const QJsonArray::iterator &other) - - -static void _init_f_operator_lt__c3305 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_lt__c3305 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonArray::iterator &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonArray::iterator *)cls)->operator< (arg1)); -} - - -// bool QJsonArray::iterator::operator<=(const QJsonArray::iterator &other) - - -static void _init_f_operator_lt__eq__c3305 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_lt__eq__c3305 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonArray::iterator &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonArray::iterator *)cls)->operator<= (arg1)); -} - - // QJsonArray::iterator &QJsonArray::iterator::operator=(const QJsonArray::iterator &other) @@ -349,44 +323,6 @@ static void _call_f_operator_eq__3305 (const qt_gsi::GenericMethod * /*decl*/, v } -// bool QJsonArray::iterator::operator>(const QJsonArray::iterator &other) - - -static void _init_f_operator_gt__c3305 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_gt__c3305 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonArray::iterator &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonArray::iterator *)cls)->operator> (arg1)); -} - - -// bool QJsonArray::iterator::operator>=(const QJsonArray::iterator &other) - - -static void _init_f_operator_gt__eq__c3305 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_gt__eq__c3305 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonArray::iterator &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonArray::iterator *)cls)->operator>= (arg1)); -} - - // QJsonValueRef QJsonArray::iterator::operator[](qsizetype j) @@ -426,18 +362,18 @@ static gsi::Methods methods_QJsonArray_Iterator () { methods += new qt_gsi::GenericMethod ("--", "@brief Method QJsonArray::iterator QJsonArray::iterator::operator--(int)\n", false, &_init_f_operator_minus__minus__767, &_call_f_operator_minus__minus__767); methods += new qt_gsi::GenericMethod ("-=", "@brief Method QJsonArray::iterator &QJsonArray::iterator::operator-=(qsizetype j)\n", false, &_init_f_operator_minus__eq__1442, &_call_f_operator_minus__eq__1442); methods += new qt_gsi::GenericMethod ("->", "@brief Method QJsonValueRef *QJsonArray::iterator::operator->()\n", true, &_init_f_operator_minus__gt__c0, &_call_f_operator_minus__gt__c0); - methods += new qt_gsi::GenericMethod ("<", "@brief Method bool QJsonArray::iterator::operator<(const QJsonArray::iterator &other)\n", true, &_init_f_operator_lt__c3305, &_call_f_operator_lt__c3305); - methods += new qt_gsi::GenericMethod ("<=", "@brief Method bool QJsonArray::iterator::operator<=(const QJsonArray::iterator &other)\n", true, &_init_f_operator_lt__eq__c3305, &_call_f_operator_lt__eq__c3305); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QJsonArray::iterator &QJsonArray::iterator::operator=(const QJsonArray::iterator &other)\n", false, &_init_f_operator_eq__3305, &_call_f_operator_eq__3305); - methods += new qt_gsi::GenericMethod (">", "@brief Method bool QJsonArray::iterator::operator>(const QJsonArray::iterator &other)\n", true, &_init_f_operator_gt__c3305, &_call_f_operator_gt__c3305); - methods += new qt_gsi::GenericMethod (">=", "@brief Method bool QJsonArray::iterator::operator>=(const QJsonArray::iterator &other)\n", true, &_init_f_operator_gt__eq__c3305, &_call_f_operator_gt__eq__c3305); methods += new qt_gsi::GenericMethod ("[]", "@brief Method QJsonValueRef QJsonArray::iterator::operator[](qsizetype j)\n", true, &_init_f_operator_index__c1442, &_call_f_operator_index__c1442); return methods; } gsi::Class decl_QJsonArray_Iterator ("QtCore", "QJsonArray_Iterator", gsi::method_ext("==", &QJsonArray_iterator_operator_eq, gsi::arg ("other"), "@brief Method bool QJsonArray::iterator::operator==(const QJsonArray::iterator &) const") + - gsi::method_ext("!=", &QJsonArray_iterator_operator_ne, gsi::arg ("other"), "@brief Method bool QJsonArray::iterator::operator!=(const QJsonArray::iterator &) const") + gsi::method_ext("!=", &QJsonArray_iterator_operator_ne, gsi::arg ("other"), "@brief Method bool QJsonArray::iterator::operator!=(const QJsonArray::iterator &) const") + + gsi::method_ext("<=", &QJsonArray_iterator_operator_le, gsi::arg ("other"), "@brief Method bool QJsonArray::iterator::operator<=(const QJsonArray::iterator &) const") + + gsi::method_ext("<", &QJsonArray_iterator_operator_lt, gsi::arg ("other"), "@brief Method bool QJsonArray::iterator::operator<(const QJsonArray::iterator &) const") + + gsi::method_ext(">=", &QJsonArray_iterator_operator_ge, gsi::arg ("other"), "@brief Method bool QJsonArray::iterator::operator>=(const QJsonArray::iterator &) const") + + gsi::method_ext(">", &QJsonArray_iterator_operator_gt, gsi::arg ("other"), "@brief Method bool QJsonArray::iterator::operator>(const QJsonArray::iterator &) const") + methods_QJsonArray_Iterator (), "@qt\n@brief Binding of QJsonArray::iterator"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQJsonObject_Iterator.cc b/src/gsiqt/qt6/QtCore/gsiDeclQJsonObject_Iterator.cc index 83388aca0..6665c2325 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQJsonObject_Iterator.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQJsonObject_Iterator.cc @@ -40,7 +40,19 @@ return *a == b; } static bool QJsonObject_iterator_operator_ne(const QJsonObject::iterator *a, const QJsonObject::iterator &b) { - return !(*a == b); + return *a != b; + } + static bool QJsonObject_iterator_operator_le(const QJsonObject::iterator *a, const QJsonObject::iterator &b) { + return *a <= b; + } + static bool QJsonObject_iterator_operator_lt(const QJsonObject::iterator *a, const QJsonObject::iterator &b) { + return *a < b; + } + static bool QJsonObject_iterator_operator_ge(const QJsonObject::iterator *a, const QJsonObject::iterator &b) { + return *a >= b; + } + static bool QJsonObject_iterator_operator_gt(const QJsonObject::iterator *a, const QJsonObject::iterator &b) { + return *a > b; } // Constructor QJsonObject::iterator::iterator() @@ -307,44 +319,6 @@ static void _call_f_operator_minus__gt__c0 (const qt_gsi::GenericMethod * /*decl } -// bool QJsonObject::iterator::operator<(const QJsonObject::iterator &other) - - -static void _init_f_operator_lt__c3393 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_lt__c3393 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonObject::iterator &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonObject::iterator *)cls)->operator< (arg1)); -} - - -// bool QJsonObject::iterator::operator<=(const QJsonObject::iterator &other) - - -static void _init_f_operator_lt__eq__c3393 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_lt__eq__c3393 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonObject::iterator &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonObject::iterator *)cls)->operator<= (arg1)); -} - - // QJsonObject::iterator &QJsonObject::iterator::operator=(const QJsonObject::iterator &other) @@ -364,44 +338,6 @@ static void _call_f_operator_eq__3393 (const qt_gsi::GenericMethod * /*decl*/, v } -// bool QJsonObject::iterator::operator>(const QJsonObject::iterator &other) - - -static void _init_f_operator_gt__c3393 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_gt__c3393 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonObject::iterator &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonObject::iterator *)cls)->operator> (arg1)); -} - - -// bool QJsonObject::iterator::operator>=(const QJsonObject::iterator &other) - - -static void _init_f_operator_gt__eq__c3393 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_gt__eq__c3393 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QJsonObject::iterator &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QJsonObject::iterator *)cls)->operator>= (arg1)); -} - - // const QJsonValueRef QJsonObject::iterator::operator[](qsizetype j) @@ -457,11 +393,7 @@ static gsi::Methods methods_QJsonObject_Iterator () { methods += new qt_gsi::GenericMethod ("--", "@brief Method QJsonObject::iterator QJsonObject::iterator::operator--(int)\n", false, &_init_f_operator_minus__minus__767, &_call_f_operator_minus__minus__767); methods += new qt_gsi::GenericMethod ("-=", "@brief Method QJsonObject::iterator &QJsonObject::iterator::operator-=(qsizetype j)\n", false, &_init_f_operator_minus__eq__1442, &_call_f_operator_minus__eq__1442); methods += new qt_gsi::GenericMethod ("->", "@brief Method QJsonValueRef *QJsonObject::iterator::operator->()\n", true, &_init_f_operator_minus__gt__c0, &_call_f_operator_minus__gt__c0); - methods += new qt_gsi::GenericMethod ("<", "@brief Method bool QJsonObject::iterator::operator<(const QJsonObject::iterator &other)\n", true, &_init_f_operator_lt__c3393, &_call_f_operator_lt__c3393); - methods += new qt_gsi::GenericMethod ("<=", "@brief Method bool QJsonObject::iterator::operator<=(const QJsonObject::iterator &other)\n", true, &_init_f_operator_lt__eq__c3393, &_call_f_operator_lt__eq__c3393); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QJsonObject::iterator &QJsonObject::iterator::operator=(const QJsonObject::iterator &other)\n", false, &_init_f_operator_eq__3393, &_call_f_operator_eq__3393); - methods += new qt_gsi::GenericMethod (">", "@brief Method bool QJsonObject::iterator::operator>(const QJsonObject::iterator &other)\n", true, &_init_f_operator_gt__c3393, &_call_f_operator_gt__c3393); - methods += new qt_gsi::GenericMethod (">=", "@brief Method bool QJsonObject::iterator::operator>=(const QJsonObject::iterator &other)\n", true, &_init_f_operator_gt__eq__c3393, &_call_f_operator_gt__eq__c3393); methods += new qt_gsi::GenericMethod ("[]", "@brief Method const QJsonValueRef QJsonObject::iterator::operator[](qsizetype j)\n", false, &_init_f_operator_index__1442, &_call_f_operator_index__1442); methods += new qt_gsi::GenericMethod ("value", "@brief Method QJsonValueRef QJsonObject::iterator::value()\n", true, &_init_f_value_c0, &_call_f_value_c0); return methods; @@ -469,7 +401,11 @@ static gsi::Methods methods_QJsonObject_Iterator () { gsi::Class decl_QJsonObject_Iterator ("QtCore", "QJsonObject_Iterator", gsi::method_ext("==", &QJsonObject_iterator_operator_eq, gsi::arg ("other"), "@brief Method bool QJsonObject::iterator::operator==(const QJsonObject::iterator &) const") + - gsi::method_ext("!=", &QJsonObject_iterator_operator_ne, gsi::arg ("other"), "@brief Method bool QJsonObject::iterator::operator!=(const QJsonObject::iterator &) const") + gsi::method_ext("!=", &QJsonObject_iterator_operator_ne, gsi::arg ("other"), "@brief Method bool QJsonObject::iterator::operator!=(const QJsonObject::iterator &) const") + + gsi::method_ext("<=", &QJsonObject_iterator_operator_le, gsi::arg ("other"), "@brief Method bool QJsonObject::iterator::operator<=(const QJsonObject::iterator &) const") + + gsi::method_ext("<", &QJsonObject_iterator_operator_lt, gsi::arg ("other"), "@brief Method bool QJsonObject::iterator::operator<(const QJsonObject::iterator &) const") + + gsi::method_ext(">=", &QJsonObject_iterator_operator_ge, gsi::arg ("other"), "@brief Method bool QJsonObject::iterator::operator>=(const QJsonObject::iterator &) const") + + gsi::method_ext(">", &QJsonObject_iterator_operator_gt, gsi::arg ("other"), "@brief Method bool QJsonObject::iterator::operator>(const QJsonObject::iterator &) const") + methods_QJsonObject_Iterator (), "@qt\n@brief Binding of QJsonObject::iterator"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQModelIndex.cc b/src/gsiqt/qt6/QtCore/gsiDeclQModelIndex.cc index 2c4dea35f..db672f30f 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQModelIndex.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQModelIndex.cc @@ -42,6 +42,9 @@ static bool QModelIndex_operator_ne(const QModelIndex *a, const QModelIndex &b) { return !(*a == b); } + static bool QModelIndex_operator_lt(const QModelIndex *a, const QModelIndex &b) { + return *a < b; + } // Constructor QModelIndex::QModelIndex() @@ -202,25 +205,6 @@ static void _call_f_multiData_c2196 (const qt_gsi::GenericMethod * /*decl*/, voi } -// bool QModelIndex::operator<(const QModelIndex &other) - - -static void _init_f_operator_lt__c2395 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_lt__c2395 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QModelIndex &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QModelIndex *)cls)->operator< (arg1)); -} - - // QModelIndex QModelIndex::parent() @@ -327,7 +311,6 @@ static gsi::Methods methods_QModelIndex () { methods += new qt_gsi::GenericMethod ("isValid?", "@brief Method bool QModelIndex::isValid()\n", true, &_init_f_isValid_c0, &_call_f_isValid_c0); methods += new qt_gsi::GenericMethod ("model", "@brief Method const QAbstractItemModel *QModelIndex::model()\n", true, &_init_f_model_c0, &_call_f_model_c0); methods += new qt_gsi::GenericMethod ("multiData", "@brief Method void QModelIndex::multiData(QModelRoleDataSpan roleDataSpan)\n", true, &_init_f_multiData_c2196, &_call_f_multiData_c2196); - methods += new qt_gsi::GenericMethod ("<", "@brief Method bool QModelIndex::operator<(const QModelIndex &other)\n", true, &_init_f_operator_lt__c2395, &_call_f_operator_lt__c2395); methods += new qt_gsi::GenericMethod ("parent", "@brief Method QModelIndex QModelIndex::parent()\n", true, &_init_f_parent_c0, &_call_f_parent_c0); methods += new qt_gsi::GenericMethod ("row", "@brief Method int QModelIndex::row()\n", true, &_init_f_row_c0, &_call_f_row_c0); methods += new qt_gsi::GenericMethod ("sibling", "@brief Method QModelIndex QModelIndex::sibling(int row, int column)\n", true, &_init_f_sibling_c1426, &_call_f_sibling_c1426); @@ -338,7 +321,8 @@ static gsi::Methods methods_QModelIndex () { gsi::Class decl_QModelIndex ("QtCore", "QModelIndex", gsi::method_ext("==", &QModelIndex_operator_eq, gsi::arg ("other"), "@brief Method bool QModelIndex::operator==(const QModelIndex &) const") + - gsi::method_ext("!=", &QModelIndex_operator_ne, gsi::arg ("other"), "@brief Method bool QModelIndex::operator!=(const QModelIndex &) const") + gsi::method_ext("!=", &QModelIndex_operator_ne, gsi::arg ("other"), "@brief Method bool QModelIndex::operator!=(const QModelIndex &) const") + + gsi::method_ext("<", &QModelIndex_operator_lt, gsi::arg ("other"), "@brief Method bool QModelIndex::operator<(const QModelIndex &) const") + methods_QModelIndex (), "@qt\n@brief Binding of QModelIndex"); diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQPersistentModelIndex.cc b/src/gsiqt/qt6/QtCore/gsiDeclQPersistentModelIndex.cc index 7fecee04c..1b412617f 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQPersistentModelIndex.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQPersistentModelIndex.cc @@ -43,6 +43,9 @@ static bool QPersistentModelIndex_operator_ne(const QPersistentModelIndex *a, const QPersistentModelIndex &b) { return !(*a == b); } + static bool QPersistentModelIndex_operator_lt(const QPersistentModelIndex *a, const QPersistentModelIndex &b) { + return *a < b; + } #if QT_VERSION < 0x60000 static const QModelIndex &castToQModelIndex(const QPersistentModelIndex *m) { @@ -252,25 +255,6 @@ static void _call_f_multiData_c2196 (const qt_gsi::GenericMethod * /*decl*/, voi } -// bool QPersistentModelIndex::operator<(const QPersistentModelIndex &other) - - -static void _init_f_operator_lt__c3468 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("other"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_lt__c3468 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QPersistentModelIndex &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QPersistentModelIndex *)cls)->operator< (arg1)); -} - - // QPersistentModelIndex &QPersistentModelIndex::operator=(const QPersistentModelIndex &other) @@ -399,7 +383,6 @@ static gsi::Methods methods_QPersistentModelIndex () { methods += new qt_gsi::GenericMethod ("isValid?", "@brief Method bool QPersistentModelIndex::isValid()\n", true, &_init_f_isValid_c0, &_call_f_isValid_c0); methods += new qt_gsi::GenericMethod ("model", "@brief Method const QAbstractItemModel *QPersistentModelIndex::model()\n", true, &_init_f_model_c0, &_call_f_model_c0); methods += new qt_gsi::GenericMethod ("multiData", "@brief Method void QPersistentModelIndex::multiData(QModelRoleDataSpan roleDataSpan)\n", true, &_init_f_multiData_c2196, &_call_f_multiData_c2196); - methods += new qt_gsi::GenericMethod ("<", "@brief Method bool QPersistentModelIndex::operator<(const QPersistentModelIndex &other)\n", true, &_init_f_operator_lt__c3468, &_call_f_operator_lt__c3468); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QPersistentModelIndex &QPersistentModelIndex::operator=(const QPersistentModelIndex &other)\n", false, &_init_f_operator_eq__3468, &_call_f_operator_eq__3468); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QPersistentModelIndex &QPersistentModelIndex::operator=(const QModelIndex &other)\n", false, &_init_f_operator_eq__2395, &_call_f_operator_eq__2395); methods += new qt_gsi::GenericMethod ("parent", "@brief Method QModelIndex QPersistentModelIndex::parent()\n", true, &_init_f_parent_c0, &_call_f_parent_c0); @@ -411,7 +394,8 @@ static gsi::Methods methods_QPersistentModelIndex () { gsi::Class decl_QPersistentModelIndex ("QtCore", "QPersistentModelIndex", gsi::method_ext("==", &QPersistentModelIndex_operator_eq, gsi::arg ("other"), "@brief Method bool QPersistentModelIndex::operator==(const QPersistentModelIndex &) const") + - gsi::method_ext("!=", &QPersistentModelIndex_operator_ne, gsi::arg ("other"), "@brief Method bool QPersistentModelIndex::operator!=(const QPersistentModelIndex &) const") + gsi::method_ext("!=", &QPersistentModelIndex_operator_ne, gsi::arg ("other"), "@brief Method bool QPersistentModelIndex::operator!=(const QPersistentModelIndex &) const") + + gsi::method_ext("<", &QPersistentModelIndex_operator_lt, gsi::arg ("other"), "@brief Method bool QPersistentModelIndex::operator<(const QPersistentModelIndex &) const") + gsi::method_ext("castToQModelIndex", &castToQModelIndex, "@brief Binding for \"operator const QModelIndex &\".") + diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQStringConverterBase.cc b/src/gsiqt/qt6/QtCore/gsiDeclQStringConverterBase.cc index e43180127..52b17a1b8 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQStringConverterBase.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQStringConverterBase.cc @@ -35,28 +35,12 @@ // ----------------------------------------------------------------------- // class QStringConverterBase -// Constructor QStringConverterBase::QStringConverterBase() - - -static void _init_ctor_QStringConverterBase_0 (qt_gsi::GenericStaticMethod *decl) -{ - decl->set_return_new (); -} - -static void _call_ctor_QStringConverterBase_0 (const qt_gsi::GenericStaticMethod * /*decl*/, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - ret.write (new QStringConverterBase ()); -} - - namespace gsi { static gsi::Methods methods_QStringConverterBase () { gsi::Methods methods; - methods += new qt_gsi::GenericStaticMethod ("new", "@brief Constructor QStringConverterBase::QStringConverterBase()\nThis method creates an object of class QStringConverterBase.", &_init_ctor_QStringConverterBase_0, &_call_ctor_QStringConverterBase_0); return methods; } diff --git a/src/gsiqt/qt6/QtCore/gsiDeclQUrl.cc b/src/gsiqt/qt6/QtCore/gsiDeclQUrl.cc index 8f0302a8b..da7e2d095 100644 --- a/src/gsiqt/qt6/QtCore/gsiDeclQUrl.cc +++ b/src/gsiqt/qt6/QtCore/gsiDeclQUrl.cc @@ -41,6 +41,9 @@ static bool QUrl_operator_ne(const QUrl *a, const QUrl &b) { return !(*a == b); } + static bool QUrl_operator_lt(const QUrl *a, const QUrl &b) { + return *a < b; + } // Constructor QUrl::QUrl() @@ -386,25 +389,6 @@ static void _call_f_matches_c9164 (const qt_gsi::GenericMethod * /*decl*/, void } -// bool QUrl::operator <(const QUrl &url) - - -static void _init_f_operator_lt__c1701 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("url"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_lt__c1701 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QUrl &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QUrl *)cls)->operator < (arg1)); -} - - // QUrl &QUrl::operator =(const QUrl ©) @@ -1222,7 +1206,6 @@ static gsi::Methods methods_QUrl () { methods += new qt_gsi::GenericMethod ("isRelative?", "@brief Method bool QUrl::isRelative()\n", true, &_init_f_isRelative_c0, &_call_f_isRelative_c0); methods += new qt_gsi::GenericMethod ("isValid?", "@brief Method bool QUrl::isValid()\n", true, &_init_f_isValid_c0, &_call_f_isValid_c0); methods += new qt_gsi::GenericMethod ("matches", "@brief Method bool QUrl::matches(const QUrl &url, QUrlTwoFlags options)\n", true, &_init_f_matches_c9164, &_call_f_matches_c9164); - methods += new qt_gsi::GenericMethod ("<", "@brief Method bool QUrl::operator <(const QUrl &url)\n", true, &_init_f_operator_lt__c1701, &_call_f_operator_lt__c1701); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QUrl &QUrl::operator =(const QUrl ©)\n", false, &_init_f_operator_eq__1701, &_call_f_operator_eq__1701); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QUrl &QUrl::operator=(const QString &url)\n", false, &_init_f_operator_eq__2025, &_call_f_operator_eq__2025); methods += new qt_gsi::GenericMethod ("password", "@brief Method QString QUrl::password(QFlags)\n", true, &_init_f_password_c4267, &_call_f_password_c4267); @@ -1267,7 +1250,8 @@ static gsi::Methods methods_QUrl () { gsi::Class decl_QUrl ("QtCore", "QUrl", gsi::method_ext("==", &QUrl_operator_eq, gsi::arg ("other"), "@brief Method bool QUrl::operator==(const QUrl &) const") + - gsi::method_ext("!=", &QUrl_operator_ne, gsi::arg ("other"), "@brief Method bool QUrl::operator!=(const QUrl &) const") + gsi::method_ext("!=", &QUrl_operator_ne, gsi::arg ("other"), "@brief Method bool QUrl::operator!=(const QUrl &) const") + + gsi::method_ext("<", &QUrl_operator_lt, gsi::arg ("other"), "@brief Method bool QUrl::operator<(const QUrl &) const") + methods_QUrl (), "@qt\n@brief Binding of QUrl"); From b9079cdfdc5f8967ad1b1c28081c03959b3e9e61 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Feb 2025 23:36:30 +0100 Subject: [PATCH 024/392] First proposal for a feature to configure lazy PCell evaluation - The PCell page gets an option menu instead of "show parameter names" - That option goes into that menu - In addition, there is a "lazy PCell evaluation" submenu with the modes: * As requested by PCell (like now) * Never * Always --- src/edt/edt/edtConfig.cc | 1 + src/edt/edt/edtConfig.h | 1 + src/edt/edt/edtPCellParametersPage.cc | 100 ++++++++++++++++++++++---- src/edt/edt/edtPCellParametersPage.h | 8 ++- 4 files changed, 95 insertions(+), 15 deletions(-) diff --git a/src/edt/edt/edtConfig.cc b/src/edt/edt/edtConfig.cc index 8d2c5203a..255b9ccbd 100644 --- a/src/edt/edt/edtConfig.cc +++ b/src/edt/edt/edtConfig.cc @@ -62,6 +62,7 @@ std::string cfg_edit_hier_copy_mode ("edit-hier-copy-mode"); std::string cfg_edit_show_shapes_of_instances ("edit-show-shapes-of-instances"); std::string cfg_edit_max_shapes_of_instances ("edit-max-shapes-of-instances"); std::string cfg_edit_pcell_show_parameter_names ("edit-pcell-show-parameter-names"); +std::string cfg_edit_pcell_lazy_eval_mode ("edit-pcell-lazy-eval-mode"); std::string cfg_edit_global_grid ("grid-micron"); std::string cfg_edit_combine_mode ("combine-mode"); diff --git a/src/edt/edt/edtConfig.h b/src/edt/edt/edtConfig.h index b4a6859ed..97964a9e0 100644 --- a/src/edt/edt/edtConfig.h +++ b/src/edt/edt/edtConfig.h @@ -70,6 +70,7 @@ extern EDT_PUBLIC std::string cfg_edit_inst_column_y; extern EDT_PUBLIC std::string cfg_edit_inst_place_origin; extern EDT_PUBLIC std::string cfg_edit_top_level_selection; extern EDT_PUBLIC std::string cfg_edit_pcell_show_parameter_names; +extern EDT_PUBLIC std::string cfg_edit_pcell_lazy_eval_mode; extern EDT_PUBLIC std::string cfg_edit_hier_copy_mode; extern EDT_PUBLIC std::string cfg_edit_combine_mode; diff --git a/src/edt/edt/edtPCellParametersPage.cc b/src/edt/edt/edtPCellParametersPage.cc index d68c0b319..9ff61cc0f 100644 --- a/src/edt/edt/edtPCellParametersPage.cc +++ b/src/edt/edt/edtPCellParametersPage.cc @@ -154,10 +154,11 @@ static void set_value (const db::PCellParameterDeclaration &p, QWidget *widget, } PCellParametersPage::PCellParametersPage (QWidget *parent, lay::Dispatcher *dispatcher, bool dense) - : QFrame (parent), mp_dispatcher (dispatcher), m_dense (dense), m_show_parameter_names (false), dm_parameter_changed (this, &PCellParametersPage::do_parameter_changed) + : QFrame (parent), mp_dispatcher (dispatcher), m_dense (dense), m_show_parameter_names (false), m_lazy_evaluation (-1), dm_parameter_changed (this, &PCellParametersPage::do_parameter_changed) { if (mp_dispatcher) { mp_dispatcher->config_get (cfg_edit_pcell_show_parameter_names, m_show_parameter_names); + mp_dispatcher->config_get (cfg_edit_pcell_lazy_eval_mode, m_lazy_evaluation); } init (); @@ -236,28 +237,99 @@ PCellParametersPage::init () error_frame_layout->setColumnStretch (2, 1); - QFrame *show_parameter_names_frame = new QFrame (this); - show_parameter_names_frame->setFrameShape (QFrame::NoFrame); - frame_layout->addWidget (show_parameter_names_frame, 3, 0, 1, 1); + QFrame *options_frame = new QFrame (this); + options_frame->setFrameShape (QFrame::NoFrame); + frame_layout->addWidget (options_frame, 3, 0, 1, 1); - QHBoxLayout *show_parameter_names_frame_layout = new QHBoxLayout (show_parameter_names_frame); - show_parameter_names_frame->setLayout (show_parameter_names_frame_layout); + QHBoxLayout *options_frame_layout = new QHBoxLayout (options_frame); + options_frame->setLayout (options_frame_layout); if (m_dense) { - show_parameter_names_frame_layout->setContentsMargins (4, 4, 4, 4); + options_frame_layout->setContentsMargins (4, 4, 4, 4); } - mp_show_parameter_names_cb = new QCheckBox (show_parameter_names_frame); - mp_show_parameter_names_cb->setText (tr ("Show parameter names")); - mp_show_parameter_names_cb->setChecked (m_show_parameter_names); - show_parameter_names_frame_layout->addWidget (mp_show_parameter_names_cb); + QToolButton *dot_menu_button = new QToolButton (options_frame); + dot_menu_button->setText (tr ("Options ")); + dot_menu_button->setAutoRaise (true); + dot_menu_button->setPopupMode (QToolButton::InstantPopup); + dot_menu_button->setToolButtonStyle (Qt::ToolButtonTextOnly); + options_frame_layout->addWidget (dot_menu_button); + options_frame_layout->addStretch (); - connect (mp_show_parameter_names_cb, SIGNAL (clicked (bool)), this, SLOT (show_parameter_names (bool))); + QMenu *dot_menu = new QMenu (dot_menu_button); + dot_menu_button->setMenu (dot_menu); + mp_show_parameter_names_action = new QAction (dot_menu); + dot_menu->addAction (mp_show_parameter_names_action); + mp_show_parameter_names_action->setText (tr ("Show parameter names")); + mp_show_parameter_names_action->setCheckable (true); + mp_show_parameter_names_action->setChecked (m_show_parameter_names); + connect (mp_show_parameter_names_action, SIGNAL (triggered (bool)), this, SLOT (show_parameter_names (bool))); + + QMenu *lazy_eval_menu = new QMenu (dot_menu); + lazy_eval_menu->setTitle (tr ("Lazy PCell evaluation")); + dot_menu->addMenu (lazy_eval_menu); + + mp_auto_lazy_eval_action = new QAction (lazy_eval_menu); + lazy_eval_menu->addAction (mp_auto_lazy_eval_action); + mp_auto_lazy_eval_action->setText (tr ("As requested by PCell")); + mp_auto_lazy_eval_action->setCheckable (true); + mp_auto_lazy_eval_action->setChecked (m_lazy_evaluation < 0); + connect (mp_auto_lazy_eval_action, SIGNAL (triggered ()), this, SLOT (lazy_eval_mode_slot ())); + + mp_always_lazy_eval_action = new QAction (lazy_eval_menu); + lazy_eval_menu->addAction (mp_always_lazy_eval_action); + mp_always_lazy_eval_action->setText (tr ("Always")); + mp_always_lazy_eval_action->setCheckable (true); + mp_always_lazy_eval_action->setChecked (m_lazy_evaluation > 0); + connect (mp_always_lazy_eval_action, SIGNAL (triggered ()), this, SLOT (lazy_eval_mode_slot ())); + + mp_never_lazy_eval_action = new QAction (lazy_eval_menu); + lazy_eval_menu->addAction (mp_never_lazy_eval_action); + mp_never_lazy_eval_action->setText (tr ("Never")); + mp_never_lazy_eval_action->setCheckable (true); + mp_never_lazy_eval_action->setChecked (m_lazy_evaluation == 0); + connect (mp_never_lazy_eval_action, SIGNAL (triggered ()), this, SLOT (lazy_eval_mode_slot ())); } bool PCellParametersPage::lazy_evaluation () { - return mp_pcell_decl.get () && mp_pcell_decl->wants_lazy_evaluation (); + if (m_lazy_evaluation < 0) { + return mp_pcell_decl.get () && mp_pcell_decl->wants_lazy_evaluation (); + } else { + return m_lazy_evaluation > 0; + } +} + +void +PCellParametersPage::lazy_eval_mode_slot () +{ + if (sender () == mp_always_lazy_eval_action) { + lazy_eval_mode (1); + } else if (sender () == mp_never_lazy_eval_action) { + lazy_eval_mode (0); + } else if (sender () == mp_auto_lazy_eval_action) { + lazy_eval_mode (-1); + } +} + +void +PCellParametersPage::lazy_eval_mode (int mode) +{ + if (mode == m_lazy_evaluation) { + return; + } + + mp_never_lazy_eval_action->setChecked (mode == 0); + mp_always_lazy_eval_action->setChecked (mode > 0); + mp_auto_lazy_eval_action->setChecked (mode < 0); + + m_lazy_evaluation = mode; + + if (mp_dispatcher) { + mp_dispatcher->config_set (cfg_edit_pcell_lazy_eval_mode, m_lazy_evaluation); + } + + setup (mp_view, m_cv_index, mp_pcell_decl.get (), get_parameters ()); } void @@ -268,7 +340,7 @@ PCellParametersPage::show_parameter_names (bool f) } m_show_parameter_names = f; - mp_show_parameter_names_cb->setChecked (f); + mp_show_parameter_names_action->setChecked (f); if (mp_dispatcher) { mp_dispatcher->config_set (cfg_edit_pcell_show_parameter_names, m_show_parameter_names); diff --git a/src/edt/edt/edtPCellParametersPage.h b/src/edt/edt/edtPCellParametersPage.h index a6c862452..1007a0163 100644 --- a/src/edt/edt/edtPCellParametersPage.h +++ b/src/edt/edt/edtPCellParametersPage.h @@ -144,10 +144,12 @@ signals: public slots: void show_parameter_names (bool f); + void lazy_eval_mode (int); private slots: void parameter_changed (); void update_button_pressed (); + void lazy_eval_mode_slot (); private: lay::Dispatcher *mp_dispatcher; @@ -160,7 +162,10 @@ private: QLabel *mp_changed_icon; QToolButton *mp_update_button; QFrame *mp_error_frame, *mp_update_frame; - QCheckBox *mp_show_parameter_names_cb; + QAction *mp_show_parameter_names_action; + QAction *mp_auto_lazy_eval_action; + QAction *mp_always_lazy_eval_action; + QAction *mp_never_lazy_eval_action; tl::weak_ptr mp_pcell_decl; std::vector m_widgets; std::vector m_icon_widgets; @@ -168,6 +173,7 @@ private: lay::LayoutViewBase *mp_view; int m_cv_index; bool m_dense, m_show_parameter_names; + int m_lazy_evaluation; tl::DeferredMethod dm_parameter_changed; db::ParameterStates m_current_states, m_initial_states; db::ParameterStates m_states; From 50ab16938529458e84ccc25f29b52a9d929ca4c4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Feb 2025 23:41:20 +0100 Subject: [PATCH 025/392] Default for lazy eval mode is 'as requested by PCell' for backward compatibility --- src/edt/edt/edtPlugin.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index 626e9ef08..422fa7b4a 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -142,6 +142,7 @@ void get_inst_options (std::vector < std::pair > &opti options.push_back (std::pair (cfg_edit_inst_column_y, "0.0")); options.push_back (std::pair (cfg_edit_inst_place_origin, "false")); options.push_back (std::pair (cfg_edit_pcell_show_parameter_names, "false")); + options.push_back (std::pair (cfg_edit_pcell_lazy_eval_mode, "-1")); options.push_back (std::pair (cfg_edit_max_shapes_of_instances, "1000")); options.push_back (std::pair (cfg_edit_show_shapes_of_instances, "true")); } From cb0b0baa4a286e472dbe3c94536af5aa9d93bd6d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Feb 2025 18:49:29 +0100 Subject: [PATCH 026/392] Switching to upload-artifact v4 as v3 is deprecated --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index be3b7623e..5b6bfc894 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -76,7 +76,7 @@ jobs: mv ./wheelhouse/.ccache $HOST_CCACHE_DIR ls -la $HOST_CCACHE_DIR ccache -s - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: ./wheelhouse/*.whl @@ -90,7 +90,7 @@ jobs: - name: Build SDist run: pipx run build --sdist - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: dist/*.tar.gz From c437d75a014f9d18830c944175092802fc3e8489 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Feb 2025 21:16:49 +0100 Subject: [PATCH 027/392] [consider merging] Fixing two problems with layer mapping 1. Mapping "[*/*] 100/0:0/0" (for example) created 0/0 two times when the input contains 100/0 and 0/0. Now it is a single layer only 2. The mapping table generated from strings now uses layer indexes from a range that should not collide with existing layer indexes. --- src/db/db/dbStreamLayers.cc | 19 ++++++++++-- src/db/unit_tests/dbStreamLayerTests.cc | 30 +++++++++++++++++++ .../gds2/unit_tests/dbGDS2ReaderTests.cc | 3 +- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/db/db/dbStreamLayers.cc b/src/db/db/dbStreamLayers.cc index c8b1b0f92..c4038549b 100644 --- a/src/db/db/dbStreamLayers.cc +++ b/src/db/db/dbStreamLayers.cc @@ -236,7 +236,19 @@ LayerMap::substitute_placeholder (const db::LayerProperties &p, const std::set::max (); + for (std::map::const_iterator t = m_target_layers.begin (); t != m_target_layers.end (); ++t) { + if (t->second.log_equal (lp_new) && layout.is_valid_layer (t->first)) { + l_new = t->first; + break; + } + } + + if (l_new == std::numeric_limits::max ()) { + l_new = layout.insert_layer (lp_new); + } + map (p, l_new, lp_new); res.insert (l_new); @@ -929,7 +941,10 @@ db::LayerMap LayerMap::from_string_file_format (const std::string &s) { db::LayerMap lm; - unsigned int l = 0; + // NOTE: this should be outside the normal layer index range of a Layout and below + // the space reserved for placeholders. With numbers like this we don't get messed + // up with existing layers. + unsigned int l = std::numeric_limits::max () / 2; int lnr = 0; diff --git a/src/db/unit_tests/dbStreamLayerTests.cc b/src/db/unit_tests/dbStreamLayerTests.cc index 3e3287aee..fc4da9fce 100644 --- a/src/db/unit_tests/dbStreamLayerTests.cc +++ b/src/db/unit_tests/dbStreamLayerTests.cc @@ -577,3 +577,33 @@ TEST(8) EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); } +// mapping of relative target to real target +TEST(9) +{ + db::Layout ly; + db::LayerMap lm; + + EXPECT_EQ (layers_to_string (ly), ""); + unsigned int l0 = ly.insert_layer (db::LayerProperties (0, 0)); + unsigned int l101 = ly.insert_layer (db::LayerProperties (101, 0)); + + unsigned int n = std::numeric_limits::max () / 2; + lm.map_expr ("*/* : */*", n++); + lm.map_expr ("100/0 : 0/0", n++); + + lm.prepare (ly); + + std::pair p; + p = lm.first_logical (db::LayerProperties (100, 0), ly); + EXPECT_EQ (p.first, true); + EXPECT_EQ (p.second, l0); + + p = lm.first_logical (db::LayerProperties (101, 0), ly); + EXPECT_EQ (p.first, true); + EXPECT_EQ (p.second, l101); + + // maps to the target 0/0 + p = lm.first_logical (db::LayerProperties (0, 0), ly); + EXPECT_EQ (p.first, true); + EXPECT_EQ (p.second, l0); +} diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2ReaderTests.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2ReaderTests.cc index 5552c8d3b..d65f9f3a4 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2ReaderTests.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2ReaderTests.cc @@ -417,9 +417,8 @@ TEST(3_AdvancedMapping) } EXPECT_EQ (lm_read.to_string_file_format (), - "1/10 : 1/0\n" + "1/0,10 : 1/0\n" "2/0-1 : 2/0\n" - "1/0 : 1/0\n" "1/1 : 1/1\n" "1/20 : 1/1020\n" "1/21 : 1/1021\n" From 0e4594c0c35292271746548de9c29e2a3bf29e75 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Feb 2025 22:06:56 +0100 Subject: [PATCH 028/392] [consider merging] fixed a potential crash on 'save all' --- src/lay/lay/layMainWindow.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index cda9b8057..7b52b97b9 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -2365,7 +2365,7 @@ MainWindow::cm_save_all () } view (view_index)->save_as (cv_index, fn, om, options, true, m_keep_backups); - add_mru (fn, current_view ()->cellview (cv_index)->tech_name ()); + add_mru (fn, cv->tech_name ()); } From 850a6f56d3703c39ed9057e4ca4f914f4996f1ed Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Feb 2025 23:22:06 +0100 Subject: [PATCH 029/392] [consider merging] avoid a crash during certain queries This query used to crash: "instances of cell .*.* where inst.trans.rot == 2" on a layout with 1 hierarchy level and some cells with rot 180 degree --- src/db/db/dbLayoutQuery.cc | 14 +++++++++----- src/db/unit_tests/dbLayoutQueryTests.cc | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/db/db/dbLayoutQuery.cc b/src/db/db/dbLayoutQuery.cc index d2753b617..c5989b5bb 100644 --- a/src/db/db/dbLayoutQuery.cc +++ b/src/db/db/dbLayoutQuery.cc @@ -1006,15 +1006,19 @@ public: db::Instance inst; if (m_reading) { - inst = mp_parent->sorted_inst_ptr (std::distance (mp_parent->begin_sorted_insts (), m_inst)); + if (mp_parent) { + inst = mp_parent->sorted_inst_ptr (std::distance (mp_parent->begin_sorted_insts (), m_inst)); + } } else { inst = m_i; } - if (m_instance_mode == ArrayInstances) { - v.push (tl::Variant (db::InstElement (inst))); - } else { - v.push (tl::Variant (db::InstElement (inst, m_array_iter))); + if (! inst.is_null ()) { + if (m_instance_mode == ArrayInstances) { + v.push (tl::Variant (db::InstElement (inst))); + } else { + v.push (tl::Variant (db::InstElement (inst, m_array_iter))); + } } return true; diff --git a/src/db/unit_tests/dbLayoutQueryTests.cc b/src/db/unit_tests/dbLayoutQueryTests.cc index e93171e89..77f091cd2 100644 --- a/src/db/unit_tests/dbLayoutQueryTests.cc +++ b/src/db/unit_tests/dbLayoutQueryTests.cc @@ -1578,3 +1578,20 @@ TEST(64) EXPECT_EQ (s, "(0.01,-0.02),(-0.01,0.02),(-0.01,0.02),(-0.01,0.02),(0.01,-0.02),(-0.01,0.02)"); } } + +TEST(65) +{ + db::Layout g; + init_layout (g); + + { + db::LayoutQuery q ("instances of cell .*.* where inst.trans.rot == 0"); + db::LayoutQueryIterator iq (q, &g); + std::string s = q2s_var (iq, "data"); + EXPECT_EQ (s, "nil,nil"); + s = q2s_var (iq, "cell_name"); + EXPECT_EQ (s, "c1,c5x"); + s = q2s_var (iq, "inst_elements"); + EXPECT_EQ (s, "(cell_index=0 r0 *1 10,-20),(cell_index=4 r0 *1 10,-20)"); + } +} From d21cba7321a07bbcd77ba347b27fe810f7917902 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Mar 2025 21:39:07 +0000 Subject: [PATCH 030/392] Bump pypa/cibuildwheel from 2.22.0 to 2.23.0 Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.22.0 to 2.23.0. - [Release notes](https://github.com/pypa/cibuildwheel/releases) - [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md) - [Commits](https://github.com/pypa/cibuildwheel/compare/v2.22.0...v2.23.0) --- updated-dependencies: - dependency-name: pypa/cibuildwheel dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index de811ab93..96f3106dc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -56,7 +56,7 @@ jobs: HOST_CCACHE_DIR="$(ccache -k cache_dir)" mkdir -p $HOST_CCACHE_DIR - name: Build wheels # check https://cibuildwheel.readthedocs.io/en/stable/setup/#github-actions - uses: pypa/cibuildwheel@v2.22.0 + uses: pypa/cibuildwheel@v2.23.0 # to supply options, put them in 'env', like: # env: # CIBW_SOME_OPTION: value From 79262028ff39b6c1c3c796217ae932472da032dc Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 1 Mar 2025 23:18:29 +0100 Subject: [PATCH 031/392] [consider merging] Fixed a bug when returning a Region into a layout with cells unselected. --- src/db/db/dbCellMapping.cc | 16 ++- src/db/db/dbCellMapping.h | 5 + src/db/db/dbDeepShapeStore.cc | 122 +++++++++++++-------- src/db/unit_tests/dbDeepShapeStoreTests.cc | 35 ++++++ testdata/algo/dss_bug.gds | Bin 0 -> 920 bytes testdata/algo/dss_bug_au.gds | Bin 0 -> 1208 bytes 6 files changed, 130 insertions(+), 48 deletions(-) create mode 100644 testdata/algo/dss_bug.gds create mode 100644 testdata/algo/dss_bug_au.gds diff --git a/src/db/db/dbCellMapping.cc b/src/db/db/dbCellMapping.cc index bc2d20223..b3bdd4e6a 100644 --- a/src/db/db/dbCellMapping.cc +++ b/src/db/db/dbCellMapping.cc @@ -284,8 +284,17 @@ std::vector CellMapping::source_cells () const return s; } +std::vector CellMapping::target_cells () const +{ + std::vector s; + s.reserve (m_b2a_mapping.size ()); + for (iterator m = begin (); m != end (); ++m) { + s.push_back (m->second); + } + return s; +} -void +void CellMapping::create_single_mapping (const db::Layout & /*layout_a*/, db::cell_index_type cell_index_a, const db::Layout & /*layout_b*/, db::cell_index_type cell_index_b) { clear (); @@ -372,7 +381,7 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout & if (! new_cells.empty ()) { // Note: this avoids frequent cell index table rebuilds if source and target layout are identical - layout_a.start_changes (); + db::LayoutLocker locker (&layout_a); // Create instances for the new cells in layout A according to their instantiation in layout B double mag = layout_b.dbu () / layout_a.dbu (); @@ -403,9 +412,6 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout & } - // Note: must be there because of start_changes - layout_a.end_changes (); - } } diff --git a/src/db/db/dbCellMapping.h b/src/db/db/dbCellMapping.h index 42e28fab6..4686e5f16 100644 --- a/src/db/db/dbCellMapping.h +++ b/src/db/db/dbCellMapping.h @@ -206,6 +206,11 @@ public: */ std::vector source_cells () const; + /** + * @brief Gets the target cells + */ + std::vector target_cells () const; + /** * @brief Access to the mapping table */ diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index 361c504f6..6f7dc204f 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -1166,62 +1166,69 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::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->second.is_valid (*into_layout, *source_layout)) { + if (cm != m_delivery_mapping_cache.end () && cm->second.is_valid (*into_layout, *source_layout)) { + return cm->second; + } - cm = m_delivery_mapping_cache.insert (std::make_pair (key, CellMappingWithGenerationIds ())).first; - cm->second.clear (); + cm = m_delivery_mapping_cache.insert (std::make_pair (key, CellMappingWithGenerationIds ())).first; + cm->second.clear (); - // collects the cell mappings we skip because they are variants (variant building or box variants) - std::map cm_skipped_variants; + // Not found in cache - compute a fresh mapping - if (into_layout == original_builder.source ().layout () && &into_layout->cell (into_cell) == original_builder.source ().top_cell () && original_builder.source ().global_trans ().is_unity ()) { + // collects the cell mappings we skip because they are variants (variant building or box variants) + std::map cm_skipped_variants; - // 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. + if (into_layout == original_builder.source ().layout () && &into_layout->cell (into_cell) == original_builder.source ().top_cell () && original_builder.source ().global_trans ().is_unity ()) { - for (HierarchyBuilder::cell_map_type::const_iterator m = original_builder.begin_cell_map (); m != original_builder.end_cell_map (); ) { + // 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. - HierarchyBuilder::cell_map_type::const_iterator mm = m; + for (HierarchyBuilder::cell_map_type::const_iterator m = original_builder.begin_cell_map (); m != original_builder.end_cell_map (); ) { + + 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.original_cell == m->first.original_cell) { + // we have cell (box) variants and cannot simply map ++mm; - bool skip = original_builder.is_variant (m->second); // skip variant cells - while (mm != original_builder.end_cell_map () && mm->first.original_cell == m->first.original_cell) { - // we have cell (box) variants and cannot simply map - ++mm; - skip = true; - } - - if (! skip) { - cm->second.map (m->second, m->first.original_cell); - } else { - for (HierarchyBuilder::cell_map_type::const_iterator n = m; n != mm; ++n) { - tl_assert (cm_skipped_variants.find (n->second) == cm_skipped_variants.end ()); - cm_skipped_variants [n->second] = n->first; - } - } - - m = mm; - + skip = true; } - } else if (into_layout->cells () == 1) { + if (! skip) { + cm->second.map (m->second, m->first.original_cell); + } else { + for (HierarchyBuilder::cell_map_type::const_iterator n = m; n != mm; ++n) { + tl_assert (cm_skipped_variants.find (n->second) == cm_skipped_variants.end ()); + cm_skipped_variants [n->second] = n->first; + } + } - // 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); + m = mm; } - // Add new cells for the variants and (possible) devices which are cells added during the device - // extraction process - std::vector > new_pairs = cm->second.create_missing_mapping2 (*into_layout, *source_layout, source_top, excluded_cells, included_cells); + } 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 + std::vector > new_pairs = cm->second.create_missing_mapping2 (*into_layout, *source_layout, source_top, excluded_cells, included_cells); + + if (! new_pairs.empty ()) { // the variant's originals we are going to delete std::set cells_to_delete; + std::vector > new_variants; // We now need to fix the cell map from the hierarchy builder, so we can import back from the modified layout. // This is in particular important if we created new cells for known variants. @@ -1234,6 +1241,7 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout // create the variant clone in the original layout too and delete this cell VariantsCollectorBase::copy_shapes (*into_layout, np->second, icm->second.original_cell); + new_variants.push_back (std::make_pair (np->second, icm->second.original_cell)); cells_to_delete.insert (icm->second.original_cell); // forget the original cell (now separated into variants) and map the variants back into the @@ -1253,15 +1261,43 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout } - // delete the variant's original cell + if (! new_variants.empty ()) { + + // copy cell instances for the new variants + + // collect the cells that are handled during cell mapping - + // we do not need to take care of them when creating variants, + // but there may be others inside "into_layout" which are + // not present in the DSS and for which we need to copy the + // instances. + std::vector mapped = cm->second.target_cells (); + std::sort (mapped.begin (), mapped.end ()); + + // Copy the variant instances - but only those for cells which are not going to be + // deleted and those not handled by the cell mapping object. + for (auto vv = new_variants.begin (); vv != new_variants.end (); ++vv) { + const db::Cell &from = into_layout->cell (vv->second); + db::Cell &to = into_layout->cell (vv->first); + for (db::Cell::const_iterator i = from.begin (); ! i.at_end (); ++i) { + if (cells_to_delete.find (i->cell_index ()) == cells_to_delete.end ()) { + auto m = std::lower_bound (mapped.begin (), mapped.end (), i->cell_index ()); + if (m == mapped.end () || *m != i->cell_index ()) { + to.insert (*i); + } + } + } + } + + } + if (! cells_to_delete.empty ()) { + // delete the variant original cells into_layout->delete_cells (cells_to_delete); } - cm->second.set_generation_ids (*into_layout, *source_layout); - } + cm->second.set_generation_ids (*into_layout, *source_layout); return cm->second; } diff --git a/src/db/unit_tests/dbDeepShapeStoreTests.cc b/src/db/unit_tests/dbDeepShapeStoreTests.cc index b69599a34..28600d159 100644 --- a/src/db/unit_tests/dbDeepShapeStoreTests.cc +++ b/src/db/unit_tests/dbDeepShapeStoreTests.cc @@ -24,6 +24,8 @@ #include "dbDeepShapeStore.h" #include "dbRegion.h" #include "dbDeepRegion.h" +#include "dbReader.h" +#include "dbTestSupport.h" #include "tlUnitTest.h" #include "tlStream.h" @@ -260,3 +262,36 @@ TEST(5_State) EXPECT_EQ (store.breakout_cells (0)->find (5) != store.breakout_cells (0)->end (), true); EXPECT_EQ (store.breakout_cells (0)->find (3) != store.breakout_cells (0)->end (), true); } + +TEST(6_RestoreWithCellSelection) +{ + db::Layout ly; + + { + std::string fn (tl::testdata ()); + fn += "/algo/dss_bug.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + + db::Cell &top_cell = ly.cell (*ly.begin_top_down ()); + + db::RecursiveShapeIterator in_it (ly, top_cell, l1); + + std::set us; + us.insert (ly.cell_by_name ("X").second); + in_it.unselect_cells (us); + + db::DeepShapeStore dss; + + db::Region in_region (in_it, dss); + ly.clear_layer (l1); + + in_region.insert_into (&ly, top_cell.cell_index (), l1); + + db::compare_layouts (_this, ly, tl::testdata () + "/algo/dss_bug_au.gds"); +} + diff --git a/testdata/algo/dss_bug.gds b/testdata/algo/dss_bug.gds new file mode 100644 index 0000000000000000000000000000000000000000..4b55407481498e6a9f0efe859237c496a4e41444 GIT binary patch literal 920 zcmb7CJud`77=Ctkc5dU2;I0tagbK+?i2Fbz=V*kWxZj##L@oifbCuhNXHJ^C^S?F;wrLJo@^-SZa_XTl4&t$)Cw;L^$6 z!QH_@H^Egnzq`ws48{fo53aAdOTKsS-S;5C*Z`r!3U6@Xpn_G@(*NNzaoFns71!I{ zyV^Q<7`^Xw$LqJ(XMKcT_IE;v{Y`Km5Hk!6VwM4@&AIdoDL)q#n&G}$wCfuAJirV1 zl_emSXra)em3$ zQU4b=n&3s_Mg+0s41rSUb?#zKewl$&M}_2*vrU_7+_W{=XRSMSw4of8Piyo>acpjc zst4q?x!*r9Oz9zYv#qIGR63Lv+z(YBN%>ec{bZiMPweXx*E1Z+RVD5uC&`S;gPm~~ zPb}K?+?gT014>rxdsF)^}bJe^2A);~T;Dzr=%j;cuoQ=3?HO3If2&2&mq+JNP@ Wl+P1`A1}bghLjfVI-xxq=lBAUv7J-^ literal 0 HcmV?d00001 From 08666aeb82c8454dcf98b51befc720e015042d75 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 2 Mar 2025 17:30:47 +0100 Subject: [PATCH 032/392] API implementation ... many, many new functions named 'split_...' --- src/db/db/dbAsIfFlatEdgePairs.cc | 32 ++- src/db/db/dbAsIfFlatEdgePairs.h | 1 + src/db/db/dbAsIfFlatEdges.cc | 17 ++ src/db/db/dbAsIfFlatEdges.h | 1 + src/db/db/dbAsIfFlatRegion.cc | 19 ++ src/db/db/dbAsIfFlatRegion.h | 1 + src/db/db/dbAsIfFlatTexts.cc | 17 ++ src/db/db/dbAsIfFlatTexts.h | 1 + src/db/db/dbDeepEdgePairs.cc | 65 ++++-- src/db/db/dbDeepEdgePairs.h | 3 +- src/db/db/dbDeepEdges.cc | 77 +++++-- src/db/db/dbDeepEdges.h | 3 +- src/db/db/dbDeepRegion.cc | 77 +++++-- src/db/db/dbDeepRegion.h | 4 +- src/db/db/dbDeepTexts.cc | 68 ++++-- src/db/db/dbDeepTexts.h | 3 +- src/db/db/dbEdgePairs.h | 12 + src/db/db/dbEdgePairsDelegate.h | 1 + src/db/db/dbEdges.h | 12 + src/db/db/dbEdgesDelegate.h | 1 + src/db/db/dbEmptyEdgePairs.h | 1 + src/db/db/dbEmptyEdges.h | 1 + src/db/db/dbEmptyRegion.h | 1 + src/db/db/dbEmptyTexts.h | 1 + src/db/db/dbRegion.h | 12 + src/db/db/dbRegionDelegate.h | 1 + src/db/db/dbTexts.h | 22 ++ src/db/db/dbTextsDelegate.h | 1 + src/db/db/gsiDeclDbEdgePairs.cc | 329 +++++++++++++++++++++++++--- src/db/db/gsiDeclDbEdges.cc | 113 +++++++++- src/db/db/gsiDeclDbRegion.cc | 363 +++++++++++++++++++++++++++---- src/db/db/gsiDeclDbTexts.cc | 44 ++++ 32 files changed, 1150 insertions(+), 154 deletions(-) diff --git a/src/db/db/dbAsIfFlatEdgePairs.cc b/src/db/db/dbAsIfFlatEdgePairs.cc index fc087ffa8..5a28a1ae9 100644 --- a/src/db/db/dbAsIfFlatEdgePairs.cc +++ b/src/db/db/dbAsIfFlatEdgePairs.cc @@ -226,6 +226,16 @@ AsIfFlatEdgePairs::processed_to_edges (const EdgePairToEdgeProcessorBase &filter return edges.release (); } +static void +insert_ep (FlatEdgePairs *dest, const db::EdgePair &ep, db::properties_id_type prop_id) +{ + if (prop_id != 0) { + dest->insert (db::EdgePairWithProperties (ep, prop_id)); + } else { + dest->insert (ep); + } +} + EdgePairsDelegate * AsIfFlatEdgePairs::filtered (const EdgePairFilterBase &filter) const { @@ -234,18 +244,28 @@ AsIfFlatEdgePairs::filtered (const EdgePairFilterBase &filter) const for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { if (filter.selected (*p)) { - db::properties_id_type prop_id = pm (p.prop_id ()); - if (prop_id != 0) { - new_edge_pairs->insert (db::EdgePairWithProperties (*p, prop_id)); - } else { - new_edge_pairs->insert (*p); - } + insert_ep (new_edge_pairs.get (), *p, pm (p.prop_id ())); } } return new_edge_pairs.release (); } +std::pair +AsIfFlatEdgePairs::filtered_pair (const EdgePairFilterBase &filter) const +{ + std::unique_ptr new_edge_pairs_true (new FlatEdgePairs ()); + std::unique_ptr new_edge_pairs_false (new FlatEdgePairs ()); + db::PropertyMapper pm (new_edge_pairs_true->properties_repository (), properties_repository ()); + + for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { + FlatEdgePairs *dest = filter.selected (*p) ? new_edge_pairs_true.get () : new_edge_pairs_false.get (); + insert_ep (dest, *p, pm (p.prop_id ())); + } + + return std::make_pair (new_edge_pairs_true.release (), new_edge_pairs_false.release ()); +} + RegionDelegate * AsIfFlatEdgePairs::pull_interacting (const Region &other) const { diff --git a/src/db/db/dbAsIfFlatEdgePairs.h b/src/db/db/dbAsIfFlatEdgePairs.h index 9a251dccb..c437fb79f 100644 --- a/src/db/db/dbAsIfFlatEdgePairs.h +++ b/src/db/db/dbAsIfFlatEdgePairs.h @@ -53,6 +53,7 @@ public: } virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const; + virtual std::pair filtered_pair (const EdgePairFilterBase &filter) const; virtual EdgePairsDelegate *process_in_place (const EdgePairProcessorBase &proc) { diff --git a/src/db/db/dbAsIfFlatEdges.cc b/src/db/db/dbAsIfFlatEdges.cc index f9f3beb30..cc3fdeee8 100644 --- a/src/db/db/dbAsIfFlatEdges.cc +++ b/src/db/db/dbAsIfFlatEdges.cc @@ -717,6 +717,23 @@ AsIfFlatEdges::filtered (const EdgeFilterBase &filter) const return new_region.release (); } +std::pair +AsIfFlatEdges::filtered_pair (const EdgeFilterBase &filter) const +{ + std::unique_ptr new_region_true (new FlatEdges ()); + std::unique_ptr new_region_false (new FlatEdges ()); + + for (EdgesIterator p (begin_merged ()); ! p.at_end (); ++p) { + if (filter.selected (*p)) { + new_region_true->insert (*p); + } else { + new_region_false->insert (*p); + } + } + + return std::make_pair (new_region_true.release (), new_region_false.release ()); +} + EdgePairsDelegate * AsIfFlatEdges::run_check (db::edge_relation_type rel, const Edges *other, db::Coord d, const db::EdgesCheckOptions &options) const { diff --git a/src/db/db/dbAsIfFlatEdges.h b/src/db/db/dbAsIfFlatEdges.h index a0bb035ab..a1113e074 100644 --- a/src/db/db/dbAsIfFlatEdges.h +++ b/src/db/db/dbAsIfFlatEdges.h @@ -101,6 +101,7 @@ public: } virtual EdgesDelegate *filtered (const EdgeFilterBase &) const; + virtual std::pair filtered_pair (const EdgeFilterBase &filter) const; virtual EdgesDelegate *merged_in_place () { diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 38f52cba2..b4d14617b 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -414,6 +414,25 @@ AsIfFlatRegion::filtered (const PolygonFilterBase &filter) const return new_region.release (); } +std::pair +AsIfFlatRegion::filtered_pair (const PolygonFilterBase &filter) const +{ + std::unique_ptr new_region_true (new FlatRegion ()); + std::unique_ptr new_region_false (new FlatRegion ()); + + for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { + if (filter.selected (*p)) { + new_region_true->insert (*p); + } else { + new_region_true->insert (*p); + } + } + + new_region_true->set_is_merged (true); + new_region_false->set_is_merged (true); + return std::make_pair (new_region_true.release (), new_region_false.release ()); +} + RegionDelegate * AsIfFlatRegion::processed (const PolygonProcessorBase &filter) const { diff --git a/src/db/db/dbAsIfFlatRegion.h b/src/db/db/dbAsIfFlatRegion.h index b34b58299..51b182443 100644 --- a/src/db/db/dbAsIfFlatRegion.h +++ b/src/db/db/dbAsIfFlatRegion.h @@ -101,6 +101,7 @@ public: } virtual RegionDelegate *filtered (const PolygonFilterBase &filter) const; + virtual std::pair filtered_pair (const PolygonFilterBase &filter) const; virtual RegionDelegate *merged_in_place () { diff --git a/src/db/db/dbAsIfFlatTexts.cc b/src/db/db/dbAsIfFlatTexts.cc index c5498fd64..6576d0d5e 100644 --- a/src/db/db/dbAsIfFlatTexts.cc +++ b/src/db/db/dbAsIfFlatTexts.cc @@ -164,6 +164,23 @@ AsIfFlatTexts::filtered (const TextFilterBase &filter) const return new_texts.release (); } +std::pair +AsIfFlatTexts::filtered_pair (const TextFilterBase &filter) const +{ + std::unique_ptr new_texts_true (new FlatTexts ()); + std::unique_ptr new_texts_false (new FlatTexts ()); + + for (TextsIterator p (begin ()); ! p.at_end (); ++p) { + if (filter.selected (*p)) { + new_texts_true->insert (*p); + } else { + new_texts_false->insert (*p); + } + } + + return std::make_pair (new_texts_true.release (), new_texts_false.release ()); +} + TextsDelegate * AsIfFlatTexts::processed (const TextProcessorBase &filter) const { diff --git a/src/db/db/dbAsIfFlatTexts.h b/src/db/db/dbAsIfFlatTexts.h index 62046626b..79591cf33 100644 --- a/src/db/db/dbAsIfFlatTexts.h +++ b/src/db/db/dbAsIfFlatTexts.h @@ -54,6 +54,7 @@ public: } virtual TextsDelegate *filtered (const TextFilterBase &) const; + virtual std::pair filtered_pair (const TextFilterBase &filter) const; virtual TextsDelegate *process_in_place (const TextProcessorBase &proc) { diff --git a/src/db/db/dbDeepEdgePairs.cc b/src/db/db/dbDeepEdgePairs.cc index cf4f06923..444bc4743 100644 --- a/src/db/db/dbDeepEdgePairs.cc +++ b/src/db/db/dbDeepEdgePairs.cc @@ -375,18 +375,24 @@ EdgePairsDelegate * DeepEdgePairs::filter_in_place (const EdgePairFilterBase &filter) { // TODO: implement to be really in-place - *this = *apply_filter (filter); + *this = *apply_filter (filter, true, false).first; return this; } EdgePairsDelegate * DeepEdgePairs::filtered (const EdgePairFilterBase &filter) const { - return apply_filter (filter); + return apply_filter (filter, true, false).first; } -DeepEdgePairs * -DeepEdgePairs::apply_filter (const EdgePairFilterBase &filter) const +std::pair +DeepEdgePairs::filtered_pair (const EdgePairFilterBase &filter) const +{ + return apply_filter (filter, true, true); +} + +std::pair +DeepEdgePairs::apply_filter (const EdgePairFilterBase &filter, bool with_true, bool with_false) const { const db::DeepLayer &edge_pairs = deep_layer (); db::Layout &layout = const_cast (edge_pairs.layout ()); @@ -404,9 +410,10 @@ DeepEdgePairs::apply_filter (const EdgePairFilterBase &filter) const } - std::map > to_commit; + std::map > to_commit_true, to_commit_false; - std::unique_ptr res (new db::DeepEdgePairs (edge_pairs.derived ())); + std::unique_ptr res_true (with_true ? new db::DeepEdgePairs (edge_pairs.derived ()) : 0); + std::unique_ptr res_false (with_false ? new db::DeepEdgePairs (edge_pairs.derived ()) : 0); for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { const db::Shapes &s = c->shapes (edge_pairs.layer ()); @@ -416,18 +423,34 @@ DeepEdgePairs::apply_filter (const EdgePairFilterBase &filter) const const std::set &vv = vars->variants (c->cell_index ()); for (auto v = vv.begin (); v != vv.end (); ++v) { - db::Shapes *st; + db::Shapes *st_true = 0, *st_false = 0; if (vv.size () == 1) { - st = & c->shapes (res->deep_layer ().layer ()); + if (with_true) { + st_true = & c->shapes (res_true->deep_layer ().layer ()); + } + if (with_false) { + st_false = & c->shapes (res_false->deep_layer ().layer ()); + } } else { - st = & to_commit [c->cell_index ()] [*v]; + if (with_true) { + st_true = & to_commit_true [c->cell_index ()] [*v]; + } + if (with_false) { + st_false = & to_commit_false [c->cell_index ()] [*v]; + } } const db::ICplxTrans &tr = *v; for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::EdgePairs); ! si.at_end (); ++si) { if (filter.selected (si->edge_pair ().transformed (tr))) { - st->insert (*si); + if (st_true) { + st_true->insert (*si); + } + } else { + if (st_false) { + st_false->insert (*si); + } } } @@ -435,11 +458,18 @@ DeepEdgePairs::apply_filter (const EdgePairFilterBase &filter) const } else { - db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + db::Shapes *st_true = with_true ? &c->shapes (res_true->deep_layer ().layer ()) : 0; + db::Shapes *st_false = with_false ? &c->shapes (res_false->deep_layer ().layer ()) : 0; for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::EdgePairs); ! si.at_end (); ++si) { if (filter.selected (si->edge_pair ())) { - st.insert (*si); + if (with_true) { + st_true->insert (*si); + } + } else { + if (with_false) { + st_false->insert (*si); + } } } @@ -447,11 +477,16 @@ DeepEdgePairs::apply_filter (const EdgePairFilterBase &filter) const } - if (! to_commit.empty () && vars.get ()) { - vars->commit_shapes (res->deep_layer ().layer (), to_commit); + if (! to_commit_true.empty () && vars.get ()) { + tl_assert (res_true.get () != 0); + vars->commit_shapes (res_true->deep_layer ().layer (), to_commit_true); + } + if (! to_commit_false.empty () && vars.get ()) { + tl_assert (res_false.get () != 0); + vars->commit_shapes (res_false->deep_layer ().layer (), to_commit_false); } - return res.release (); + return std::make_pair (res_true.release (), res_false.release ()); } EdgePairsDelegate *DeepEdgePairs::process_in_place (const EdgePairProcessorBase &filter) diff --git a/src/db/db/dbDeepEdgePairs.h b/src/db/db/dbDeepEdgePairs.h index 89a659cbf..8537d77c8 100644 --- a/src/db/db/dbDeepEdgePairs.h +++ b/src/db/db/dbDeepEdgePairs.h @@ -78,6 +78,7 @@ public: virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &filter); virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const; + virtual std::pair filtered_pair (const EdgePairFilterBase &filter) const; virtual EdgePairsDelegate *process_in_place (const EdgePairProcessorBase &); virtual EdgePairsDelegate *processed (const EdgePairProcessorBase &) const; virtual RegionDelegate *processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const; @@ -117,7 +118,7 @@ private: void init (); EdgesDelegate *generic_edges (bool first, bool second) const; - DeepEdgePairs *apply_filter (const EdgePairFilterBase &filter) const; + std::pair apply_filter (const EdgePairFilterBase &filter, bool with_true, bool with_false) const; }; } diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc index 8f1872b76..c2e86caaa 100644 --- a/src/db/db/dbDeepEdges.cc +++ b/src/db/db/dbDeepEdges.cc @@ -809,18 +809,24 @@ EdgesDelegate * DeepEdges::filter_in_place (const EdgeFilterBase &filter) { // TODO: implement to be really in-place - *this = *apply_filter (filter); + *this = *apply_filter (filter, true, false).first; return this; } EdgesDelegate * DeepEdges::filtered (const EdgeFilterBase &filter) const { - return apply_filter (filter); + return apply_filter (filter, true, false).first; } -DeepEdges * -DeepEdges::apply_filter (const EdgeFilterBase &filter) const +std::pair +DeepEdges::filtered_pair (const EdgeFilterBase &filter) const +{ + return apply_filter (filter, true, true); +} + +std::pair +DeepEdges::apply_filter (const EdgeFilterBase &filter, bool with_true, bool with_false) const { const db::DeepLayer &edges = filter.requires_raw_input () ? deep_layer () : merged_deep_layer (); db::Layout &layout = const_cast (edges.layout ()); @@ -838,9 +844,10 @@ DeepEdges::apply_filter (const EdgeFilterBase &filter) const } - std::map > to_commit; + std::map > to_commit_true, to_commit_false; - std::unique_ptr res (new db::DeepEdges (edges.derived ())); + std::unique_ptr res_true (with_true ? new db::DeepEdges (edges.derived ()) : 0); + std::unique_ptr res_false (with_false ? new db::DeepEdges (edges.derived ()) : 0); for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { const db::Shapes &s = c->shapes (edges.layer ()); @@ -850,16 +857,34 @@ DeepEdges::apply_filter (const EdgeFilterBase &filter) const const std::set &vv = vars->variants (c->cell_index ()); for (auto v = vv.begin (); v != vv.end (); ++v) { - db::Shapes *st; + db::Shapes *st_true = 0, *st_false = 0; if (vv.size () == 1) { - st = & c->shapes (res->deep_layer ().layer ()); + if (with_true) { + st_true = & c->shapes (res_true->deep_layer ().layer ()); + } + if (with_false) { + st_false = & c->shapes (res_false->deep_layer ().layer ()); + } } else { - st = & to_commit [c->cell_index ()] [*v]; + if (with_true) { + st_true = & to_commit_true [c->cell_index ()] [*v]; + } + if (with_false) { + st_false = & to_commit_false [c->cell_index ()] [*v]; + } } + const db::ICplxTrans &tr = *v; + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Edges); ! si.at_end (); ++si) { - if (filter.selected (si->edge ().transformed (*v))) { - st->insert (*si); + if (filter.selected (si->edge ().transformed (tr))) { + if (st_true) { + st_true->insert (*si); + } + } else { + if (st_false) { + st_false->insert (*si); + } } } @@ -867,11 +892,18 @@ DeepEdges::apply_filter (const EdgeFilterBase &filter) const } else { - db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + db::Shapes *st_true = with_true ? &c->shapes (res_true->deep_layer ().layer ()) : 0; + db::Shapes *st_false = with_false ? &c->shapes (res_false->deep_layer ().layer ()) : 0; for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Edges); ! si.at_end (); ++si) { if (filter.selected (si->edge ())) { - st.insert (*si); + if (with_true) { + st_true->insert (*si); + } + } else { + if (with_false) { + st_false->insert (*si); + } } } @@ -879,14 +911,25 @@ DeepEdges::apply_filter (const EdgeFilterBase &filter) const } - if (! to_commit.empty () && vars.get ()) { - vars->commit_shapes (res->deep_layer ().layer (), to_commit); + if (! to_commit_true.empty () && vars.get ()) { + tl_assert (res_true.get () != 0); + vars->commit_shapes (res_true->deep_layer ().layer (), to_commit_true); + } + if (! to_commit_false.empty () && vars.get ()) { + tl_assert (res_false.get () != 0); + vars->commit_shapes (res_false->deep_layer ().layer (), to_commit_false); } if (! filter.requires_raw_input ()) { - res->set_is_merged (true); + if (res_true.get ()) { + res_true->set_is_merged (true); + } + if (res_false.get ()) { + res_false->set_is_merged (true); + } } - return res.release (); + + return std::make_pair (res_true.release (), res_false.release ()); } EdgesDelegate *DeepEdges::merged_in_place () diff --git a/src/db/db/dbDeepEdges.h b/src/db/db/dbDeepEdges.h index da104c0ec..3a1262732 100644 --- a/src/db/db/dbDeepEdges.h +++ b/src/db/db/dbDeepEdges.h @@ -127,6 +127,7 @@ public: virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &filter); virtual EdgesDelegate *filtered (const EdgeFilterBase &) const; + virtual std::pair filtered_pair (const EdgeFilterBase &filter) const; virtual EdgesDelegate *process_in_place (const EdgeProcessorBase &); virtual EdgesDelegate *processed (const EdgeProcessorBase &) const; virtual EdgePairsDelegate *processed_to_edge_pairs (const EdgeToEdgePairProcessorBase &filter) const; @@ -199,7 +200,7 @@ private: virtual std::pair selected_interacting_pair_generic (const Region ®ion, EdgeInteractionMode mode, size_t min_count, size_t max_count) const; EdgesDelegate *selected_interacting_generic_impl (const DeepRegion *other_deep, EdgeInteractionMode mode, bool inverse, size_t min_count, size_t max_count) const; std::pair selected_interacting_pair_generic_impl (const DeepRegion *other_deep, EdgeInteractionMode mode, size_t min_count, size_t max_count) const; - DeepEdges *apply_filter (const EdgeFilterBase &filter) const; + std::pair apply_filter (const EdgeFilterBase &filter, bool with_true, bool with_false) const; template OutputContainer *processed_impl (const edge_processor &filter) const; }; diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 03e278d26..9dac1d01e 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1599,7 +1599,7 @@ DeepRegion::filter_in_place (const PolygonFilterBase &filter) } // TODO: implement to be really in-place - *this = *apply_filter (filter); + *this = *apply_filter (filter, true, false).first; return this; } @@ -1610,11 +1610,17 @@ DeepRegion::filtered (const PolygonFilterBase &filter) const return clone (); } - return apply_filter (filter); + return apply_filter (filter, true, false).first; } -DeepRegion * -DeepRegion::apply_filter (const PolygonFilterBase &filter) const +std::pair +DeepRegion::filtered_pair (const PolygonFilterBase &filter) const +{ + return apply_filter (filter, true, true); +} + +std::pair +DeepRegion::apply_filter (const PolygonFilterBase &filter, bool with_true, bool with_false) const { const db::DeepLayer &polygons = filter.requires_raw_input () ? deep_layer () : merged_deep_layer (); db::Layout &layout = const_cast (polygons.layout ()); @@ -1632,9 +1638,10 @@ DeepRegion::apply_filter (const PolygonFilterBase &filter) const } - std::map > to_commit; + std::map > to_commit_true, to_commit_false; - std::unique_ptr res (new db::DeepRegion (polygons.derived ())); + std::unique_ptr res_true (with_true ? new db::DeepRegion (polygons.derived ()) : 0); + std::unique_ptr res_false (with_false ? new db::DeepRegion (polygons.derived ()) : 0); for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { const db::Shapes &s = c->shapes (polygons.layer ()); @@ -1644,18 +1651,36 @@ DeepRegion::apply_filter (const PolygonFilterBase &filter) const const std::set &vv = vars->variants (c->cell_index ()); for (auto v = vv.begin (); v != vv.end (); ++v) { - db::Shapes *st; + db::Shapes *st_true = 0, *st_false = 0; if (vv.size () == 1) { - st = & c->shapes (res->deep_layer ().layer ()); + if (with_true) { + st_true = & c->shapes (res_true->deep_layer ().layer ()); + } + if (with_false) { + st_false = & c->shapes (res_false->deep_layer ().layer ()); + } } else { - st = & to_commit [c->cell_index ()] [*v]; + if (with_true) { + st_true = & to_commit_true [c->cell_index ()] [*v]; + } + if (with_false) { + st_false = & to_commit_false [c->cell_index ()] [*v]; + } } + const db::ICplxTrans &tr = *v; + 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 (*v))) { - st->insert (*si); + if (filter.selected (poly.transformed (tr))) { + if (st_true) { + st_true->insert (*si); + } + } else { + if (st_false) { + st_false->insert (*si); + } } } @@ -1663,13 +1688,20 @@ DeepRegion::apply_filter (const PolygonFilterBase &filter) const } else { - db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + db::Shapes *st_true = with_true ? &c->shapes (res_true->deep_layer ().layer ()) : 0; + db::Shapes *st_false = with_false ? &c->shapes (res_false->deep_layer ().layer ()) : 0; 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 (with_true) { + st_true->insert (*si); + } + } else { + if (with_false) { + st_false->insert (*si); + } } } @@ -1677,14 +1709,25 @@ DeepRegion::apply_filter (const PolygonFilterBase &filter) const } - if (! to_commit.empty () && vars.get ()) { - vars->commit_shapes (res->deep_layer ().layer (), to_commit); + if (! to_commit_true.empty () && vars.get ()) { + tl_assert (res_true.get () != 0); + vars->commit_shapes (res_true->deep_layer ().layer (), to_commit_true); + } + if (! to_commit_false.empty () && vars.get ()) { + tl_assert (res_false.get () != 0); + vars->commit_shapes (res_false->deep_layer ().layer (), to_commit_false); } if (! filter.requires_raw_input ()) { - res->set_is_merged (true); + if (res_true.get ()) { + res_true->set_is_merged (true); + } + if (res_false.get ()) { + res_false->set_is_merged (true); + } } - return res.release (); + + return std::make_pair (res_true.release (), res_false.release ()); } RegionDelegate * diff --git a/src/db/db/dbDeepRegion.h b/src/db/db/dbDeepRegion.h index 6860fc472..b162168a4 100644 --- a/src/db/db/dbDeepRegion.h +++ b/src/db/db/dbDeepRegion.h @@ -127,6 +127,7 @@ public: 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 std::pair filtered_pair (const PolygonFilterBase &filter) const; virtual RegionDelegate *merged_in_place (); virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc); @@ -183,8 +184,7 @@ private: DeepLayer not_with_impl (const DeepRegion *other, PropertyConstraint property_constraint) const; DeepLayer and_with_impl (const DeepRegion *other, PropertyConstraint property_constraint) const; std::pair and_and_not_with (const DeepRegion *other, PropertyConstraint property_constraint) const; - DeepRegion *apply_filter (const PolygonFilterBase &filter) const; - + std::pair apply_filter (const PolygonFilterBase &filter, bool with_true, bool with_false) const; template void configure_proc (Proc &proc) const { diff --git a/src/db/db/dbDeepTexts.cc b/src/db/db/dbDeepTexts.cc index 3d9df8ca4..8933f79c5 100644 --- a/src/db/db/dbDeepTexts.cc +++ b/src/db/db/dbDeepTexts.cc @@ -395,16 +395,23 @@ TextsDelegate *DeepTexts::add (const Texts &other) const TextsDelegate *DeepTexts::filter_in_place (const TextFilterBase &filter) { // TODO: implement as really in place - *this = *apply_filter (filter); + *this = *apply_filter (filter, true, false).first; return this; } TextsDelegate *DeepTexts::filtered (const TextFilterBase &filter) const { - return apply_filter (filter); + return apply_filter (filter, true, false).first; } -DeepTexts *DeepTexts::apply_filter (const TextFilterBase &filter) const +std::pair +DeepTexts::filtered_pair (const TextFilterBase &filter) const +{ + return apply_filter (filter, true, true); +} + +std::pair +DeepTexts::apply_filter (const TextFilterBase &filter, bool with_true, bool with_false) const { const db::DeepLayer &texts = deep_layer (); db::Layout &layout = const_cast (texts.layout ()); @@ -422,9 +429,10 @@ DeepTexts *DeepTexts::apply_filter (const TextFilterBase &filter) const } - std::map > to_commit; + std::map > to_commit_true, to_commit_false; - std::unique_ptr res (new db::DeepTexts (texts.derived ())); + std::unique_ptr res_true (with_true ? new db::DeepTexts (texts.derived ()) : 0); + std::unique_ptr res_false (with_false ? new db::DeepTexts (texts.derived ()) : 0); for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) { const db::Shapes &s = c->shapes (texts.layer ()); @@ -434,18 +442,36 @@ DeepTexts *DeepTexts::apply_filter (const TextFilterBase &filter) const const std::set &vv = vars->variants (c->cell_index ()); for (auto v = vv.begin (); v != vv.end (); ++v) { - db::Shapes *st; + db::Shapes *st_true = 0, *st_false = 0; if (vv.size () == 1) { - st = & c->shapes (res->deep_layer ().layer ()); + if (with_true) { + st_true = & c->shapes (res_true->deep_layer ().layer ()); + } + if (with_false) { + st_false = & c->shapes (res_false->deep_layer ().layer ()); + } } else { - st = & to_commit [c->cell_index ()] [*v]; + if (with_true) { + st_true = & to_commit_true [c->cell_index ()] [*v]; + } + if (with_false) { + st_false = & to_commit_false [c->cell_index ()] [*v]; + } } + const db::ICplxTrans &tr = *v; + for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Texts); ! si.at_end (); ++si) { db::Text text; si->text (text); - if (filter.selected (text.transformed (*v))) { - st->insert (*si); + if (filter.selected (text.transformed (tr))) { + if (st_true) { + st_true->insert (*si); + } + } else { + if (st_false) { + st_false->insert (*si); + } } } @@ -453,13 +479,20 @@ DeepTexts *DeepTexts::apply_filter (const TextFilterBase &filter) const } else { - db::Shapes &st = c->shapes (res->deep_layer ().layer ()); + db::Shapes *st_true = with_true ? &c->shapes (res_true->deep_layer ().layer ()) : 0; + db::Shapes *st_false = with_false ? &c->shapes (res_false->deep_layer ().layer ()) : 0; for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Texts); ! si.at_end (); ++si) { db::Text text; si->text (text); if (filter.selected (text)) { - st.insert (*si); + if (with_true) { + st_true->insert (*si); + } + } else { + if (with_false) { + st_false->insert (*si); + } } } @@ -467,11 +500,16 @@ DeepTexts *DeepTexts::apply_filter (const TextFilterBase &filter) const } - if (! to_commit.empty () && vars.get ()) { - vars->commit_shapes (res->deep_layer ().layer (), to_commit); + if (! to_commit_true.empty () && vars.get ()) { + tl_assert (res_true.get () != 0); + vars->commit_shapes (res_true->deep_layer ().layer (), to_commit_true); + } + if (! to_commit_false.empty () && vars.get ()) { + tl_assert (res_false.get () != 0); + vars->commit_shapes (res_false->deep_layer ().layer (), to_commit_false); } - return res.release (); + return std::make_pair (res_true.release (), res_false.release ()); } TextsDelegate *DeepTexts::process_in_place (const TextProcessorBase &filter) diff --git a/src/db/db/dbDeepTexts.h b/src/db/db/dbDeepTexts.h index 95d0d13e1..7c8e125ff 100644 --- a/src/db/db/dbDeepTexts.h +++ b/src/db/db/dbDeepTexts.h @@ -79,6 +79,7 @@ public: virtual TextsDelegate *filter_in_place (const TextFilterBase &filter); virtual TextsDelegate *filtered (const TextFilterBase &) const; + virtual std::pair filtered_pair (const TextFilterBase &filter) const; virtual TextsDelegate *process_in_place (const TextProcessorBase &); virtual TextsDelegate *processed (const TextProcessorBase &) const; @@ -107,7 +108,7 @@ private: DeepTexts &operator= (const DeepTexts &other); void init (); - DeepTexts *apply_filter (const TextFilterBase &filter) const; + std::pair apply_filter (const TextFilterBase &filter, bool with_true, bool with_false) const; virtual TextsDelegate *selected_interacting_generic (const Region &other, bool inverse) const; virtual RegionDelegate *pull_generic (const Region &other) const; diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index b1b451a58..36b8960f8 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -346,6 +346,18 @@ public: return EdgePairs (mp_delegate->filtered (filter)); } + /** + * @brief Returns the filtered edge pairs and the others + * + * This method will return a new edge pair collection with only those edge pairs which + * conform to the filter criterion and another for those which don't. + */ + std::pair split_filter (const EdgePairFilterBase &filter) const + { + std::pair p = mp_delegate->filtered_pair (filter); + return std::pair (EdgePairs (p.first), EdgePairs (p.second)); + } + /** * @brief Processes the edge pairs in-place * diff --git a/src/db/db/dbEdgePairsDelegate.h b/src/db/db/dbEdgePairsDelegate.h index eeb05ec19..419c61409 100644 --- a/src/db/db/dbEdgePairsDelegate.h +++ b/src/db/db/dbEdgePairsDelegate.h @@ -201,6 +201,7 @@ public: virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &filter) = 0; virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &filter) const = 0; + virtual std::pair filtered_pair (const EdgePairFilterBase &filter) const = 0; virtual EdgePairsDelegate *process_in_place (const EdgePairProcessorBase &proc) = 0; virtual EdgePairsDelegate *processed (const EdgePairProcessorBase &proc) const = 0; virtual RegionDelegate *processed_to_polygons (const EdgePairToPolygonProcessorBase &proc) const = 0; diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index 649ded1c3..bb57ada29 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -468,6 +468,18 @@ public: return Edges (mp_delegate->filtered (filter)); } + /** + * @brief Returns the filtered edges and the others + * + * This method will return a new edge collection with only those edges which + * conform to the filter criterion and another for those which don't. + */ + std::pair split_filter (const EdgeFilterBase &filter) const + { + std::pair p = mp_delegate->filtered_pair (filter); + return std::pair (Edges (p.first), Edges (p.second)); + } + /** * @brief Processes the (merged) edges * diff --git a/src/db/db/dbEdgesDelegate.h b/src/db/db/dbEdgesDelegate.h index caeab6524..c0b971044 100644 --- a/src/db/db/dbEdgesDelegate.h +++ b/src/db/db/dbEdgesDelegate.h @@ -228,6 +228,7 @@ public: virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &filter) = 0; virtual EdgesDelegate *filtered (const EdgeFilterBase &filter) const = 0; + virtual std::pair filtered_pair (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; diff --git a/src/db/db/dbEmptyEdgePairs.h b/src/db/db/dbEmptyEdgePairs.h index f1f6b981a..6f01ddd92 100644 --- a/src/db/db/dbEmptyEdgePairs.h +++ b/src/db/db/dbEmptyEdgePairs.h @@ -56,6 +56,7 @@ public: virtual EdgePairsDelegate *filter_in_place (const EdgePairFilterBase &) { return this; } virtual EdgePairsDelegate *filtered (const EdgePairFilterBase &) const { return new EmptyEdgePairs (); } + virtual std::pair filtered_pair (const EdgePairFilterBase &) const { return std::make_pair (new EmptyEdgePairs (), new EmptyEdgePairs ()); } virtual EdgePairsDelegate *process_in_place (const EdgePairProcessorBase &) { return this; } virtual EdgePairsDelegate *processed (const EdgePairProcessorBase &) const { return new EmptyEdgePairs (); } virtual RegionDelegate *processed_to_polygons (const EdgePairToPolygonProcessorBase &filter) const; diff --git a/src/db/db/dbEmptyEdges.h b/src/db/db/dbEmptyEdges.h index 4514ea80d..13dfd39d3 100644 --- a/src/db/db/dbEmptyEdges.h +++ b/src/db/db/dbEmptyEdges.h @@ -66,6 +66,7 @@ public: virtual EdgesDelegate *filter_in_place (const EdgeFilterBase &) { return this; } virtual EdgesDelegate *filtered (const EdgeFilterBase &) const { return new EmptyEdges (); } + virtual std::pair filtered_pair (const EdgeFilterBase &) const { return std::make_pair (new EmptyEdges (), 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; diff --git a/src/db/db/dbEmptyRegion.h b/src/db/db/dbEmptyRegion.h index 46dc1397a..1016b6080 100644 --- a/src/db/db/dbEmptyRegion.h +++ b/src/db/db/dbEmptyRegion.h @@ -85,6 +85,7 @@ public: virtual EdgesDelegate *edges (const EdgeFilterBase *, const PolygonToEdgeProcessorBase *) const; virtual RegionDelegate *filter_in_place (const PolygonFilterBase &) { return this; } virtual RegionDelegate *filtered (const PolygonFilterBase &) const { return new EmptyRegion (); } + virtual std::pair filtered_pair (const PolygonFilterBase &) const { return std::make_pair (new EmptyRegion (), 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; diff --git a/src/db/db/dbEmptyTexts.h b/src/db/db/dbEmptyTexts.h index ecc3f313a..934bf62c9 100644 --- a/src/db/db/dbEmptyTexts.h +++ b/src/db/db/dbEmptyTexts.h @@ -56,6 +56,7 @@ public: virtual TextsDelegate *filter_in_place (const TextFilterBase &) { return this; } virtual TextsDelegate *filtered (const TextFilterBase &) const { return new EmptyTexts (); } + virtual std::pair filtered_pair (const TextFilterBase &) const { return std::make_pair (new EmptyTexts (), new EmptyTexts ()); } virtual TextsDelegate *process_in_place (const TextProcessorBase &) { return this; } virtual TextsDelegate *processed (const TextProcessorBase &) const { return new EmptyTexts (); } diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 26a908fe7..893931949 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -572,6 +572,18 @@ public: return Region (mp_delegate->filtered (filter)); } + /** + * @brief Returns the filtered polygons and the others + * + * This method will return a new region with only those polygons which + * conform to the filter criterion and another for those which don't. + */ + std::pair split_filter (const PolygonFilterBase &filter) const + { + std::pair p = mp_delegate->filtered_pair (filter); + return std::pair (Region (p.first), Region (p.second)); + } + /** * @brief Processes the (merged) polygons * diff --git a/src/db/db/dbRegionDelegate.h b/src/db/db/dbRegionDelegate.h index 70acd8810..0920cb0e9 100644 --- a/src/db/db/dbRegionDelegate.h +++ b/src/db/db/dbRegionDelegate.h @@ -214,6 +214,7 @@ public: virtual EdgesDelegate *edges (const EdgeFilterBase *filter, const db::PolygonToEdgeProcessorBase *proc) const = 0; virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter) = 0; virtual RegionDelegate *filtered (const PolygonFilterBase &filter) const = 0; + virtual std::pair filtered_pair (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; diff --git a/src/db/db/dbTexts.h b/src/db/db/dbTexts.h index 86b2a7e24..03594f1fd 100644 --- a/src/db/db/dbTexts.h +++ b/src/db/db/dbTexts.h @@ -213,6 +213,16 @@ public: return mp_delegate; } + /** + * @brief Takes the underlying delegate object + */ + TextsDelegate *take_delegate () + { + TextsDelegate *delegate = mp_delegate; + mp_delegate = 0; + return delegate; + } + /** * @brief Iterator of the text set * @@ -323,6 +333,18 @@ public: return Texts (mp_delegate->filtered (filter)); } + /** + * @brief Returns the filtered texts and the others + * + * This method will return a new text collection with only those texts which + * conform to the filter criterion and another for those which don't. + */ + std::pair split_filter (const TextFilterBase &filter) const + { + std::pair p = mp_delegate->filtered_pair (filter); + return std::pair (Texts (p.first), Texts (p.second)); + } + /** * @brief Processes the edge pairs in-place * diff --git a/src/db/db/dbTextsDelegate.h b/src/db/db/dbTextsDelegate.h index c22e6be67..ea6ca62f1 100644 --- a/src/db/db/dbTextsDelegate.h +++ b/src/db/db/dbTextsDelegate.h @@ -95,6 +95,7 @@ public: virtual TextsDelegate *filter_in_place (const TextFilterBase &filter) = 0; virtual TextsDelegate *filtered (const TextFilterBase &filter) const = 0; + virtual std::pair filtered_pair (const TextFilterBase &filter) const = 0; virtual TextsDelegate *process_in_place (const TextProcessorBase &proc) = 0; virtual TextsDelegate *processed (const TextProcessorBase &proc) const = 0; virtual RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &proc) const = 0; diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 99f289880..e17e89c48 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -409,6 +409,11 @@ static void filter (db::EdgePairs *r, const EdgePairFilterImpl *f) r->filter (*f); } +static std::vector split_filter (const db::EdgePairs *r, const EdgePairFilterImpl *f) +{ + return as_2edge_pairs_vector (r->split_filter (*f)); +} + static db::EdgePairs processed_epep (const db::EdgePairs *r, const shape_processor_impl *f) { return r->processed (*f); @@ -439,12 +444,24 @@ static db::EdgePairs with_distance1 (const db::EdgePairs *r, db::EdgePairs::dist return r->filtered (ef); } +static std::vector split_with_distance1 (const db::EdgePairs *r, db::EdgePairs::distance_type length) +{ + db::EdgePairFilterByDistance ef (length, length + 1, false); + return as_2edge_pairs_vector (r->split_filter (ef)); +} + static db::EdgePairs with_distance2 (const db::EdgePairs *r, const tl::Variant &min, const tl::Variant &max, bool inverse) { db::EdgePairFilterByDistance ef (min.is_nil () ? db::Edges::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), inverse); return r->filtered (ef); } +static std::vector split_with_distance2 (const db::EdgePairs *r, const tl::Variant &min, const tl::Variant &max) +{ + db::EdgePairFilterByDistance ef (min.is_nil () ? db::Edges::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), false); + return as_2edge_pairs_vector (r->split_filter (ef)); +} + static db::EdgePairs with_length1 (const db::EdgePairs *r, db::EdgePairs::distance_type length, bool inverse) { db::EdgeLengthFilter f (length, length + 1, inverse); @@ -452,6 +469,13 @@ static db::EdgePairs with_length1 (const db::EdgePairs *r, db::EdgePairs::distan return r->filtered (ef); } +static std::vector split_with_length1 (const db::EdgePairs *r, db::EdgePairs::distance_type length) +{ + db::EdgeLengthFilter f (length, length + 1, false); + db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); + return as_2edge_pairs_vector (r->split_filter (ef)); +} + static db::EdgePairs with_length2 (const db::EdgePairs *r, const tl::Variant &min, const tl::Variant &max, bool inverse) { db::EdgeLengthFilter f (min.is_nil () ? db::Edges::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), inverse); @@ -459,6 +483,13 @@ static db::EdgePairs with_length2 (const db::EdgePairs *r, const tl::Variant &mi return r->filtered (ef); } +static std::vector split_with_length2 (const db::EdgePairs *r, const tl::Variant &min, const tl::Variant &max) +{ + db::EdgeLengthFilter f (min.is_nil () ? db::Edges::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), false); + db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); + return as_2edge_pairs_vector (r->split_filter (ef)); +} + static db::EdgePairs with_length_both1 (const db::EdgePairs *r, db::EdgePairs::distance_type length, bool inverse) { db::EdgeLengthFilter f (length, length + 1, inverse); @@ -466,6 +497,13 @@ static db::EdgePairs with_length_both1 (const db::EdgePairs *r, db::EdgePairs::d return r->filtered (ef); } +static std::vector split_with_length_both1 (const db::EdgePairs *r, db::EdgePairs::distance_type length) +{ + db::EdgeLengthFilter f (length, length + 1, false); + db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); + return as_2edge_pairs_vector (r->split_filter (ef)); +} + static db::EdgePairs with_length_both2 (const db::EdgePairs *r, const tl::Variant &min, const tl::Variant &max, bool inverse) { db::EdgeLengthFilter f (min.is_nil () ? db::Edges::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), inverse); @@ -473,6 +511,13 @@ static db::EdgePairs with_length_both2 (const db::EdgePairs *r, const tl::Varian return r->filtered (ef); } +static std::vector split_with_length_both2 (const db::EdgePairs *r, const tl::Variant &min, const tl::Variant &max) +{ + db::EdgeLengthFilter f (min.is_nil () ? db::Edges::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), false); + db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); + return as_2edge_pairs_vector (r->split_filter (ef)); +} + static db::EdgePairs with_angle1 (const db::EdgePairs *r, double a, bool inverse) { db::EdgeOrientationFilter f (a, inverse, false); @@ -480,6 +525,13 @@ static db::EdgePairs with_angle1 (const db::EdgePairs *r, double a, bool inverse return r->filtered (ef); } +static std::vector split_with_angle1 (const db::EdgePairs *r, double a) +{ + db::EdgeOrientationFilter f (a, false, false); + db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); + return as_2edge_pairs_vector (r->split_filter (ef)); +} + static db::EdgePairs with_angle2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) { db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, false); @@ -487,6 +539,13 @@ static db::EdgePairs with_angle2 (const db::EdgePairs *r, double amin, double am return r->filtered (ef); } +static std::vector split_with_angle2 (const db::EdgePairs *r, double amin, double amax, bool include_amin, bool include_amax) +{ + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, false, false); + db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); + return as_2edge_pairs_vector (r->split_filter (ef)); +} + static db::EdgePairs with_abs_angle1 (const db::EdgePairs *r, double a, bool inverse) { db::EdgeOrientationFilter f (a, inverse, true); @@ -494,6 +553,13 @@ static db::EdgePairs with_abs_angle1 (const db::EdgePairs *r, double a, bool inv return r->filtered (ef); } +static std::vector split_with_abs_angle1 (const db::EdgePairs *r, double a) +{ + db::EdgeOrientationFilter f (a, false, true); + db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); + return as_2edge_pairs_vector (r->split_filter (ef)); +} + static db::EdgePairs with_abs_angle2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) { db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, true); @@ -501,6 +567,13 @@ static db::EdgePairs with_abs_angle2 (const db::EdgePairs *r, double amin, doubl return r->filtered (ef); } +static std::vector split_with_abs_angle2 (const db::EdgePairs *r, double amin, double amax, bool include_amin, bool include_amax) +{ + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, false, true); + db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); + return as_2edge_pairs_vector (r->split_filter (ef)); +} + static db::EdgePairs with_angle3 (const db::EdgePairs *r, db::SpecialEdgeOrientationFilter::FilterType type, bool inverse) { db::SpecialEdgeOrientationFilter f (type, inverse); @@ -508,6 +581,13 @@ static db::EdgePairs with_angle3 (const db::EdgePairs *r, db::SpecialEdgeOrienta return r->filtered (ef); } +static std::vector split_with_angle3 (const db::EdgePairs *r, db::SpecialEdgeOrientationFilter::FilterType type) +{ + db::SpecialEdgeOrientationFilter f (type, false); + db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); + return as_2edge_pairs_vector (r->split_filter (ef)); +} + static db::EdgePairs with_angle_both1 (const db::EdgePairs *r, double a, bool inverse) { db::EdgeOrientationFilter f (a, inverse, false); @@ -515,6 +595,13 @@ static db::EdgePairs with_angle_both1 (const db::EdgePairs *r, double a, bool in return r->filtered (ef); } +static std::vector split_with_angle_both1 (const db::EdgePairs *r, double a) +{ + db::EdgeOrientationFilter f (a, false, false); + db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); + return as_2edge_pairs_vector (r->split_filter (ef)); +} + static db::EdgePairs with_angle_both2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) { db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, false); @@ -522,6 +609,13 @@ static db::EdgePairs with_angle_both2 (const db::EdgePairs *r, double amin, doub return r->filtered (ef); } +static std::vector split_with_angle_both2 (const db::EdgePairs *r, double amin, double amax, bool include_amin, bool include_amax) +{ + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, false, false); + db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); + return as_2edge_pairs_vector (r->split_filter (ef)); +} + static db::EdgePairs with_abs_angle_both1 (const db::EdgePairs *r, double a, bool inverse) { db::EdgeOrientationFilter f (a, inverse, true); @@ -529,6 +623,13 @@ static db::EdgePairs with_abs_angle_both1 (const db::EdgePairs *r, double a, boo return r->filtered (ef); } +static std::vector split_with_abs_angle_both1 (const db::EdgePairs *r, double a) +{ + db::EdgeOrientationFilter f (a, false, true); + db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); + return as_2edge_pairs_vector (r->split_filter (ef)); +} + static db::EdgePairs with_abs_angle_both2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) { db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, true); @@ -536,6 +637,13 @@ static db::EdgePairs with_abs_angle_both2 (const db::EdgePairs *r, double amin, return r->filtered (ef); } +static std::vector split_with_abs_angle_both2 (const db::EdgePairs *r, double amin, double amax, bool include_amin, bool include_amax) +{ + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, false, true); + db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); + return as_2edge_pairs_vector (r->split_filter (ef)); +} + static db::EdgePairs with_angle_both3 (const db::EdgePairs *r, db::SpecialEdgeOrientationFilter::FilterType type, bool inverse) { db::SpecialEdgeOrientationFilter f (type, inverse); @@ -543,30 +651,61 @@ static db::EdgePairs with_angle_both3 (const db::EdgePairs *r, db::SpecialEdgeOr return r->filtered (ef); } +static std::vector split_with_angle_both3 (const db::EdgePairs *r, db::SpecialEdgeOrientationFilter::FilterType type) +{ + db::SpecialEdgeOrientationFilter f (type, false); + db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); + return as_2edge_pairs_vector (r->split_filter (ef)); +} + static db::EdgePairs with_internal_angle1 (const db::EdgePairs *r, double a, bool inverse) { db::InternalAngleEdgePairFilter f (a, inverse); return r->filtered (f); } +static std::vector split_with_internal_angle1 (const db::EdgePairs *r, double a) +{ + db::InternalAngleEdgePairFilter f (a, false); + return as_2edge_pairs_vector (r->split_filter (f)); +} + static db::EdgePairs with_internal_angle2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) { db::InternalAngleEdgePairFilter f (amin, include_amin, amax, include_amax, inverse); return r->filtered (f); } +static std::vector split_with_internal_angle2 (const db::EdgePairs *r, double amin, double amax, bool include_amin, bool include_amax) +{ + db::InternalAngleEdgePairFilter f (amin, include_amin, amax, include_amax, false); + return as_2edge_pairs_vector (r->split_filter (f)); +} + static db::EdgePairs with_area1 (const db::EdgePairs *r, db::EdgePair::area_type a, bool inverse) { db::EdgePairFilterByArea f (a, a + 1, inverse); return r->filtered (f); } +static std::vector split_with_area1 (const db::EdgePairs *r, db::EdgePair::area_type a) +{ + db::EdgePairFilterByArea f (a, a + 1, false); + return as_2edge_pairs_vector (r->split_filter (f)); +} + static db::EdgePairs with_area2 (const db::EdgePairs *r, db::EdgePair::area_type amin, db::EdgePair::area_type amax, bool inverse) { db::EdgePairFilterByArea f (amin, amax, inverse); return r->filtered (f); } +static std::vector split_with_area2 (const db::EdgePairs *r, db::EdgePair::area_type amin, db::EdgePair::area_type amax) +{ + db::EdgePairFilterByArea f (amin, amax, false); + return as_2edge_pairs_vector (r->split_filter (f)); +} + extern Class decl_dbShapeCollection; Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", @@ -911,12 +1050,18 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been introduced in version 0.29.\n" ) + - method_ext ("filtered", &filtered, gsi::arg ("filtered"), + method_ext ("filtered", &filtered, gsi::arg ("filter"), "@brief Applies a generic filter and returns a filtered copy\n" "See \\EdgePairFilter for a description of this feature.\n" "\n" "This method has been introduced in version 0.29.\n" ) + + method_ext ("split_filter", &split_filter, gsi::arg ("filter"), + "@brief Applies a generic filter and returns a copy with all matching shapes and one with the non-matching ones\n" + "See \\EdgePairFilter for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("process", &process_epep, gsi::arg ("process"), "@brief Applies a generic edge pair processor in place (replacing the edge pairs from the EdgePairs collection)\n" "See \\EdgePairProcessor for a description of this feature.\n" @@ -959,6 +1104,18 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.27.1.\n" ) + + method_ext ("split_with_length", split_with_length1, gsi::arg ("length"), + "@brief Like \\with_length, but returning two edge pair collections\n" + "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_length", split_with_length2, gsi::arg ("min_length"), gsi::arg ("max_length"), + "@brief Like \\with_length, but returning two edge pair collections\n" + "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("with_length_both", with_length_both1, gsi::arg ("length"), gsi::arg ("inverse"), "@brief Filters the edge pairs by length of both of their edges\n" "Filters the edge pairs in the edge pair collection by length of both of their edges. If \"inverse\" is false, only " @@ -977,6 +1134,18 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.27.1.\n" ) + + method_ext ("split_with_length_both", split_with_length_both1, gsi::arg ("length"), + "@brief Like \\with_length_both, but returning two edge pair collections\n" + "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_length_both", split_with_length_both2, gsi::arg ("min_length"), gsi::arg ("max_length"), + "@brief Like \\with_length_both, but returning two edge pair collections\n" + "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("with_distance", with_distance1, gsi::arg ("distance"), gsi::arg ("inverse"), "@brief Filters the edge pairs by the distance of the edges\n" "Filters the edge pairs in the edge pair collection by distance of the edges. If \"inverse\" is false, only " @@ -997,6 +1166,18 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.27.1.\n" ) + + method_ext ("split_with_distance", split_with_distance1, gsi::arg ("distance"), + "@brief Like \\with_distance, but returning two edge pair collections\n" + "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_distance", split_with_distance2, gsi::arg ("min_distance"), gsi::arg ("max_distance"), + "@brief Like \\with_distance, but returning two edge pair collections\n" + "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("with_angle", with_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), "@brief Filter the edge pairs by orientation of their edges\n" "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " @@ -1038,22 +1219,6 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.27.1.\n" ) + - method_ext ("with_abs_angle", with_abs_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), - "@brief Filter the edge pairs by orientation of their edges\n" - "\n" - "This method behaves like \\with_angle, but angles are always positive - i.e. there is no " - "differentiation between edges sloping 'down' vs. edges sloping 'up.\n" - "\n" - "This method has been added in version 0.29.1.\n" - ) + - method_ext ("with_abs_angle", with_abs_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), - "@brief Filter the edge pairs by orientation of their edges\n" - "\n" - "This method behaves like \\with_angle, but angles are always positive - i.e. there is no " - "differentiation between edges sloping 'down' vs. edges sloping 'up.\n" - "\n" - "This method has been added in version 0.29.1.\n" - ) + method_ext ("with_angle", with_angle3, gsi::arg ("type"), gsi::arg ("inverse"), "@brief Filter the edge pairs by orientation of their edges\n" "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " @@ -1073,6 +1238,52 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.28.\n" ) + + method_ext ("split_with_angle", split_with_angle1, gsi::arg ("angle"), + "@brief Like \\with_angle, but returning two edge pair collections\n" + "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_angle", split_with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), + "@brief Like \\with_angle, but returning two edge pair collections\n" + "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_angle", split_with_angle3, gsi::arg ("type"), + "@brief Like \\with_angle, but returning two edge pair collections\n" + "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("with_abs_angle", with_abs_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), + "@brief Filter the edge pairs by orientation of their edges\n" + "\n" + "This method behaves like \\with_angle, but angles are always positive - i.e. there is no " + "differentiation between edges sloping 'down' vs. edges sloping 'up.\n" + "\n" + "This method has been added in version 0.29.1.\n" + ) + + method_ext ("with_abs_angle", with_abs_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), + "@brief Filter the edge pairs by orientation of their edges\n" + "\n" + "This method behaves like \\with_angle, but angles are always positive - i.e. there is no " + "differentiation between edges sloping 'down' vs. edges sloping 'up.\n" + "\n" + "This method has been added in version 0.29.1.\n" + ) + + method_ext ("split_with_abs_angle", split_with_abs_angle1, gsi::arg ("angle"), + "@brief Like \\with_abs_angle, but returning two edge pair collections\n" + "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_abs_angle", split_with_abs_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), + "@brief Like \\with_abs_angle, but returning two edge pair collections\n" + "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("with_angle_both", with_angle_both1, gsi::arg ("angle"), gsi::arg ("inverse"), "@brief Filter the edge pairs by orientation of both of their edges\n" "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " @@ -1114,21 +1325,6 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.27.1.\n" ) + - method_ext ("with_abs_angle_both", with_abs_angle_both1, gsi::arg ("angle"), gsi::arg ("inverse"), - "@brief Filter the edge pairs by orientation of both of their edges\n" - "\n" - "This method behaves like \\with_angle_both, but angles are always positive - i.e. there is no " - "differentiation between edges sloping 'down' vs. edges sloping 'up.\n" - "\n" - "This method has been added in version 0.29.1.\n" - ) + - method_ext ("with_abs_angle_both", with_abs_angle_both2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), - "\n" - "This method behaves like \\with_angle_both, but angles are always positive - i.e. there is no " - "differentiation between edges sloping 'down' vs. edges sloping 'up.\n" - "\n" - "This method has been added in version 0.29.1.\n" - ) + method_ext ("with_angle_both", with_angle_both3, gsi::arg ("type"), gsi::arg ("inverse"), "@brief Filter the edge pairs by orientation of their edges\n" "Filters the edge pairs in the edge pair collection by orientation. If \"inverse\" is false, only " @@ -1148,6 +1344,51 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.28.\n" ) + + method_ext ("split_with_angle_both", split_with_angle_both1, gsi::arg ("angle"), + "@brief Like \\with_angle_both, but returning two edge pair collections\n" + "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_angle_both", split_with_angle_both2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), + "@brief Like \\with_angle_both, but returning two edge pair collections\n" + "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_angle_both", split_with_angle_both3, gsi::arg ("type"), + "@brief Like \\with_angle_both, but returning two edge pair collections\n" + "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("with_abs_angle_both", with_abs_angle_both1, gsi::arg ("angle"), gsi::arg ("inverse"), + "@brief Filter the edge pairs by orientation of both of their edges\n" + "\n" + "This method behaves like \\with_angle_both, but angles are always positive - i.e. there is no " + "differentiation between edges sloping 'down' vs. edges sloping 'up.\n" + "\n" + "This method has been added in version 0.29.1.\n" + ) + + method_ext ("with_abs_angle_both", with_abs_angle_both2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), + "\n" + "This method behaves like \\with_angle_both, but angles are always positive - i.e. there is no " + "differentiation between edges sloping 'down' vs. edges sloping 'up.\n" + "\n" + "This method has been added in version 0.29.1.\n" + ) + + method_ext ("split_with_abs_angle_both", split_with_abs_angle_both1, gsi::arg ("angle"), + "@brief Like \\with_abs_angle_both, but returning two edge pair collections\n" + "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_abs_angle_both", split_with_abs_angle_both2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), + "@brief Like \\with_abs_angle_both, but returning two edge pair collections\n" + "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("with_area", with_area1, gsi::arg ("area"), gsi::arg ("inverse"), "@brief Filters the edge pairs by the enclosed area\n" "Filters the edge pairs in the edge pair collection by enclosed area. If \"inverse\" is false, only " @@ -1164,6 +1405,18 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.27.2.\n" ) + + method_ext ("split_with_area", split_with_area1, gsi::arg ("area"), + "@brief Like \\with_area, but returning two edge pair collections\n" + "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_area", split_with_area2, gsi::arg ("min_area"), gsi::arg ("max_area"), + "@brief Like \\with_area, but returning two edge pair collections\n" + "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("with_internal_angle", with_internal_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), "@brief Filters the edge pairs by the angle between their edges\n" "Filters the edge pairs in the edge pair collection by the angle between their edges. If \"inverse\" is false, only " @@ -1187,6 +1440,18 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.27.2.\n" ) + + method_ext ("split_with_internal_angle", split_with_internal_angle1, gsi::arg ("angle"), + "@brief Like \\with_internal_angle, but returning two edge pair collections\n" + "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_internal_angle", split_with_internal_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), + "@brief Like \\with_internal_angle, but returning two edge pair collections\n" + "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("polygons", &polygons1, "@brief Converts the edge pairs to polygons\n" "This method creates polygons from the edge pairs. Each polygon will be a triangle or quadrangle " diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 76181040e..51722c2f9 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -399,6 +399,11 @@ static db::Edges filtered (const db::Edges *r, const EdgeFilterImpl *f) return r->filtered (*f); } +static std::vector split_filter (const db::Edges *r, const EdgeFilterImpl *f) +{ + return as_2edges_vector (r->split_filter (*f)); +} + static void filter (db::Edges *r, const EdgeFilterImpl *f) { r->filter (*f); @@ -432,42 +437,84 @@ static db::Edges with_length1 (const db::Edges *r, db::Edges::distance_type leng return r->filtered (f); } +static std::vector split_with_length1 (const db::Edges *r, db::Edges::distance_type length) +{ + db::EdgeLengthFilter f (length, length + 1, false); + return as_2edges_vector (r->split_filter (f)); +} + static db::Edges with_length2 (const db::Edges *r, const tl::Variant &min, const tl::Variant &max, bool inverse) { db::EdgeLengthFilter f (min.is_nil () ? db::Edges::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), inverse); return r->filtered (f); } +static std::vector split_with_length2 (const db::Edges *r, const tl::Variant &min, const tl::Variant &max) +{ + db::EdgeLengthFilter f (min.is_nil () ? db::Edges::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), false); + return as_2edges_vector (r->split_filter (f)); +} + static db::Edges with_angle1 (const db::Edges *r, double a, bool inverse) { db::EdgeOrientationFilter f (a, inverse, false); return r->filtered (f); } +static std::vector split_with_angle1 (const db::Edges *r, double a) +{ + db::EdgeOrientationFilter f (a, false, false); + return as_2edges_vector (r->split_filter (f)); +} + static db::Edges with_angle2 (const db::Edges *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) { db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, false); return r->filtered (f); } +static std::vector split_with_angle2 (const db::Edges *r, double amin, double amax, bool include_amin, bool include_amax) +{ + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, false, false); + return as_2edges_vector (r->split_filter (f)); +} + static db::Edges with_abs_angle1 (const db::Edges *r, double a, bool inverse) { db::EdgeOrientationFilter f (a, inverse, true); return r->filtered (f); } +static std::vector split_with_abs_angle1 (const db::Edges *r, double a) +{ + db::EdgeOrientationFilter f (a, false, true); + return as_2edges_vector (r->split_filter (f)); +} + static db::Edges with_abs_angle2 (const db::Edges *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) { db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, true); return r->filtered (f); } +static std::vector split_with_abs_angle2 (const db::Edges *r, double amin, double amax, bool include_amin, bool include_amax) +{ + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, false, true); + return as_2edges_vector (r->split_filter (f)); +} + static db::Edges with_angle3 (const db::Edges *r, db::SpecialEdgeOrientationFilter::FilterType type, bool inverse) { db::SpecialEdgeOrientationFilter f (type, inverse); return r->filtered (f); } +static std::vector split_with_angle3 (const db::Edges *r, db::SpecialEdgeOrientationFilter::FilterType type) +{ + db::SpecialEdgeOrientationFilter f (type, false); + return as_2edges_vector (r->split_filter (f)); +} + static db::EdgePairs width2 (const db::Edges *r, db::Edges::coord_type d, bool whole_edges, db::metrics_type metrics, const tl::Variant &ignore_angle, const tl::Variant &min_projection, const tl::Variant &max_projection, db::zero_distance_mode zd_mode) { return r->width_check (d, db::EdgesCheckOptions (whole_edges, @@ -867,12 +914,18 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "This method has been introduced in version 0.29.\n" ) + - method_ext ("filtered", &filtered, gsi::arg ("filtered"), + method_ext ("filtered", &filtered, gsi::arg ("filter"), "@brief Applies a generic filter and returns a filtered copy\n" "See \\EdgeFilter for a description of this feature.\n" "\n" "This method has been introduced in version 0.29.\n" ) + + method_ext ("split_filter", &split_filter, gsi::arg ("filter"), + "@brief Applies a generic filter and returns a copy with all matching shapes and one with the non-matching ones\n" + "See \\EdgeFilter for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("process", &process_ee, gsi::arg ("process"), "@brief Applies a generic edge processor in place (replacing the edges from the Edges collection)\n" "See \\EdgeProcessor for a description of this feature.\n" @@ -913,6 +966,18 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "If you don't want to specify a lower or upper limit, pass nil to that parameter.\n" ) + + method_ext ("split_with_length", split_with_length1, gsi::arg ("length"), + "@brief Like \\with_length, but returning two edge collections\n" + "The first edge collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_length", split_with_length2, gsi::arg ("min_length"), gsi::arg ("max_length"), + "@brief Like \\with_length, but returning two edge collections\n" + "The first edge collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("with_angle", with_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), "@brief Filters the edges by orientation\n" "Filters the edges in the edge collection by orientation. If \"inverse\" is false, only " @@ -937,6 +1002,35 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "The two \"include..\" arguments have been added in version 0.27.\n" ) + + method_ext ("with_angle", with_angle3, gsi::arg ("type"), gsi::arg ("inverse"), + "@brief Filters the edges by orientation type\n" + "Filters the edges in the edge collection by orientation. If \"inverse\" is false, only " + "edges which have an angle of the given type are returned. If \"inverse\" is true, " + "edges which do not conform to this criterion are returned.\n" + "\n" + "This version allows specifying an edge type instead of an angle. Edge types include multiple distinct orientations " + "and are specified using one of the \\OrthoEdges, \\DiagonalEdges or \\OrthoDiagonalEdges types.\n" + "\n" + "This method has been added in version 0.28.\n" + ) + + method_ext ("split_with_angle", split_with_angle1, gsi::arg ("angle"), + "@brief Like \\with_angle, but returning two edge collections\n" + "The first edge collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_angle", split_with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), + "@brief Like \\with_angle, but returning two edge collections\n" + "The first edge collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_angle", split_with_angle3, gsi::arg ("type"), + "@brief Like \\with_angle, but returning two edge collections\n" + "The first edge collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("with_abs_angle", with_abs_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), "@brief Filter the edges by orientation\n" "\n" @@ -953,16 +1047,17 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "This method has been added in version 0.29.1.\n" ) + - method_ext ("with_angle", with_angle3, gsi::arg ("type"), gsi::arg ("inverse"), - "@brief Filters the edges by orientation type\n" - "Filters the edges in the edge collection by orientation. If \"inverse\" is false, only " - "edges which have an angle of the given type are returned. If \"inverse\" is true, " - "edges which do not conform to this criterion are returned.\n" + method_ext ("split_with_abs_angle", split_with_abs_angle1, gsi::arg ("angle"), + "@brief Like \\with_abs_angle, but returning two edge collections\n" + "The first edge collection will contain all matching shapes, the other the non-matching ones.\n" "\n" - "This version allows specifying an edge type instead of an angle. Edge types include multiple distinct orientations " - "and are specified using one of the \\OrthoEdges, \\DiagonalEdges or \\OrthoDiagonalEdges types.\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_abs_angle", split_with_abs_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), + "@brief Like \\with_abs_angle, but returning two edge collections\n" + "The first edge collection will contain all matching shapes, the other the non-matching ones.\n" "\n" - "This method has been added in version 0.28.\n" + "This method has been introduced in version 0.29.12.\n" ) + method ("insert", (void (db::Edges::*)(const db::Edge &)) &db::Edges::insert, gsi::arg ("edge"), "@brief Inserts an edge\n" diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 9294f23d4..0775c78ec 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -513,6 +513,11 @@ static db::Region filtered (const db::Region *r, const PolygonFilterImpl *f) return r->filtered (*f); } +static std::vector split_filter (const db::Region *r, const PolygonFilterImpl *f) +{ + return as_2region_vector (r->split_filter (*f)); +} + static void filter (db::Region *r, const PolygonFilterImpl *f) { r->filter (*f); @@ -544,84 +549,240 @@ static db::Region with_perimeter1 (const db::Region *r, db::Region::perimeter_ty return r->filtered (f); } +static std::vector split_with_perimeter1 (const db::Region *r, db::Region::perimeter_type perimeter) +{ + db::RegionPerimeterFilter f (perimeter, perimeter + 1, false); + return as_2region_vector (r->split_filter (f)); +} + static db::Region with_perimeter2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max, bool inverse) { db::RegionPerimeterFilter f (min.is_nil () ? db::Region::perimeter_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), inverse); return r->filtered (f); } +static std::vector split_with_perimeter2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max) +{ + db::RegionPerimeterFilter f (min.is_nil () ? db::Region::perimeter_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), false); + return as_2region_vector (r->split_filter (f)); +} + static db::Region with_area1 (const db::Region *r, db::Region::area_type area, bool inverse) { db::RegionAreaFilter f (area, area + 1, inverse); return r->filtered (f); } +static std::vector split_with_area1 (const db::Region *r, db::Region::area_type area) +{ + db::RegionAreaFilter f (area, area + 1, false); + return as_2region_vector (r->split_filter (f)); +} + static db::Region with_area2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max, bool inverse) { db::RegionAreaFilter f (min.is_nil () ? db::Region::area_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), inverse); return r->filtered (f); } +static std::vector split_with_area2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max) +{ + db::RegionAreaFilter f (min.is_nil () ? db::Region::area_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), false); + return as_2region_vector (r->split_filter (f)); +} + static db::Region with_holes1 (const db::Region *r, size_t n, bool inverse) { db::HoleCountFilter f (n, n + 1, inverse); return r->filtered (f); } +static std::vector split_with_holes1 (const db::Region *r, size_t n) +{ + db::HoleCountFilter f (n, n + 1, false); + return as_2region_vector (r->split_filter (f)); +} + static db::Region with_holes2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max, bool inverse) { db::HoleCountFilter f (min.is_nil () ? size_t (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), inverse); return r->filtered (f); } +static std::vector split_with_holes2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max) +{ + db::HoleCountFilter f (min.is_nil () ? size_t (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), false); + return as_2region_vector (r->split_filter (f)); +} + static db::Region with_bbox_width1 (const db::Region *r, db::Region::distance_type bbox_width, bool inverse) { db::RegionBBoxFilter f (bbox_width, bbox_width + 1, inverse, db::RegionBBoxFilter::BoxWidth); return r->filtered (f); } +static std::vector split_with_bbox_width1 (const db::Region *r, db::Region::distance_type bbox_width) +{ + db::RegionBBoxFilter f (bbox_width, bbox_width + 1, false, db::RegionBBoxFilter::BoxWidth); + return as_2region_vector (r->split_filter (f)); +} + static db::Region with_bbox_width2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max, bool inverse) { db::RegionBBoxFilter f (min.is_nil () ? db::Region::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), inverse, db::RegionBBoxFilter::BoxWidth); return r->filtered (f); } +static std::vector split_with_bbox_width2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max) +{ + db::RegionBBoxFilter f (min.is_nil () ? db::Region::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), false, db::RegionBBoxFilter::BoxWidth); + return as_2region_vector (r->split_filter (f)); +} + static db::Region with_bbox_height1 (const db::Region *r, db::Region::distance_type bbox_height, bool inverse) { db::RegionBBoxFilter f (bbox_height, bbox_height + 1, inverse, db::RegionBBoxFilter::BoxHeight); return r->filtered (f); } +static std::vector split_with_bbox_height1 (const db::Region *r, db::Region::distance_type bbox_height) +{ + db::RegionBBoxFilter f (bbox_height, bbox_height + 1, false, db::RegionBBoxFilter::BoxHeight); + return as_2region_vector (r->split_filter (f)); +} + static db::Region with_bbox_height2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max, bool inverse) { db::RegionBBoxFilter f (min.is_nil () ? db::Region::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), inverse, db::RegionBBoxFilter::BoxHeight); return r->filtered (f); } +static std::vector split_with_bbox_height2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max) +{ + db::RegionBBoxFilter f (min.is_nil () ? db::Region::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), false, db::RegionBBoxFilter::BoxHeight); + return as_2region_vector (r->split_filter (f)); +} + static db::Region with_bbox_min1 (const db::Region *r, db::Region::distance_type bbox_min, bool inverse) { db::RegionBBoxFilter f (bbox_min, bbox_min + 1, inverse, db::RegionBBoxFilter::BoxMinDim); return r->filtered (f); } +static std::vector split_with_bbox_min1 (const db::Region *r, db::Region::distance_type bbox_min) +{ + db::RegionBBoxFilter f (bbox_min, bbox_min + 1, false, db::RegionBBoxFilter::BoxMinDim); + return as_2region_vector (r->split_filter (f)); +} + static db::Region with_bbox_min2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max, bool inverse) { db::RegionBBoxFilter f (min.is_nil () ? db::Region::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), inverse, db::RegionBBoxFilter::BoxMinDim); return r->filtered (f); } +static std::vector split_with_bbox_min2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max) +{ + db::RegionBBoxFilter f (min.is_nil () ? db::Region::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), false, db::RegionBBoxFilter::BoxMinDim); + return as_2region_vector (r->split_filter (f)); +} + static db::Region with_bbox_max1 (const db::Region *r, db::Region::distance_type bbox_max, bool inverse) { db::RegionBBoxFilter f (bbox_max, bbox_max + 1, inverse, db::RegionBBoxFilter::BoxMaxDim); return r->filtered (f); } +static std::vector split_with_bbox_max1 (const db::Region *r, db::Region::distance_type bbox_max) +{ + db::RegionBBoxFilter f (bbox_max, bbox_max + 1, false, db::RegionBBoxFilter::BoxMaxDim); + return as_2region_vector (r->split_filter (f)); +} + static db::Region with_bbox_max2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max, bool inverse) { db::RegionBBoxFilter f (min.is_nil () ? db::Region::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), inverse, db::RegionBBoxFilter::BoxMaxDim); return r->filtered (f); } +static std::vector split_with_bbox_max2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max) +{ + db::RegionBBoxFilter f (min.is_nil () ? db::Region::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), false, db::RegionBBoxFilter::BoxMaxDim); + return as_2region_vector (r->split_filter (f)); +} + +static db::Region with_bbox_aspect_ratio1 (const db::Region *r, double v, bool inverse) +{ + db::RegionRatioFilter f (v, true, v, true, inverse, db::RegionRatioFilter::AspectRatio); + return r->filtered (f); +} + +static std::vector split_with_bbox_aspect_ratio1 (const db::Region *r, double v) +{ + db::RegionRatioFilter f (v, true, v, true, false, db::RegionRatioFilter::AspectRatio); + return as_2region_vector (r->split_filter (f)); +} + +static db::Region with_bbox_aspect_ratio2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max, bool inverse, bool min_included, bool max_included) +{ + db::RegionRatioFilter f (min.is_nil () ? 0.0 : min.to (), min_included, max.is_nil () ? std::numeric_limits ::max () : max.to (), max_included, inverse, db::RegionRatioFilter::AspectRatio); + return r->filtered (f); +} + +static std::vector split_with_bbox_aspect_ratio2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max, bool min_included, bool max_included) +{ + db::RegionRatioFilter f (min.is_nil () ? 0.0 : min.to (), min_included, max.is_nil () ? std::numeric_limits ::max () : max.to (), max_included, false, db::RegionRatioFilter::AspectRatio); + return as_2region_vector (r->split_filter (f)); +} + +static db::Region with_area_ratio1 (const db::Region *r, double v, bool inverse) +{ + db::RegionRatioFilter f (v, true, v, true, inverse, db::RegionRatioFilter::AreaRatio); + return r->filtered (f); +} + +static std::vector split_with_area_ratio1 (const db::Region *r, double v) +{ + db::RegionRatioFilter f (v, true, v, true, false, db::RegionRatioFilter::AreaRatio); + return as_2region_vector (r->split_filter (f)); +} + +static db::Region with_area_ratio2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max, bool inverse, bool min_included, bool max_included) +{ + db::RegionRatioFilter f (min.is_nil () ? 0.0 : min.to (), min_included, max.is_nil () ? std::numeric_limits ::max () : max.to (), max_included, inverse, db::RegionRatioFilter::AreaRatio); + return r->filtered (f); +} + +static std::vector split_with_area_ratio2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max, bool min_included, bool max_included) +{ + db::RegionRatioFilter f (min.is_nil () ? 0.0 : min.to (), min_included, max.is_nil () ? std::numeric_limits ::max () : max.to (), max_included, false, db::RegionRatioFilter::AreaRatio); + return as_2region_vector (r->split_filter (f)); +} + +static db::Region with_relative_height1 (const db::Region *r, double v, bool inverse) +{ + db::RegionRatioFilter f (v, true, v, true, inverse, db::RegionRatioFilter::RelativeHeight); + return r->filtered (f); +} + +static std::vector split_with_relative_height1 (const db::Region *r, double v) +{ + db::RegionRatioFilter f (v, true, v, true, false, db::RegionRatioFilter::RelativeHeight); + return as_2region_vector (r->split_filter (f)); +} + +static db::Region with_relative_height2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max, bool inverse, bool min_included, bool max_included) +{ + db::RegionRatioFilter f (min.is_nil () ? 0.0 : min.to (), min_included, max.is_nil () ? std::numeric_limits ::max () : max.to (), max_included, inverse, db::RegionRatioFilter::RelativeHeight); + return r->filtered (f); +} + +static std::vector split_with_relative_height2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max, bool min_included, bool max_included) +{ + db::RegionRatioFilter f (min.is_nil () ? 0.0 : min.to (), min_included, max.is_nil () ? std::numeric_limits ::max () : max.to (), max_included, false, db::RegionRatioFilter::RelativeHeight); + return as_2region_vector (r->split_filter (f)); +} + static db::EdgePairs angle_check1 (const db::Region *r, double angle, bool inverse) { return r->angle_check (angle, angle + 1e-4, inverse); @@ -632,42 +793,6 @@ static db::EdgePairs angle_check2 (const db::Region *r, double amin, double amax return r->angle_check (amin, amax, inverse); } -static db::Region with_bbox_aspect_ratio1 (const db::Region *r, double v, bool inverse) -{ - db::RegionRatioFilter f (v, true, v, true, inverse, db::RegionRatioFilter::AspectRatio); - return r->filtered (f); -} - -static db::Region with_bbox_aspect_ratio2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max, bool inverse, bool min_included, bool max_included) -{ - db::RegionRatioFilter f (min.is_nil () ? 0.0 : min.to (), min_included, max.is_nil () ? std::numeric_limits ::max () : max.to (), max_included, inverse, db::RegionRatioFilter::AspectRatio); - return r->filtered (f); -} - -static db::Region with_area_ratio1 (const db::Region *r, double v, bool inverse) -{ - db::RegionRatioFilter f (v, true, v, true, inverse, db::RegionRatioFilter::AreaRatio); - return r->filtered (f); -} - -static db::Region with_area_ratio2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max, bool inverse, bool min_included, bool max_included) -{ - db::RegionRatioFilter f (min.is_nil () ? 0.0 : min.to (), min_included, max.is_nil () ? std::numeric_limits ::max () : max.to (), max_included, inverse, db::RegionRatioFilter::AreaRatio); - return r->filtered (f); -} - -static db::Region with_relative_height1 (const db::Region *r, double v, bool inverse) -{ - db::RegionRatioFilter f (v, true, v, true, inverse, db::RegionRatioFilter::RelativeHeight); - return r->filtered (f); -} - -static db::Region with_relative_height2 (const db::Region *r, const tl::Variant &min, const tl::Variant &max, bool inverse, bool min_included, bool max_included) -{ - db::RegionRatioFilter f (min.is_nil () ? 0.0 : min.to (), min_included, max.is_nil () ? std::numeric_limits ::max () : max.to (), max_included, inverse, db::RegionRatioFilter::RelativeHeight); - return r->filtered (f); -} - static db::Region in (const db::Region *r, const db::Region &other) { return r->in (other, false); @@ -695,6 +820,12 @@ static db::Region non_rectangles (const db::Region *r) return r->filtered (f); } +static std::vector split_rectangles (const db::Region *r) +{ + db::RectangleFilter f (false, false); + return as_2region_vector (r->split_filter (f)); +} + static db::Region squares (const db::Region *r) { db::RectangleFilter f (true, false); @@ -707,6 +838,12 @@ static db::Region non_squares (const db::Region *r) return r->filtered (f); } +static std::vector split_squares (const db::Region *r) +{ + db::RectangleFilter f (true, false); + return as_2region_vector (r->split_filter (f)); +} + static db::Region rectilinear (const db::Region *r) { db::RectilinearFilter f (false); @@ -719,6 +856,12 @@ static db::Region non_rectilinear (const db::Region *r) return r->filtered (f); } +static std::vector split_rectilinear (const db::Region *r) +{ + db::RectilinearFilter f (false); + return as_2region_vector (r->split_filter (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)); @@ -1416,6 +1559,18 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n" ) + + method_ext ("split_with_perimeter", split_with_perimeter1, gsi::arg ("perimeter"), + "@brief Like \\with_perimeter, but returning two regions\n" + "The first region will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_perimeter", split_with_perimeter2, gsi::arg ("min_perimeter"), gsi::arg ("max_perimeter"), + "@brief Like \\with_perimeter, but returning two regions\n" + "The first region will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("with_area", with_area1, gsi::arg ("area"), gsi::arg ("inverse"), "@brief Filter the polygons by area\n" "Filters the polygons of the region by area. If \"inverse\" is false, only " @@ -1436,6 +1591,18 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n" ) + + method_ext ("split_with_area", split_with_area1, gsi::arg ("area"), + "@brief Like \\with_area, but returning two regions\n" + "The first region will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_area", split_with_area2, gsi::arg ("min_area"), gsi::arg ("max_area"), + "@brief Like \\with_area, but returning two regions\n" + "The first region will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("with_holes", with_holes1, gsi::arg ("nholes"), gsi::arg ("inverse"), "@brief Filters the polygons by their number of holes\n" "Filters the polygons of the region by number of holes. If \"inverse\" is false, only " @@ -1446,7 +1613,7 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This method has been introduced in version 0.27.\n" ) + - method_ext ("with_holes", with_holes2, gsi::arg ("min_bholes"), gsi::arg ("max_nholes"), gsi::arg ("inverse"), + method_ext ("with_holes", with_holes2, gsi::arg ("min_nholes"), gsi::arg ("max_nholes"), gsi::arg ("inverse"), "@brief Filter the polygons by their number of holes\n" "Filters the polygons of the region by number of holes. If \"inverse\" is false, only " "polygons which have a hole count larger or equal to \"min_nholes\" and less than \"max_nholes\" are " @@ -1460,6 +1627,18 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This method has been introduced in version 0.27.\n" ) + + method_ext ("split_with_holes", split_with_holes1, gsi::arg ("nholes"), + "@brief Like \\with_holes, but returning two regions\n" + "The first region will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_holes", split_with_holes2, gsi::arg ("min_nholes"), gsi::arg ("max_nholes"), + "@brief Like \\with_holes, but returning two regions\n" + "The first region will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("with_bbox_width", with_bbox_width1, gsi::arg ("width"), gsi::arg ("inverse"), "@brief Filter the polygons by bounding box width\n" "Filters the polygons of the region by the width of their bounding box. If \"inverse\" is false, only " @@ -1478,6 +1657,18 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n" ) + + method_ext ("split_with_bbox_width", split_with_bbox_width1, gsi::arg ("width"), + "@brief Like \\with_bbox_width, but returning two regions\n" + "The first region will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_bbox_width", split_with_bbox_width2, gsi::arg ("min_width"), gsi::arg ("max_width"), + "@brief Like \\with_bbox_width, but returning two regions\n" + "The first region will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("with_bbox_height", with_bbox_height1, gsi::arg ("height"), gsi::arg ("inverse"), "@brief Filter the polygons by bounding box height\n" "Filters the polygons of the region by the height of their bounding box. If \"inverse\" is false, only " @@ -1496,6 +1687,18 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n" ) + + method_ext ("split_with_bbox_height", split_with_bbox_height1, gsi::arg ("height"), + "@brief Like \\with_bbox_height, but returning two regions\n" + "The first region will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_bbox_height", split_with_bbox_height2, gsi::arg ("min_height"), gsi::arg ("max_height"), + "@brief Like \\with_bbox_height, but returning two regions\n" + "The first region will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("with_bbox_min", with_bbox_min1, gsi::arg ("dim"), gsi::arg ("inverse"), "@brief Filter the polygons by bounding box width or height, whichever is smaller\n" "Filters the polygons inside the region by the minimum dimension of their bounding box. " @@ -1516,6 +1719,18 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n" ) + + method_ext ("split_with_bbox_min", split_with_bbox_min1, gsi::arg ("dim"), + "@brief Like \\with_bbox_min, but returning two regions\n" + "The first region will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_bbox_min", split_with_bbox_min2, gsi::arg ("min_dim"), gsi::arg ("max_dim"), + "@brief Like \\with_bbox_min, but returning two regions\n" + "The first region will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("with_bbox_max", with_bbox_max1, gsi::arg ("dim"), gsi::arg ("inverse"), "@brief Filter the polygons by bounding box width or height, whichever is larger\n" "Filters the polygons of the region by the maximum dimension of their bounding box. " @@ -1536,6 +1751,18 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n" ) + + method_ext ("split_with_bbox_max", split_with_bbox_max1, gsi::arg ("dim"), + "@brief Like \\with_bbox_max, but returning two regions\n" + "The first region will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_bbox_max", split_with_bbox_max2, gsi::arg ("min_dim"), gsi::arg ("max_dim"), + "@brief Like \\with_bbox_max, but returning two regions\n" + "The first region will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("with_bbox_aspect_ratio", with_bbox_aspect_ratio1, gsi::arg ("ratio"), gsi::arg ("inverse"), "@brief Filters the polygons by the aspect ratio of their bounding boxes\n" "Filters the polygons of the region by the aspect ratio of their bounding boxes. " @@ -1565,6 +1792,18 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This method has been introduced in version 0.27.\n" ) + + method_ext ("split_with_bbox_aspect_ratio", split_with_bbox_aspect_ratio1, gsi::arg ("ratio"), + "@brief Like \\with_bbox_aspect_ratio, but returning two regions\n" + "The first region will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_bbox_aspect_ratio", split_with_bbox_aspect_ratio2, gsi::arg ("min_ratio"), gsi::arg ("max_ratio"), gsi::arg ("min_included", true), gsi::arg ("max_included", true), + "@brief Like \\with_bbox_aspect_ratio, but returning two regions\n" + "The first region will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("with_area_ratio", with_area_ratio1, gsi::arg ("ratio"), gsi::arg ("inverse"), "@brief Filters the polygons by the bounding box area to polygon area ratio\n" "The area ratio is defined by the ratio of bounding box area to polygon area. It's a measure " @@ -1594,6 +1833,18 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This method has been introduced in version 0.27.\n" ) + + method_ext ("split_with_area_ratio", split_with_area_ratio1, gsi::arg ("ratio"), + "@brief Like \\with_area_ratio, but returning two regions\n" + "The first region will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_area_ratio", split_with_area_ratio2, gsi::arg ("min_ratio"), gsi::arg ("max_ratio"), gsi::arg ("min_included", true), gsi::arg ("max_included", true), + "@brief Like \\with_area_ratio, but returning two regions\n" + "The first region will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("with_relative_height", with_relative_height1, gsi::arg ("ratio"), gsi::arg ("inverse"), "@brief Filters the polygons by the ratio of height to width\n" "This method filters the polygons of the region by the ratio of height vs. width of their bounding boxes. " @@ -1625,6 +1876,18 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This method has been introduced in version 0.27.\n" ) + + method_ext ("split_with_relative_height", split_with_relative_height1, gsi::arg ("ratio"), + "@brief Like \\with_relative_height, but returning two regions\n" + "The first region will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + + method_ext ("split_with_relative_height", split_with_relative_height2, gsi::arg ("min_ratio"), gsi::arg ("max_ratio"), gsi::arg ("min_included", true), gsi::arg ("max_included", true), + "@brief Like \\with_relative_height, but returning two regions\n" + "The first region will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method ("strange_polygon_check", &db::Region::strange_polygon_check, "@brief Returns a region containing those parts of polygons which are \"strange\"\n" "Strange parts of polygons are self-overlapping parts or non-orientable parts (i.e. in the \"8\" configuration).\n" @@ -2849,12 +3112,18 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This method has been introduced in version 0.29.\n" ) + - method_ext ("filtered", &filtered, gsi::arg ("filtered"), + method_ext ("filtered", &filtered, gsi::arg ("filter"), "@brief Applies a generic filter and returns a filtered copy\n" "See \\PolygonFilter for a description of this feature.\n" "\n" "This method has been introduced in version 0.29.\n" ) + + method_ext ("split_filter", &split_filter, gsi::arg ("filter"), + "@brief Applies a generic filter and returns a copy with all matching shapes and one with the non-matching ones\n" + "See \\PolygonFilter for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("process", &process_pp, gsi::arg ("process"), "@brief Applies a generic polygon processor in place (replacing the polygons from the Region)\n" "See \\PolygonProcessor for a description of this feature.\n" @@ -2889,6 +3158,12 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "This method returns all polygons in self which are not rectangles." "Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n" ) + + method_ext ("split_rectangles", &split_rectangles, + "@brief Combined results of \\rectangles and \\non_rectangles\n" + "This method returns a list with two Regions, the first is the result of \\rectangles, the second the result of \\non_rectangles. " + "Using this method is faster when you need both.\n\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("squares", &squares, "@brief Returns all polygons which are squares\n" "This method returns all polygons in self which are squares." @@ -2903,6 +3178,12 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This method has been introduced in version 0.27.\n" ) + + method_ext ("split_squares", &split_squares, + "@brief Combined results of \\squares and \\non_squares\n" + "This method returns a list with two Regions, the first is the result of \\squares, the second the result of \\non_squares. " + "Using this method is faster when you need both.\n\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("rectilinear", &rectilinear, "@brief Returns all polygons which are rectilinear\n" "This method returns all polygons in self which are rectilinear." @@ -2913,6 +3194,12 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "This method returns all polygons in self which are not rectilinear." "Merged semantics applies for this method (see \\merged_semantics= for a description of this concept)\n" ) + + method_ext ("split_rectilinear", &split_rectilinear, + "@brief Combined results of \\rectilinear and \\non_rectilinear\n" + "This method returns a list with two Regions, the first is the result of \\rectilinear, the second the result of \\non_rectilinear. " + "Using this method is faster when you need both.\n\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("break_polygons|#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" diff --git a/src/db/db/gsiDeclDbTexts.cc b/src/db/db/gsiDeclDbTexts.cc index 241e734c4..227a754c3 100644 --- a/src/db/db/gsiDeclDbTexts.cc +++ b/src/db/db/gsiDeclDbTexts.cc @@ -187,6 +187,15 @@ Class > decl_TextToPolygonP // --------------------------------------------------------------------------------- // Texts binding +static inline std::vector as_2texts_vector (const std::pair &rp) +{ + std::vector res; + res.reserve (2); + res.push_back (db::Texts (const_cast (rp.first).take_delegate ())); + res.push_back (db::Texts (const_cast (rp.second).take_delegate ())); + return res; +} + static db::Texts *new_v () { return new db::Texts (); @@ -316,6 +325,11 @@ static void filter (db::Texts *r, const TextFilterImpl *f) r->filter (*f); } +static std::vector split_filter (const db::Texts *r, const TextFilterImpl *f) +{ + return as_2texts_vector (r->split_filter (*f)); +} + static db::Texts processed_tt (const db::Texts *r, const shape_processor_impl *f) { return r->processed (*f); @@ -339,12 +353,24 @@ static db::Texts with_text (const db::Texts *r, const std::string &text, bool in return r->filtered (f); } +static std::vector split_with_text (const db::Texts *r, const std::string &text) +{ + db::TextStringFilter f (text, false); + return as_2texts_vector (r->split_filter (f)); +} + static db::Texts with_match (const db::Texts *r, const std::string &pattern, bool inverse) { db::TextPatternFilter f (pattern, inverse); return r->filtered (f); } +static std::vector split_with_match (const db::Texts *r, const std::string &pattern) +{ + db::TextPatternFilter f (pattern, false); + return as_2texts_vector (r->split_filter (f)); +} + static db::Region pull_interacting (const db::Texts *r, const db::Region &other) { db::Region out; @@ -600,6 +626,12 @@ Class decl_Texts (decl_dbShapeCollection, "db", "Texts", "\n" "This method has been introduced in version 0.29.\n" ) + + method_ext ("split_filter", &split_filter, gsi::arg ("filter"), + "@brief Applies a generic filter and returns a copy with all matching shapes and one with the non-matching ones\n" + "See \\TextFilter for a description of this feature.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("process", &process_tt, gsi::arg ("process"), "@brief Applies a generic text processor in place (replacing the texts from the text collection)\n" "See \\TextProcessor for a description of this feature.\n" @@ -623,12 +655,24 @@ Class decl_Texts (decl_dbShapeCollection, "db", "Texts", "If \"inverse\" is false, this method returns the texts with the given string.\n" "If \"inverse\" is true, this method returns the texts not having the given string.\n" ) + + method_ext ("split_with_text", split_with_text, gsi::arg ("text"), + "@brief Like \\with_text, but returning two text collections\n" + "The first text collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method_ext ("with_match", with_match, gsi::arg ("pattern"), gsi::arg ("inverse"), "@brief Filter the text by glob pattern\n" "\"pattern\" is a glob-style pattern (e.g. \"A*\" will select all texts starting with a capital \"A\").\n" "If \"inverse\" is false, this method returns the texts matching the pattern.\n" "If \"inverse\" is true, this method returns the texts not matching the pattern.\n" ) + + method_ext ("split_with_match", split_with_match, gsi::arg ("pattern"), + "@brief Like \\with_match, but returning two text collections\n" + "The first text collection will contain all matching shapes, the other the non-matching ones.\n" + "\n" + "This method has been introduced in version 0.29.12.\n" + ) + method ("interacting|&", (db::Texts (db::Texts::*) (const db::Region &) const) &db::Texts::selected_interacting, gsi::arg ("other"), "@brief Returns the texts from this text collection which are inside or on the edge of polygons from the given region\n" "\n" From c6e7c47d6c2dff92af6601b15b03725c705de0cd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 2 Mar 2025 18:30:31 +0100 Subject: [PATCH 033/392] Some bug fixes, updated tests --- src/db/db/dbAsIfFlatRegion.cc | 2 +- src/db/db/gsiDeclDbEdgePairs.cc | 98 ++++++++++++++++++-------------- testdata/ruby/dbEdgePairsTest.rb | 41 ++++++++++++- testdata/ruby/dbEdgesTest.rb | 12 ++++ testdata/ruby/dbRegionTest.rb | 52 +++++++++++++++++ testdata/ruby/dbTextsTest.rb | 6 ++ 6 files changed, 167 insertions(+), 44 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index b4d14617b..7e9b2c9a1 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -424,7 +424,7 @@ AsIfFlatRegion::filtered_pair (const PolygonFilterBase &filter) const if (filter.selected (*p)) { new_region_true->insert (*p); } else { - new_region_true->insert (*p); + new_region_false->insert (*p); } } diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index e17e89c48..087031191 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -469,9 +469,9 @@ static db::EdgePairs with_length1 (const db::EdgePairs *r, db::EdgePairs::distan return r->filtered (ef); } -static std::vector split_with_length1 (const db::EdgePairs *r, db::EdgePairs::distance_type length) +static std::vector split_with_length1 (const db::EdgePairs *r, db::EdgePairs::distance_type length, bool inverse) { - db::EdgeLengthFilter f (length, length + 1, false); + db::EdgeLengthFilter f (length, length + 1, inverse); db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); return as_2edge_pairs_vector (r->split_filter (ef)); } @@ -483,9 +483,9 @@ static db::EdgePairs with_length2 (const db::EdgePairs *r, const tl::Variant &mi return r->filtered (ef); } -static std::vector split_with_length2 (const db::EdgePairs *r, const tl::Variant &min, const tl::Variant &max) +static std::vector split_with_length2 (const db::EdgePairs *r, const tl::Variant &min, const tl::Variant &max, bool inverse) { - db::EdgeLengthFilter f (min.is_nil () ? db::Edges::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), false); + db::EdgeLengthFilter f (min.is_nil () ? db::Edges::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), inverse); db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); return as_2edge_pairs_vector (r->split_filter (ef)); } @@ -497,9 +497,9 @@ static db::EdgePairs with_length_both1 (const db::EdgePairs *r, db::EdgePairs::d return r->filtered (ef); } -static std::vector split_with_length_both1 (const db::EdgePairs *r, db::EdgePairs::distance_type length) +static std::vector split_with_length_both1 (const db::EdgePairs *r, db::EdgePairs::distance_type length, bool inverse) { - db::EdgeLengthFilter f (length, length + 1, false); + db::EdgeLengthFilter f (length, length + 1, inverse); db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); return as_2edge_pairs_vector (r->split_filter (ef)); } @@ -511,9 +511,9 @@ static db::EdgePairs with_length_both2 (const db::EdgePairs *r, const tl::Varian return r->filtered (ef); } -static std::vector split_with_length_both2 (const db::EdgePairs *r, const tl::Variant &min, const tl::Variant &max) +static std::vector split_with_length_both2 (const db::EdgePairs *r, const tl::Variant &min, const tl::Variant &max, bool inverse) { - db::EdgeLengthFilter f (min.is_nil () ? db::Edges::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), false); + db::EdgeLengthFilter f (min.is_nil () ? db::Edges::distance_type (0) : min.to (), max.is_nil () ? std::numeric_limits ::max () : max.to (), inverse); db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); return as_2edge_pairs_vector (r->split_filter (ef)); } @@ -525,9 +525,9 @@ static db::EdgePairs with_angle1 (const db::EdgePairs *r, double a, bool inverse return r->filtered (ef); } -static std::vector split_with_angle1 (const db::EdgePairs *r, double a) +static std::vector split_with_angle1 (const db::EdgePairs *r, double a, bool inverse) { - db::EdgeOrientationFilter f (a, false, false); + db::EdgeOrientationFilter f (a, inverse, false); db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); return as_2edge_pairs_vector (r->split_filter (ef)); } @@ -539,9 +539,9 @@ static db::EdgePairs with_angle2 (const db::EdgePairs *r, double amin, double am return r->filtered (ef); } -static std::vector split_with_angle2 (const db::EdgePairs *r, double amin, double amax, bool include_amin, bool include_amax) +static std::vector split_with_angle2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) { - db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, false, false); + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, false); db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); return as_2edge_pairs_vector (r->split_filter (ef)); } @@ -553,9 +553,9 @@ static db::EdgePairs with_abs_angle1 (const db::EdgePairs *r, double a, bool inv return r->filtered (ef); } -static std::vector split_with_abs_angle1 (const db::EdgePairs *r, double a) +static std::vector split_with_abs_angle1 (const db::EdgePairs *r, double a, bool inverse) { - db::EdgeOrientationFilter f (a, false, true); + db::EdgeOrientationFilter f (a, inverse, true); db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); return as_2edge_pairs_vector (r->split_filter (ef)); } @@ -567,9 +567,9 @@ static db::EdgePairs with_abs_angle2 (const db::EdgePairs *r, double amin, doubl return r->filtered (ef); } -static std::vector split_with_abs_angle2 (const db::EdgePairs *r, double amin, double amax, bool include_amin, bool include_amax) +static std::vector split_with_abs_angle2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) { - db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, false, true); + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, true); db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); return as_2edge_pairs_vector (r->split_filter (ef)); } @@ -581,9 +581,9 @@ static db::EdgePairs with_angle3 (const db::EdgePairs *r, db::SpecialEdgeOrienta return r->filtered (ef); } -static std::vector split_with_angle3 (const db::EdgePairs *r, db::SpecialEdgeOrientationFilter::FilterType type) +static std::vector split_with_angle3 (const db::EdgePairs *r, db::SpecialEdgeOrientationFilter::FilterType type, bool inverse) { - db::SpecialEdgeOrientationFilter f (type, false); + db::SpecialEdgeOrientationFilter f (type, inverse); db::EdgeFilterBasedEdgePairFilter ef (&f, true /*one must match*/); return as_2edge_pairs_vector (r->split_filter (ef)); } @@ -595,9 +595,9 @@ static db::EdgePairs with_angle_both1 (const db::EdgePairs *r, double a, bool in return r->filtered (ef); } -static std::vector split_with_angle_both1 (const db::EdgePairs *r, double a) +static std::vector split_with_angle_both1 (const db::EdgePairs *r, double a, bool inverse) { - db::EdgeOrientationFilter f (a, false, false); + db::EdgeOrientationFilter f (a, inverse, false); db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); return as_2edge_pairs_vector (r->split_filter (ef)); } @@ -609,9 +609,9 @@ static db::EdgePairs with_angle_both2 (const db::EdgePairs *r, double amin, doub return r->filtered (ef); } -static std::vector split_with_angle_both2 (const db::EdgePairs *r, double amin, double amax, bool include_amin, bool include_amax) +static std::vector split_with_angle_both2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) { - db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, false, false); + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, false); db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); return as_2edge_pairs_vector (r->split_filter (ef)); } @@ -623,9 +623,9 @@ static db::EdgePairs with_abs_angle_both1 (const db::EdgePairs *r, double a, boo return r->filtered (ef); } -static std::vector split_with_abs_angle_both1 (const db::EdgePairs *r, double a) +static std::vector split_with_abs_angle_both1 (const db::EdgePairs *r, double a, bool inverse) { - db::EdgeOrientationFilter f (a, false, true); + db::EdgeOrientationFilter f (a, inverse, true); db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); return as_2edge_pairs_vector (r->split_filter (ef)); } @@ -637,9 +637,9 @@ static db::EdgePairs with_abs_angle_both2 (const db::EdgePairs *r, double amin, return r->filtered (ef); } -static std::vector split_with_abs_angle_both2 (const db::EdgePairs *r, double amin, double amax, bool include_amin, bool include_amax) +static std::vector split_with_abs_angle_both2 (const db::EdgePairs *r, double amin, double amax, bool inverse, bool include_amin, bool include_amax) { - db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, false, true); + db::EdgeOrientationFilter f (amin, include_amin, amax, include_amax, inverse, true); db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); return as_2edge_pairs_vector (r->split_filter (ef)); } @@ -651,9 +651,9 @@ static db::EdgePairs with_angle_both3 (const db::EdgePairs *r, db::SpecialEdgeOr return r->filtered (ef); } -static std::vector split_with_angle_both3 (const db::EdgePairs *r, db::SpecialEdgeOrientationFilter::FilterType type) +static std::vector split_with_angle_both3 (const db::EdgePairs *r, db::SpecialEdgeOrientationFilter::FilterType type, bool inverse) { - db::SpecialEdgeOrientationFilter f (type, false); + db::SpecialEdgeOrientationFilter f (type, inverse); db::EdgeFilterBasedEdgePairFilter ef (&f, false /*both must match*/); return as_2edge_pairs_vector (r->split_filter (ef)); } @@ -1104,15 +1104,17 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.27.1.\n" ) + - method_ext ("split_with_length", split_with_length1, gsi::arg ("length"), + method_ext ("split_with_length", split_with_length1, gsi::arg ("length"), gsi::arg ("inverse"), "@brief Like \\with_length, but returning two edge pair collections\n" "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "Note that 'inverse' controls the way each edge is checked, not overall.\n" "\n" "This method has been introduced in version 0.29.12.\n" ) + - method_ext ("split_with_length", split_with_length2, gsi::arg ("min_length"), gsi::arg ("max_length"), + method_ext ("split_with_length", split_with_length2, gsi::arg ("min_length"), gsi::arg ("max_length"), gsi::arg ("inverse"), "@brief Like \\with_length, but returning two edge pair collections\n" "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "Note that 'inverse' controls the way each edge is checked, not overall.\n" "\n" "This method has been introduced in version 0.29.12.\n" ) + @@ -1134,15 +1136,17 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.27.1.\n" ) + - method_ext ("split_with_length_both", split_with_length_both1, gsi::arg ("length"), + method_ext ("split_with_length_both", split_with_length_both1, gsi::arg ("length"), gsi::arg ("inverse"), "@brief Like \\with_length_both, but returning two edge pair collections\n" "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "Note that 'inverse' controls the way each edge is checked, not overall.\n" "\n" "This method has been introduced in version 0.29.12.\n" ) + - method_ext ("split_with_length_both", split_with_length_both2, gsi::arg ("min_length"), gsi::arg ("max_length"), + method_ext ("split_with_length_both", split_with_length_both2, gsi::arg ("min_length"), gsi::arg ("max_length"), gsi::arg ("inverse"), "@brief Like \\with_length_both, but returning two edge pair collections\n" "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "Note that 'inverse' controls the way each edge is checked, not overall.\n" "\n" "This method has been introduced in version 0.29.12.\n" ) + @@ -1238,21 +1242,24 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.28.\n" ) + - method_ext ("split_with_angle", split_with_angle1, gsi::arg ("angle"), + method_ext ("split_with_angle", split_with_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), "@brief Like \\with_angle, but returning two edge pair collections\n" "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "Note that 'inverse' controls the way each edge is checked, not overall.\n" "\n" "This method has been introduced in version 0.29.12.\n" ) + - method_ext ("split_with_angle", split_with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), + method_ext ("split_with_angle", split_with_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), "@brief Like \\with_angle, but returning two edge pair collections\n" "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "Note that 'inverse' controls the way each edge is checked, not overall.\n" "\n" "This method has been introduced in version 0.29.12.\n" ) + - method_ext ("split_with_angle", split_with_angle3, gsi::arg ("type"), + method_ext ("split_with_angle", split_with_angle3, gsi::arg ("type"), gsi::arg ("inverse"), "@brief Like \\with_angle, but returning two edge pair collections\n" "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "Note that 'inverse' controls the way each edge is checked, not overall.\n" "\n" "This method has been introduced in version 0.29.12.\n" ) + @@ -1272,15 +1279,17 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.29.1.\n" ) + - method_ext ("split_with_abs_angle", split_with_abs_angle1, gsi::arg ("angle"), + method_ext ("split_with_abs_angle", split_with_abs_angle1, gsi::arg ("angle"), gsi::arg ("inverse"), "@brief Like \\with_abs_angle, but returning two edge pair collections\n" "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "Note that 'inverse' controls the way each edge is checked, not overall.\n" "\n" "This method has been introduced in version 0.29.12.\n" ) + - method_ext ("split_with_abs_angle", split_with_abs_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), + method_ext ("split_with_abs_angle", split_with_abs_angle2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), "@brief Like \\with_abs_angle, but returning two edge pair collections\n" "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "Note that 'inverse' controls the way each edge is checked, not overall.\n" "\n" "This method has been introduced in version 0.29.12.\n" ) + @@ -1344,21 +1353,24 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.28.\n" ) + - method_ext ("split_with_angle_both", split_with_angle_both1, gsi::arg ("angle"), + method_ext ("split_with_angle_both", split_with_angle_both1, gsi::arg ("angle"), gsi::arg ("inverse"), "@brief Like \\with_angle_both, but returning two edge pair collections\n" "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "Note that 'inverse' controls the way each edge is checked, not overall.\n" "\n" "This method has been introduced in version 0.29.12.\n" ) + - method_ext ("split_with_angle_both", split_with_angle_both2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), + method_ext ("split_with_angle_both", split_with_angle_both2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), "@brief Like \\with_angle_both, but returning two edge pair collections\n" "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "Note that 'inverse' controls the way each edge is checked, not overall.\n" "\n" "This method has been introduced in version 0.29.12.\n" ) + - method_ext ("split_with_angle_both", split_with_angle_both3, gsi::arg ("type"), + method_ext ("split_with_angle_both", split_with_angle_both3, gsi::arg ("type"), gsi::arg ("inverse"), "@brief Like \\with_angle_both, but returning two edge pair collections\n" "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "Note that 'inverse' controls the way each edge is checked, not overall.\n" "\n" "This method has been introduced in version 0.29.12.\n" ) + @@ -1377,15 +1389,17 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This method has been added in version 0.29.1.\n" ) + - method_ext ("split_with_abs_angle_both", split_with_abs_angle_both1, gsi::arg ("angle"), + method_ext ("split_with_abs_angle_both", split_with_abs_angle_both1, gsi::arg ("angle"), gsi::arg ("inverse"), "@brief Like \\with_abs_angle_both, but returning two edge pair collections\n" "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "Note that 'inverse' controls the way each edge is checked, not overall.\n" "\n" "This method has been introduced in version 0.29.12.\n" ) + - method_ext ("split_with_abs_angle_both", split_with_abs_angle_both2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), + method_ext ("split_with_abs_angle_both", split_with_abs_angle_both2, gsi::arg ("min_angle"), gsi::arg ("max_angle"), gsi::arg ("inverse"), gsi::arg ("include_min_angle", true), gsi::arg ("include_max_angle", false), "@brief Like \\with_abs_angle_both, but returning two edge pair collections\n" "The first edge pair collection will contain all matching shapes, the other the non-matching ones.\n" + "Note that 'inverse' controls the way each edge is checked, not overall.\n" "\n" "This method has been introduced in version 0.29.12.\n" ) + diff --git a/testdata/ruby/dbEdgePairsTest.rb b/testdata/ruby/dbEdgePairsTest.rb index 0c46601c7..3afa3458f 100644 --- a/testdata/ruby/dbEdgePairsTest.rb +++ b/testdata/ruby/dbEdgePairsTest.rb @@ -294,53 +294,90 @@ class DBEdgePairs_TestClass < TestBase ep4 = RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 10), RBA::Edge::new(10, 0, 10, 10)) r1 = RBA::EdgePairs::new([ ep1, ep2, ep3, ep4 ]) - assert_equal(r1.with_angle(0, 90, false).to_s, "") # @@@ + assert_equal(r1.with_angle(0, 90, false).to_s, "") assert_equal(r1.with_distance(10, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_distance(10)[0].to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_distance(5, 20, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_distance(5, 20)[0].to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_distance(15, 20, false).to_s, "") + assert_equal(r1.split_with_distance(15, 20)[0].to_s, "") assert_equal(r1.with_distance(15, 20, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_distance(15, 20)[1].to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_length(10, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_length(10, false)[0].to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_length(10, 20, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_length(10, 20, false)[0].to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_length(10, 21, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_length(10, 21, false)[0].to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_length(10, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0)") + assert_equal(r1.split_with_length(10, false)[1].to_s, "(0,0;0,20)/(10,20;10,0)") assert_equal(r1.with_length_both(10, false).to_s, "(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_length_both(10, false)[0].to_s, "(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_length_both(10, 20, false).to_s, "(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_length_both(10, 20, false)[0].to_s, "(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_length_both(10, 21, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_length_both(10, 21, false)[0].to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_length_both(10, true).to_s, "(0,0;0,20)/(10,20;10,0)") + assert_equal(r1.split_with_length_both(10, false)[1].to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0)") assert_equal(r1.with_angle(0, false).to_s, "") + assert_equal(r1.split_with_angle(0, false)[0].to_s, "") assert_equal(r1.with_abs_angle(0, false).to_s, "") + assert_equal(r1.split_with_abs_angle(0, false)[0].to_s, "") assert_equal(r1.with_angle(0, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_angle(0, false)[1].to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_abs_angle(0, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_abs_angle(0, false)[1].to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_angle(90, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_angle(90, false)[0].to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_abs_angle(90, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_abs_angle(90, false)[0].to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_angle(0, 90, false).to_s, "") + assert_equal(r1.split_with_angle(0, 90, false)[0].to_s, "") assert_equal(r1.with_abs_angle(0, 90, false).to_s, "") + assert_equal(r1.split_with_abs_angle(0, 90, false)[0].to_s, "") assert_equal(r1.with_angle(0, 90, false, true, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_angle(0, 90, false, true, true)[0].to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_abs_angle(0, 90, false, true, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_abs_angle(0, 90, false, true, true)[0].to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_angle_both(0, false).to_s, "") + assert_equal(r1.split_with_angle_both(0, false)[0].to_s, "") assert_equal(r1.with_abs_angle_both(0, false).to_s, "") + assert_equal(r1.split_with_abs_angle_both(0, false)[0].to_s, "") assert_equal(r1.with_angle_both(0, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_angle_both(0, false)[1].to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_abs_angle_both(0, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_abs_angle_both(0, false)[1].to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_angle_both(90, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_angle_both(90, false)[0].to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_abs_angle_both(90, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_abs_angle_both(90, false)[0].to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_angle_both(0, 90, false).to_s, "") assert_equal(r1.with_abs_angle_both(0, 90, false).to_s, "") assert_equal(r1.with_angle_both(0, 90, false, true, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_abs_angle_both(0, 90, false, true, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_area(0, false).to_s, "(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_area(0)[0].to_s, "(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_area(150, false).to_s, "(0,0;0,10)/(10,20;10,0)") + assert_equal(r1.split_with_area(150)[0].to_s, "(0,0;0,10)/(10,20;10,0)") assert_equal(r1.with_area(0, true).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0)") + assert_equal(r1.split_with_area(0)[1].to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0)") assert_equal(r1.with_area(150, 151, false).to_s, "(0,0;0,10)/(10,20;10,0)") + assert_equal(r1.split_with_area(150, 151)[0].to_s, "(0,0;0,10)/(10,20;10,0)") assert_equal(r1.with_area(150, 150, false).to_s, "") + assert_equal(r1.split_with_area(150, 150)[0].to_s, "") assert_equal(r1.with_area(150, 151, true).to_s, "(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_area(150, 151)[1].to_s, "(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_internal_angle(0, false).to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") + assert_equal(r1.split_with_internal_angle(0)[0].to_s, "(0,0;0,10)/(10,20;10,0);(0,0;0,10)/(10,0;10,20);(0,0;0,20)/(10,20;10,0);(0,0;0,10)/(10,0;10,10)") assert_equal(r1.with_internal_angle(0, 0, false).to_s, "") + assert_equal(r1.split_with_internal_angle(0, 0)[0].to_s, "") assert_equal(r1.with_internal_angle(0, true).to_s, "") + assert_equal(r1.split_with_internal_angle(0)[1].to_s, "") ep1 = RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 10), RBA::Edge::new(10, 20, 10, 0)) ep2 = RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 10), RBA::Edge::new(20, 0, 30, 0)) @@ -419,6 +456,8 @@ class DBEdgePairs_TestClass < TestBase edge_pairs.insert(RBA::EdgePair::new([200, 0, 300, 0], [200, 100, 220, 300 ])) assert_equal(edge_pairs.filtered(f).to_s, "(0,0;100,0)/(0,100;0,300)") + assert_equal(edge_pairs.split_filter(f)[0].to_s, "(0,0;100,0)/(0,100;0,300)") + assert_equal(edge_pairs.split_filter(f)[1].to_s, "(200,0;300,0)/(200,100;220,300)") assert_equal(edge_pairs.to_s, "(0,0;100,0)/(0,100;0,300);(200,0;300,0)/(200,100;220,300)") edge_pairs.filter(f) assert_equal(edge_pairs.to_s, "(0,0;100,0)/(0,100;0,300)") diff --git a/testdata/ruby/dbEdgesTest.rb b/testdata/ruby/dbEdgesTest.rb index 87bc8674a..329d6b7f1 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -585,25 +585,35 @@ class DBEdges_TestClass < TestBase r.insert(RBA::Edge::new(0, 0, 100, 0)) r.insert(RBA::Edge::new(100, 0, 100, 50)) assert_equal(r.with_angle(0, false).to_s, "(0,0;100,0)") + assert_equal(r.split_with_angle(0)[0].to_s, "(0,0;100,0)") assert_equal(r.with_abs_angle(0, false).to_s, "(0,0;100,0)") + assert_equal(r.split_with_abs_angle(0)[0].to_s, "(0,0;100,0)") assert_equal(r.with_angle(0, true).to_s, "(100,0;100,50)") + assert_equal(r.split_with_angle(0)[1].to_s, "(100,0;100,50)") assert_equal(r.with_abs_angle(0, true).to_s, "(100,0;100,50)") + assert_equal(r.split_with_abs_angle(0)[1].to_s, "(100,0;100,50)") assert_equal(r.with_angle(90, false).to_s, "(100,0;100,50)") assert_equal(r.with_abs_angle(90, false).to_s, "(100,0;100,50)") assert_equal(r.with_angle(90, true).to_s, "(0,0;100,0)") assert_equal(r.with_abs_angle(90, true).to_s, "(0,0;100,0)") assert_equal(r.with_angle(-10, 10, false).to_s, "(0,0;100,0)") + assert_equal(r.split_with_angle(-10, 10)[0].to_s, "(0,0;100,0)") assert_equal(r.with_abs_angle(-10, 10, false).to_s, "(0,0;100,0)") assert_equal(r.with_angle(-10, 10, true).to_s, "(100,0;100,50)") + assert_equal(r.split_with_angle(-10, 10)[1].to_s, "(100,0;100,50)") assert_equal(r.with_abs_angle(-10, 10, true).to_s, "(100,0;100,50)") assert_equal(r.with_angle(80, 100, false).to_s, "(100,0;100,50)") assert_equal(r.with_abs_angle(80, 100, false).to_s, "(100,0;100,50)") assert_equal(r.with_length(100, false).to_s, "(0,0;100,0)") + assert_equal(r.split_with_length(100)[0].to_s, "(0,0;100,0)") assert_equal(r.with_length(100, true).to_s, "(100,0;100,50)") + assert_equal(r.split_with_length(100)[1].to_s, "(100,0;100,50)") assert_equal(r.with_length(50, false).to_s, "(100,0;100,50)") assert_equal(r.with_length(50, true).to_s, "(0,0;100,0)") assert_equal(r.with_length(100, nil, false).to_s, "(0,0;100,0)") + assert_equal(r.split_with_length(100, nil)[0].to_s, "(0,0;100,0)") assert_equal(r.with_length(100, 200, true).to_s, "(100,0;100,50)") + assert_equal(r.split_with_length(100, 200)[1].to_s, "(100,0;100,50)") assert_equal(r.with_length(nil, 100, false).to_s, "(100,0;100,50)") r = RBA::Edges::new @@ -882,6 +892,8 @@ class DBEdges_TestClass < TestBase edges.insert(RBA::Edge::new(100, 100, 100, 200)) assert_equal(edges.filtered(f).to_s, "(100,0;200,100)") + assert_equal(edges.split_filter(f)[0].to_s, "(100,0;200,100)") + assert_equal(edges.split_filter(f)[1].to_s, "(100,100;100,200)") assert_equal(edges.to_s, "(100,0;200,100);(100,100;100,200)") edges.filter(f) assert_equal(edges.to_s, "(100,0;200,100)") diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index ea7b13feb..531fcea11 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -485,46 +485,68 @@ class DBRegion_TestClass < TestBase r.merged_semantics = false assert_equal(r.with_area(20000, false).to_s, "(0,0;0,200;100,200;100,0)") + assert_equal(r.split_with_area(20000)[0].to_s, "(0,0;0,200;100,200;100,0)") assert_equal(r.with_area(20000, true).to_s, "(0,0;0,100;400,100;400,0)") + assert_equal(r.split_with_area(20000)[1].to_s, "(0,0;0,100;400,100;400,0)") assert_equal(csort(r.with_area(10000, nil, false).to_s), csort("(0,0;0,200;100,200;100,0);(0,0;0,100;400,100;400,0)")) assert_equal(r.with_area(10000, 20000, false).to_s, "") assert_equal(r.with_area(nil, 20001, false).to_s, "(0,0;0,200;100,200;100,0)") assert_equal(csort(r.with_area(10000, 20000, true).to_s), csort("(0,0;0,200;100,200;100,0);(0,0;0,100;400,100;400,0)")) assert_equal(r.with_perimeter(600, false).to_s, "(0,0;0,200;100,200;100,0)") + assert_equal(r.split_with_perimeter(600)[0].to_s, "(0,0;0,200;100,200;100,0)") assert_equal(r.with_perimeter(600, true).to_s, "(0,0;0,100;400,100;400,0)") + assert_equal(r.split_with_perimeter(600)[1].to_s, "(0,0;0,100;400,100;400,0)") assert_equal(csort(r.with_perimeter(600, nil, false).to_s), csort("(0,0;0,200;100,200;100,0);(0,0;0,100;400,100;400,0)")) assert_equal(r.with_perimeter(600, 1000, false).to_s, "(0,0;0,200;100,200;100,0)") assert_equal(r.with_perimeter(nil, 1000, false).to_s, "(0,0;0,200;100,200;100,0)") + assert_equal(r.split_with_perimeter(nil, 1000)[0].to_s, "(0,0;0,200;100,200;100,0)") assert_equal(r.with_perimeter(600, 1000, true).to_s, "(0,0;0,100;400,100;400,0)") + assert_equal(r.split_with_perimeter(600, 1000)[1].to_s, "(0,0;0,100;400,100;400,0)") assert_equal(r.with_bbox_height(200, false).to_s, "(0,0;0,200;100,200;100,0)") + assert_equal(r.split_with_bbox_height(200)[0].to_s, "(0,0;0,200;100,200;100,0)") assert_equal(r.with_bbox_height(200, true).to_s, "(0,0;0,100;400,100;400,0)") + assert_equal(r.split_with_bbox_height(200)[1].to_s, "(0,0;0,100;400,100;400,0)") assert_equal(csort(r.with_bbox_height(100, nil, false).to_s), csort("(0,0;0,200;100,200;100,0);(0,0;0,100;400,100;400,0)")) assert_equal(r.with_bbox_height(100, 200, false).to_s, "(0,0;0,100;400,100;400,0)") assert_equal(csort(r.with_bbox_height(nil, 201, false).to_s), csort("(0,0;0,200;100,200;100,0);(0,0;0,100;400,100;400,0)")) + assert_equal(csort(r.split_with_bbox_height(nil, 201)[0].to_s), csort("(0,0;0,200;100,200;100,0);(0,0;0,100;400,100;400,0)")) assert_equal(r.with_bbox_height(100, 200, true).to_s, "(0,0;0,200;100,200;100,0)") + assert_equal(r.split_with_bbox_height(100, 200)[1].to_s, "(0,0;0,200;100,200;100,0)") assert_equal(r.with_bbox_width(400, false).to_s, "(0,0;0,100;400,100;400,0)") + assert_equal(r.split_with_bbox_width(400)[0].to_s, "(0,0;0,100;400,100;400,0)") assert_equal(r.with_bbox_width(400, true).to_s, "(0,0;0,200;100,200;100,0)") + assert_equal(r.split_with_bbox_width(400)[1].to_s, "(0,0;0,200;100,200;100,0)") assert_equal(csort(r.with_bbox_width(100, nil, false).to_s), csort("(0,0;0,200;100,200;100,0);(0,0;0,100;400,100;400,0)")) assert_equal(r.with_bbox_width(100, 400, false).to_s, "(0,0;0,200;100,200;100,0)") assert_equal(csort(r.with_bbox_width(nil, 401, false).to_s), csort("(0,0;0,200;100,200;100,0);(0,0;0,100;400,100;400,0)")) + assert_equal(csort(r.split_with_bbox_width(nil, 401)[0].to_s), csort("(0,0;0,200;100,200;100,0);(0,0;0,100;400,100;400,0)")) assert_equal(r.with_bbox_width(100, 400, true).to_s, "(0,0;0,100;400,100;400,0)") + assert_equal(r.split_with_bbox_width(100, 400)[1].to_s, "(0,0;0,100;400,100;400,0)") assert_equal(csort(r.with_bbox_min(100, false).to_s), csort("(0,0;0,200;100,200;100,0);(0,0;0,100;400,100;400,0)")) + assert_equal(csort(r.split_with_bbox_min(100)[0].to_s), csort("(0,0;0,200;100,200;100,0);(0,0;0,100;400,100;400,0)")) assert_equal(r.with_bbox_min(100, true).to_s, "") + assert_equal(r.split_with_bbox_min(100)[1].to_s, "") assert_equal(csort(r.with_bbox_min(100, nil, false).to_s), csort("(0,0;0,200;100,200;100,0);(0,0;0,100;400,100;400,0)")) assert_equal(csort(r.with_bbox_min(100, 101, false).to_s), csort("(0,0;0,200;100,200;100,0);(0,0;0,100;400,100;400,0)")) assert_equal(csort(r.with_bbox_min(nil, 101, false).to_s), csort("(0,0;0,200;100,200;100,0);(0,0;0,100;400,100;400,0)")) + assert_equal(csort(r.split_with_bbox_min(nil, 101)[0].to_s), csort("(0,0;0,200;100,200;100,0);(0,0;0,100;400,100;400,0)")) assert_equal(r.with_bbox_min(100, 101, true).to_s, "") + assert_equal(r.split_with_bbox_min(100, 101)[1].to_s, "") assert_equal(r.with_bbox_max(200, false).to_s, "(0,0;0,200;100,200;100,0)") + assert_equal(r.split_with_bbox_max(200)[0].to_s, "(0,0;0,200;100,200;100,0)") assert_equal(r.with_bbox_max(200, true).to_s, "(0,0;0,100;400,100;400,0)") + assert_equal(r.split_with_bbox_max(200)[1].to_s, "(0,0;0,100;400,100;400,0)") assert_equal(csort(r.with_bbox_max(200, nil, false).to_s), csort("(0,0;0,200;100,200;100,0);(0,0;0,100;400,100;400,0)")) assert_equal(r.with_bbox_max(200, 400, false).to_s, "(0,0;0,200;100,200;100,0)") assert_equal(r.with_bbox_max(nil, 400, false).to_s, "(0,0;0,200;100,200;100,0)") + assert_equal(r.split_with_bbox_max(nil, 400)[0].to_s, "(0,0;0,200;100,200;100,0)") assert_equal(r.with_bbox_max(200, 400, true).to_s, "(0,0;0,100;400,100;400,0)") + assert_equal(r.split_with_bbox_max(200, 400)[1].to_s, "(0,0;0,100;400,100;400,0)") end @@ -840,15 +862,23 @@ class DBRegion_TestClass < TestBase r1.merged_semantics = false assert_equal(csort(r1.rectangles.to_s), csort("(10,20;10,200;100,200;100,20);(50,70;50,270;150,270;150,70)")) + assert_equal(csort(r1.split_rectangles[0].to_s), csort("(10,20;10,200;100,200;100,20);(50,70;50,270;150,270;150,70)")) assert_equal(csort(r1.non_rectangles.to_s), csort("(0,0;100,100;100,0);(0,0;0,100;50,100;50,200;100,200;100,0)")) + assert_equal(csort(r1.split_rectangles[1].to_s), csort("(0,0;100,100;100,0);(0,0;0,100;50,100;50,200;100,200;100,0)")) assert_equal(csort(r1.rectilinear.to_s), csort("(10,20;10,200;100,200;100,20);(50,70;50,270;150,270;150,70);(0,0;0,100;50,100;50,200;100,200;100,0)")) + assert_equal(csort(r1.split_rectilinear[0].to_s), csort("(10,20;10,200;100,200;100,20);(50,70;50,270;150,270;150,70);(0,0;0,100;50,100;50,200;100,200;100,0)")) assert_equal(r1.non_rectilinear.to_s, "(0,0;100,100;100,0)") + assert_equal(r1.split_rectilinear[1].to_s, "(0,0;100,100;100,0)") r1.merged_semantics = true assert_equal(r1.rectangles.to_s, "") + assert_equal(r1.split_rectangles[0].to_s, "") assert_equal(r1.non_rectangles.to_s, "(0,0;0,100;10,100;10,200;50,200;50,270;150,270;150,70;100,70;100,0)") + assert_equal(r1.split_rectangles[1].to_s, "(0,0;0,100;10,100;10,200;50,200;50,270;150,270;150,70;100,70;100,0)") assert_equal(r1.rectilinear.to_s, "(0,0;0,100;10,100;10,200;50,200;50,270;150,270;150,70;100,70;100,0)") + assert_equal(r1.split_rectilinear[0].to_s, "(0,0;0,100;10,100;10,200;50,200;50,270;150,270;150,70;100,70;100,0)") assert_equal(r1.non_rectilinear.to_s, "") + assert_equal(r1.split_rectilinear[1].to_s, "") end @@ -1188,19 +1218,25 @@ class DBRegion_TestClass < TestBase r.insert(RBA::Box::new(0, 10000, 2000, 12000)) assert_equal(r.with_bbox_width(1000, false).to_s, "(0,0;0,5000;1000,5000;1000,0)") + assert_equal(r.split_with_bbox_width(1000)[0].to_s, "(0,0;0,5000;1000,5000;1000,0)") assert_equal(csort(r.with_bbox_width(1000, true).to_s), csort("(3000,0;3000,1000;7000,1000;7000,0);(0,10000;0,12000;2000,12000;2000,10000)")) + assert_equal(csort(r.split_with_bbox_width(1000)[1].to_s), csort("(3000,0;3000,1000;7000,1000;7000,0);(0,10000;0,12000;2000,12000;2000,10000)")) assert_equal(csort(r.with_bbox_width(1000, 2001, false).to_s), csort("(0,0;0,5000;1000,5000;1000,0);(0,10000;0,12000;2000,12000;2000,10000)")) assert_equal(r.with_bbox_width(1000, 2000, false).to_s, "(0,0;0,5000;1000,5000;1000,0)") assert_equal(r.with_bbox_width(1000, 2001, true).to_s, "(3000,0;3000,1000;7000,1000;7000,0)") assert_equal(r.with_bbox_height(5000, false).to_s, "(0,0;0,5000;1000,5000;1000,0)") + assert_equal(r.split_with_bbox_height(5000)[0].to_s, "(0,0;0,5000;1000,5000;1000,0)") assert_equal(csort(r.with_bbox_height(5000, true).to_s), csort("(3000,0;3000,1000;7000,1000;7000,0);(0,10000;0,12000;2000,12000;2000,10000)")) + assert_equal(csort(r.split_with_bbox_height(5000)[1].to_s), csort("(3000,0;3000,1000;7000,1000;7000,0);(0,10000;0,12000;2000,12000;2000,10000)")) assert_equal(csort(r.with_bbox_height(1000, 2001, false).to_s), csort("(3000,0;3000,1000;7000,1000;7000,0);(0,10000;0,12000;2000,12000;2000,10000)")) assert_equal(r.with_bbox_height(1000, 1001, false).to_s, "(3000,0;3000,1000;7000,1000;7000,0)") assert_equal(csort(r.with_bbox_height(1000, 1001, true).to_s), csort("(0,0;0,5000;1000,5000;1000,0);(0,10000;0,12000;2000,12000;2000,10000)")) assert_equal(r.with_bbox_aspect_ratio(1.0, false).to_s, "(0,10000;0,12000;2000,12000;2000,10000)") + assert_equal(r.split_with_bbox_aspect_ratio(1.0)[0].to_s, "(0,10000;0,12000;2000,12000;2000,10000)") assert_equal(csort(r.with_bbox_aspect_ratio(1.0, true).to_s), csort("(3000,0;3000,1000;7000,1000;7000,0);(0,0;0,5000;1000,5000;1000,0)")) + assert_equal(csort(r.split_with_bbox_aspect_ratio(1.0)[1].to_s), csort("(3000,0;3000,1000;7000,1000;7000,0);(0,0;0,5000;1000,5000;1000,0)")) assert_equal(r.with_bbox_aspect_ratio(0.9, 1.0, false).to_s, "(0,10000;0,12000;2000,12000;2000,10000)") assert_equal(r.with_bbox_aspect_ratio(1.0, 1.1, false).to_s, "(0,10000;0,12000;2000,12000;2000,10000)") assert_equal(r.with_bbox_aspect_ratio(0.9, 0.95, false).to_s, "") @@ -1208,7 +1244,9 @@ class DBRegion_TestClass < TestBase assert_equal(r.with_bbox_aspect_ratio(1.0, 1.1, false, false, true).to_s, "") assert_equal(r.with_relative_height(1.0, false).to_s, "(0,10000;0,12000;2000,12000;2000,10000)") + assert_equal(r.split_with_relative_height(1.0)[0].to_s, "(0,10000;0,12000;2000,12000;2000,10000)") assert_equal(csort(r.with_relative_height(1.0, true).to_s), csort("(3000,0;3000,1000;7000,1000;7000,0);(0,0;0,5000;1000,5000;1000,0)")) + assert_equal(csort(r.split_with_relative_height(1.0)[1].to_s), csort("(3000,0;3000,1000;7000,1000;7000,0);(0,0;0,5000;1000,5000;1000,0)")) assert_equal(r.with_relative_height(0.9, 1.0, false).to_s, "(0,10000;0,12000;2000,12000;2000,10000)") assert_equal(r.with_relative_height(1.0, 1.1, false).to_s, "(0,10000;0,12000;2000,12000;2000,10000)") assert_equal(r.with_relative_height(0.9, 0.95, false).to_s, "") @@ -1216,9 +1254,13 @@ class DBRegion_TestClass < TestBase assert_equal(r.with_relative_height(1.0, 1.1, false, false, true).to_s, "") assert_equal(csort(r.rectangles.to_s), csort("(3000,0;3000,1000;7000,1000;7000,0);(0,0;0,5000;1000,5000;1000,0);(0,10000;0,12000;2000,12000;2000,10000)")) + assert_equal(csort(r.split_rectangles[0].to_s), csort("(3000,0;3000,1000;7000,1000;7000,0);(0,0;0,5000;1000,5000;1000,0);(0,10000;0,12000;2000,12000;2000,10000)")) assert_equal(r.non_rectangles.to_s, "") + assert_equal(r.split_rectangles[1].to_s, "") assert_equal(r.squares.to_s, "(0,10000;0,12000;2000,12000;2000,10000)") + assert_equal(r.split_squares[0].to_s, "(0,10000;0,12000;2000,12000;2000,10000)") assert_equal(csort(r.non_squares.to_s), csort("(3000,0;3000,1000;7000,1000;7000,0);(0,0;0,5000;1000,5000;1000,0)")) + assert_equal(csort(r.split_squares[1].to_s), csort("(3000,0;3000,1000;7000,1000;7000,0);(0,0;0,5000;1000,5000;1000,0)")) r = RBA::Region::new r.insert(RBA::Box::new(0, 0, 1000, 2000)) @@ -1226,7 +1268,9 @@ class DBRegion_TestClass < TestBase r.insert(RBA::Box::new(0, 10000, 2000, 12000)) assert_equal(r.with_area_ratio(1.0, false).to_s, "(0,10000;0,12000;2000,12000;2000,10000)") + assert_equal(r.split_with_area_ratio(1.0)[0].to_s, "(0,10000;0,12000;2000,12000;2000,10000)") assert_equal(r.with_area_ratio(1.0, true).to_s, "(0,0;0,2000;1000,2000;1000,1000;2000,1000;2000,0)") + assert_equal(r.split_with_area_ratio(1.0)[1].to_s, "(0,0;0,2000;1000,2000;1000,1000;2000,1000;2000,0)") assert_equal(r.with_area_ratio(4.0 / 3.0, false).to_s, "(0,0;0,2000;1000,2000;1000,1000;2000,1000;2000,0)") assert_equal(r.with_area_ratio(1.3, 1.4, false).to_s, "(0,0;0,2000;1000,2000;1000,1000;2000,1000;2000,0)") assert_equal(r.with_area_ratio(1.3, 4.0 / 3.0, false, false, true).to_s, "(0,0;0,2000;1000,2000;1000,1000;2000,1000;2000,0)") @@ -1247,11 +1291,17 @@ class DBRegion_TestClass < TestBase r -= rr assert_equal(r.with_holes(0, false).to_s, "") + assert_equal(r.split_with_holes(0)[0].to_s, "") assert_equal(r.with_holes(0, true).to_s, "(0,0;0,200;100,200;100,0/10,10;20,10;20,20;10,20/30,30;40,30;40,40;30,40)") + assert_equal(r.split_with_holes(0)[1].to_s, "(0,0;0,200;100,200;100,0/10,10;20,10;20,20;10,20/30,30;40,30;40,40;30,40)") assert_equal(rr.with_holes(0, false).to_s, "(10,10;10,20;20,20;20,10);(30,30;30,40;40,40;40,30)") + assert_equal(rr.split_with_holes(0)[0].to_s, "(10,10;10,20;20,20;20,10);(30,30;30,40;40,40;40,30)") assert_equal(rr.with_holes(0, true).to_s, "") + assert_equal(rr.split_with_holes(0)[1].to_s, "") assert_equal(rr.with_holes(2, false).to_s, "") + assert_equal(rr.split_with_holes(2)[0].to_s, "") assert_equal(r.with_holes(1, 3, false).to_s, "(0,0;0,200;100,200;100,0/10,10;20,10;20,20;10,20/30,30;40,30;40,40;30,40)") + assert_equal(r.split_with_holes(1, 3)[0].to_s, "(0,0;0,200;100,200;100,0/10,10;20,10;20,20;10,20/30,30;40,30;40,40;30,40)") assert_equal(r.with_holes(2, 3, false).to_s, "(0,0;0,200;100,200;100,0/10,10;20,10;20,20;10,20/30,30;40,30;40,40;30,40)") assert_equal(r.with_holes(1, 2, false).to_s, "") @@ -1349,6 +1399,8 @@ class DBRegion_TestClass < TestBase region.insert(RBA::Box::new(200, 0, 300, 100)) assert_equal(region.filtered(TriangleFilter::new).to_s, "(0,0;100,100;100,0)") + assert_equal(region.split_filter(TriangleFilter::new)[0].to_s, "(0,0;100,100;100,0)") + assert_equal(region.split_filter(TriangleFilter::new)[1].to_s, "(200,0;200,100;300,100;300,0)") assert_equal(region.to_s, "(0,0;100,100;100,0);(200,0;200,100;300,100;300,0)") region.filter(TriangleFilter::new) assert_equal(region.to_s, "(0,0;100,100;100,0)") diff --git a/testdata/ruby/dbTextsTest.rb b/testdata/ruby/dbTextsTest.rb index 813fe29ad..492fa96f3 100644 --- a/testdata/ruby/dbTextsTest.rb +++ b/testdata/ruby/dbTextsTest.rb @@ -179,9 +179,13 @@ class DBTexts_TestClass < TestBase r1 = RBA::Texts::new([ text1, text2 ]) assert_equal(csort(r1.to_s), csort("('abc',r0 100,-200);('uvm',r0 110,210)")) assert_equal(r1.with_text("abc", false).to_s, "('abc',r0 100,-200)") + assert_equal(r1.split_with_text("abc")[0].to_s, "('abc',r0 100,-200)") assert_equal(r1.with_text("abc", true).to_s, "('uvm',r0 110,210)") + assert_equal(r1.split_with_text("abc")[1].to_s, "('uvm',r0 110,210)") assert_equal(r1.with_match("*b*", false).to_s, "('abc',r0 100,-200)") + assert_equal(r1.split_with_match("*b*")[0].to_s, "('abc',r0 100,-200)") assert_equal(r1.with_match("*b*", true).to_s, "('uvm',r0 110,210)") + assert_equal(r1.split_with_match("*b*")[1].to_s, "('uvm',r0 110,210)") r1 = RBA::Texts::new(text1) assert_equal(r1.to_s, "('abc',r0 100,-200)") @@ -394,6 +398,8 @@ class DBTexts_TestClass < TestBase texts.insert(RBA::Text::new("00", [ RBA::Trans::R90, 0, 20 ])) assert_equal(texts.filtered(f).to_s, "('tla',r0 0,0)") + assert_equal(texts.split_filter(f)[0].to_s, "('tla',r0 0,0)") + assert_equal(texts.split_filter(f)[1].to_s, "('long',m45 10,0);('00',r90 0,20)") assert_equal(texts.to_s, "('long',m45 10,0);('tla',r0 0,0);('00',r90 0,20)") texts.filter(f) assert_equal(texts.to_s, "('tla',r0 0,0)") From 1372a7d04ef1d6746959df2ad58f151f3c247923 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 9 Feb 2025 22:44:34 +0100 Subject: [PATCH 034/392] [consider merging] Bugfix: fixing a crash when editing PCell parameters (QR code PCell in this case) while the macro editor is open --- src/edt/edt/edtPCellParametersPage.cc | 7 +++++++ src/edt/edt/edtPCellParametersPage.h | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/src/edt/edt/edtPCellParametersPage.cc b/src/edt/edt/edtPCellParametersPage.cc index d68c0b319..afbf327f6 100644 --- a/src/edt/edt/edtPCellParametersPage.cc +++ b/src/edt/edt/edtPCellParametersPage.cc @@ -706,6 +706,13 @@ PCellParametersPage::parameter_changed () dm_parameter_changed (); } +void +PCellParametersPage::deleteLater () +{ + dm_parameter_changed.cancel (); + QFrame::deleteLater (); +} + void PCellParametersPage::do_parameter_changed () { diff --git a/src/edt/edt/edtPCellParametersPage.h b/src/edt/edt/edtPCellParametersPage.h index a6c862452..c976dc254 100644 --- a/src/edt/edt/edtPCellParametersPage.h +++ b/src/edt/edt/edtPCellParametersPage.h @@ -139,6 +139,11 @@ public: */ void set_parameters (const std::vector &values); + /** + * @brief Reimplementation of deleteLater + */ + void deleteLater (); + signals: void edited (); From b434eb3239458cf296c668a410376965ef2a5da1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Feb 2025 21:16:49 +0100 Subject: [PATCH 035/392] [consider merging] Fixing two problems with layer mapping 1. Mapping "[*/*] 100/0:0/0" (for example) created 0/0 two times when the input contains 100/0 and 0/0. Now it is a single layer only 2. The mapping table generated from strings now uses layer indexes from a range that should not collide with existing layer indexes. --- src/db/db/dbStreamLayers.cc | 19 ++++++++++-- src/db/unit_tests/dbStreamLayerTests.cc | 30 +++++++++++++++++++ .../gds2/unit_tests/dbGDS2ReaderTests.cc | 3 +- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/db/db/dbStreamLayers.cc b/src/db/db/dbStreamLayers.cc index c8b1b0f92..c4038549b 100644 --- a/src/db/db/dbStreamLayers.cc +++ b/src/db/db/dbStreamLayers.cc @@ -236,7 +236,19 @@ LayerMap::substitute_placeholder (const db::LayerProperties &p, const std::set::max (); + for (std::map::const_iterator t = m_target_layers.begin (); t != m_target_layers.end (); ++t) { + if (t->second.log_equal (lp_new) && layout.is_valid_layer (t->first)) { + l_new = t->first; + break; + } + } + + if (l_new == std::numeric_limits::max ()) { + l_new = layout.insert_layer (lp_new); + } + map (p, l_new, lp_new); res.insert (l_new); @@ -929,7 +941,10 @@ db::LayerMap LayerMap::from_string_file_format (const std::string &s) { db::LayerMap lm; - unsigned int l = 0; + // NOTE: this should be outside the normal layer index range of a Layout and below + // the space reserved for placeholders. With numbers like this we don't get messed + // up with existing layers. + unsigned int l = std::numeric_limits::max () / 2; int lnr = 0; diff --git a/src/db/unit_tests/dbStreamLayerTests.cc b/src/db/unit_tests/dbStreamLayerTests.cc index 3e3287aee..fc4da9fce 100644 --- a/src/db/unit_tests/dbStreamLayerTests.cc +++ b/src/db/unit_tests/dbStreamLayerTests.cc @@ -577,3 +577,33 @@ TEST(8) EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); } +// mapping of relative target to real target +TEST(9) +{ + db::Layout ly; + db::LayerMap lm; + + EXPECT_EQ (layers_to_string (ly), ""); + unsigned int l0 = ly.insert_layer (db::LayerProperties (0, 0)); + unsigned int l101 = ly.insert_layer (db::LayerProperties (101, 0)); + + unsigned int n = std::numeric_limits::max () / 2; + lm.map_expr ("*/* : */*", n++); + lm.map_expr ("100/0 : 0/0", n++); + + lm.prepare (ly); + + std::pair p; + p = lm.first_logical (db::LayerProperties (100, 0), ly); + EXPECT_EQ (p.first, true); + EXPECT_EQ (p.second, l0); + + p = lm.first_logical (db::LayerProperties (101, 0), ly); + EXPECT_EQ (p.first, true); + EXPECT_EQ (p.second, l101); + + // maps to the target 0/0 + p = lm.first_logical (db::LayerProperties (0, 0), ly); + EXPECT_EQ (p.first, true); + EXPECT_EQ (p.second, l0); +} diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2ReaderTests.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2ReaderTests.cc index 5552c8d3b..d65f9f3a4 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2ReaderTests.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2ReaderTests.cc @@ -417,9 +417,8 @@ TEST(3_AdvancedMapping) } EXPECT_EQ (lm_read.to_string_file_format (), - "1/10 : 1/0\n" + "1/0,10 : 1/0\n" "2/0-1 : 2/0\n" - "1/0 : 1/0\n" "1/1 : 1/1\n" "1/20 : 1/1020\n" "1/21 : 1/1021\n" From 3185a7aeb2d8bf3980d029e16b12b19d489d171f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Feb 2025 22:06:56 +0100 Subject: [PATCH 036/392] [consider merging] fixed a potential crash on 'save all' --- src/lay/lay/layMainWindow.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index cda9b8057..7b52b97b9 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -2365,7 +2365,7 @@ MainWindow::cm_save_all () } view (view_index)->save_as (cv_index, fn, om, options, true, m_keep_backups); - add_mru (fn, current_view ()->cellview (cv_index)->tech_name ()); + add_mru (fn, cv->tech_name ()); } From 01cc54ba49dbf09923e2b96009b01fa550204111 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Feb 2025 23:22:06 +0100 Subject: [PATCH 037/392] [consider merging] avoid a crash during certain queries This query used to crash: "instances of cell .*.* where inst.trans.rot == 2" on a layout with 1 hierarchy level and some cells with rot 180 degree --- src/db/db/dbLayoutQuery.cc | 14 +++++++++----- src/db/unit_tests/dbLayoutQueryTests.cc | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/db/db/dbLayoutQuery.cc b/src/db/db/dbLayoutQuery.cc index d2753b617..c5989b5bb 100644 --- a/src/db/db/dbLayoutQuery.cc +++ b/src/db/db/dbLayoutQuery.cc @@ -1006,15 +1006,19 @@ public: db::Instance inst; if (m_reading) { - inst = mp_parent->sorted_inst_ptr (std::distance (mp_parent->begin_sorted_insts (), m_inst)); + if (mp_parent) { + inst = mp_parent->sorted_inst_ptr (std::distance (mp_parent->begin_sorted_insts (), m_inst)); + } } else { inst = m_i; } - if (m_instance_mode == ArrayInstances) { - v.push (tl::Variant (db::InstElement (inst))); - } else { - v.push (tl::Variant (db::InstElement (inst, m_array_iter))); + if (! inst.is_null ()) { + if (m_instance_mode == ArrayInstances) { + v.push (tl::Variant (db::InstElement (inst))); + } else { + v.push (tl::Variant (db::InstElement (inst, m_array_iter))); + } } return true; diff --git a/src/db/unit_tests/dbLayoutQueryTests.cc b/src/db/unit_tests/dbLayoutQueryTests.cc index 06e9cf811..08d2587c5 100644 --- a/src/db/unit_tests/dbLayoutQueryTests.cc +++ b/src/db/unit_tests/dbLayoutQueryTests.cc @@ -1578,3 +1578,20 @@ TEST(64) EXPECT_EQ (s, "0.01,-0.02,-0.01,0.02,-0.01,0.02,-0.01,0.02,0.01,-0.02,-0.01,0.02"); } } + +TEST(65) +{ + db::Layout g; + init_layout (g); + + { + db::LayoutQuery q ("instances of cell .*.* where inst.trans.rot == 0"); + db::LayoutQueryIterator iq (q, &g); + std::string s = q2s_var (iq, "data"); + EXPECT_EQ (s, "nil,nil"); + s = q2s_var (iq, "cell_name"); + EXPECT_EQ (s, "c1,c5x"); + s = q2s_var (iq, "inst_elements"); + EXPECT_EQ (s, "(cell_index=0 r0 *1 10,-20),(cell_index=4 r0 *1 10,-20)"); + } +} From 32fe65adc0bd6813d34960aba4b63ba67991db7a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 1 Mar 2025 23:18:29 +0100 Subject: [PATCH 038/392] [consider merging] Fixed a bug when returning a Region into a layout with cells unselected. --- src/db/db/dbCellMapping.cc | 16 ++- src/db/db/dbCellMapping.h | 5 + src/db/db/dbDeepShapeStore.cc | 122 +++++++++++++-------- src/db/unit_tests/dbDeepShapeStoreTests.cc | 35 ++++++ testdata/algo/dss_bug.gds | Bin 0 -> 920 bytes testdata/algo/dss_bug_au.gds | Bin 0 -> 1208 bytes 6 files changed, 130 insertions(+), 48 deletions(-) create mode 100644 testdata/algo/dss_bug.gds create mode 100644 testdata/algo/dss_bug_au.gds diff --git a/src/db/db/dbCellMapping.cc b/src/db/db/dbCellMapping.cc index 90d9d6f97..3e0d30e37 100644 --- a/src/db/db/dbCellMapping.cc +++ b/src/db/db/dbCellMapping.cc @@ -284,8 +284,17 @@ std::vector CellMapping::source_cells () const return s; } +std::vector CellMapping::target_cells () const +{ + std::vector s; + s.reserve (m_b2a_mapping.size ()); + for (iterator m = begin (); m != end (); ++m) { + s.push_back (m->second); + } + return s; +} -void +void CellMapping::create_single_mapping (const db::Layout & /*layout_a*/, db::cell_index_type cell_index_a, const db::Layout & /*layout_b*/, db::cell_index_type cell_index_b) { clear (); @@ -374,7 +383,7 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout & db::PropertyMapper pm (&layout_a, &layout_b); // Note: this avoids frequent cell index table rebuilds if source and target layout are identical - layout_a.start_changes (); + db::LayoutLocker locker (&layout_a); // Create instances for the new cells in layout A according to their instantiation in layout B double mag = layout_b.dbu () / layout_a.dbu (); @@ -405,9 +414,6 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout & } - // Note: must be there because of start_changes - layout_a.end_changes (); - } } diff --git a/src/db/db/dbCellMapping.h b/src/db/db/dbCellMapping.h index 42e28fab6..4686e5f16 100644 --- a/src/db/db/dbCellMapping.h +++ b/src/db/db/dbCellMapping.h @@ -206,6 +206,11 @@ public: */ std::vector source_cells () const; + /** + * @brief Gets the target cells + */ + std::vector target_cells () const; + /** * @brief Access to the mapping table */ diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index a01df20c5..23a28ef30 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -1172,62 +1172,69 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::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->second.is_valid (*into_layout, *source_layout)) { + if (cm != m_delivery_mapping_cache.end () && cm->second.is_valid (*into_layout, *source_layout)) { + return cm->second; + } - cm = m_delivery_mapping_cache.insert (std::make_pair (key, CellMappingWithGenerationIds ())).first; - cm->second.clear (); + cm = m_delivery_mapping_cache.insert (std::make_pair (key, CellMappingWithGenerationIds ())).first; + cm->second.clear (); - // collects the cell mappings we skip because they are variants (variant building or box variants) - std::map cm_skipped_variants; + // Not found in cache - compute a fresh mapping - if (into_layout == original_builder.source ().layout () && &into_layout->cell (into_cell) == original_builder.source ().top_cell () && original_builder.source ().global_trans ().is_unity ()) { + // collects the cell mappings we skip because they are variants (variant building or box variants) + std::map cm_skipped_variants; - // 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. + if (into_layout == original_builder.source ().layout () && &into_layout->cell (into_cell) == original_builder.source ().top_cell () && original_builder.source ().global_trans ().is_unity ()) { - for (HierarchyBuilder::cell_map_type::const_iterator m = original_builder.begin_cell_map (); m != original_builder.end_cell_map (); ) { + // 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. - HierarchyBuilder::cell_map_type::const_iterator mm = m; + for (HierarchyBuilder::cell_map_type::const_iterator m = original_builder.begin_cell_map (); m != original_builder.end_cell_map (); ) { + + 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.original_cell == m->first.original_cell) { + // we have cell (box) variants and cannot simply map ++mm; - bool skip = original_builder.is_variant (m->second); // skip variant cells - while (mm != original_builder.end_cell_map () && mm->first.original_cell == m->first.original_cell) { - // we have cell (box) variants and cannot simply map - ++mm; - skip = true; - } - - if (! skip) { - cm->second.map (m->second, m->first.original_cell); - } else { - for (HierarchyBuilder::cell_map_type::const_iterator n = m; n != mm; ++n) { - tl_assert (cm_skipped_variants.find (n->second) == cm_skipped_variants.end ()); - cm_skipped_variants [n->second] = n->first; - } - } - - m = mm; - + skip = true; } - } else if (into_layout->cells () == 1) { + if (! skip) { + cm->second.map (m->second, m->first.original_cell); + } else { + for (HierarchyBuilder::cell_map_type::const_iterator n = m; n != mm; ++n) { + tl_assert (cm_skipped_variants.find (n->second) == cm_skipped_variants.end ()); + cm_skipped_variants [n->second] = n->first; + } + } - // 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); + m = mm; } - // Add new cells for the variants and (possible) devices which are cells added during the device - // extraction process - std::vector > new_pairs = cm->second.create_missing_mapping2 (*into_layout, *source_layout, source_top, excluded_cells, included_cells); + } 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 + std::vector > new_pairs = cm->second.create_missing_mapping2 (*into_layout, *source_layout, source_top, excluded_cells, included_cells); + + if (! new_pairs.empty ()) { // the variant's originals we are going to delete std::set cells_to_delete; + std::vector > new_variants; // We now need to fix the cell map from the hierarchy builder, so we can import back from the modified layout. // This is in particular important if we created new cells for known variants. @@ -1240,6 +1247,7 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout // create the variant clone in the original layout too and delete this cell VariantsCollectorBase::copy_shapes (*into_layout, np->second, icm->second.original_cell); + new_variants.push_back (std::make_pair (np->second, icm->second.original_cell)); cells_to_delete.insert (icm->second.original_cell); // forget the original cell (now separated into variants) and map the variants back into the @@ -1259,15 +1267,43 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout } - // delete the variant's original cell + if (! new_variants.empty ()) { + + // copy cell instances for the new variants + + // collect the cells that are handled during cell mapping - + // we do not need to take care of them when creating variants, + // but there may be others inside "into_layout" which are + // not present in the DSS and for which we need to copy the + // instances. + std::vector mapped = cm->second.target_cells (); + std::sort (mapped.begin (), mapped.end ()); + + // Copy the variant instances - but only those for cells which are not going to be + // deleted and those not handled by the cell mapping object. + for (auto vv = new_variants.begin (); vv != new_variants.end (); ++vv) { + const db::Cell &from = into_layout->cell (vv->second); + db::Cell &to = into_layout->cell (vv->first); + for (db::Cell::const_iterator i = from.begin (); ! i.at_end (); ++i) { + if (cells_to_delete.find (i->cell_index ()) == cells_to_delete.end ()) { + auto m = std::lower_bound (mapped.begin (), mapped.end (), i->cell_index ()); + if (m == mapped.end () || *m != i->cell_index ()) { + to.insert (*i); + } + } + } + } + + } + if (! cells_to_delete.empty ()) { + // delete the variant original cells into_layout->delete_cells (cells_to_delete); } - cm->second.set_generation_ids (*into_layout, *source_layout); - } + cm->second.set_generation_ids (*into_layout, *source_layout); return cm->second; } diff --git a/src/db/unit_tests/dbDeepShapeStoreTests.cc b/src/db/unit_tests/dbDeepShapeStoreTests.cc index 0acce3658..c0a56db08 100644 --- a/src/db/unit_tests/dbDeepShapeStoreTests.cc +++ b/src/db/unit_tests/dbDeepShapeStoreTests.cc @@ -24,6 +24,8 @@ #include "dbDeepShapeStore.h" #include "dbRegion.h" #include "dbDeepRegion.h" +#include "dbReader.h" +#include "dbTestSupport.h" #include "tlUnitTest.h" #include "tlStream.h" @@ -266,3 +268,36 @@ TEST(5_State) EXPECT_EQ (store.breakout_cells (0)->find (5) != store.breakout_cells (0)->end (), true); EXPECT_EQ (store.breakout_cells (0)->find (3) != store.breakout_cells (0)->end (), true); } + +TEST(6_RestoreWithCellSelection) +{ + db::Layout ly; + + { + std::string fn (tl::testdata ()); + fn += "/algo/dss_bug.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + + db::Cell &top_cell = ly.cell (*ly.begin_top_down ()); + + db::RecursiveShapeIterator in_it (ly, top_cell, l1); + + std::set us; + us.insert (ly.cell_by_name ("X").second); + in_it.unselect_cells (us); + + db::DeepShapeStore dss; + + db::Region in_region (in_it, dss); + ly.clear_layer (l1); + + in_region.insert_into (&ly, top_cell.cell_index (), l1); + + db::compare_layouts (_this, ly, tl::testdata () + "/algo/dss_bug_au.gds"); +} + diff --git a/testdata/algo/dss_bug.gds b/testdata/algo/dss_bug.gds new file mode 100644 index 0000000000000000000000000000000000000000..4b55407481498e6a9f0efe859237c496a4e41444 GIT binary patch literal 920 zcmb7CJud`77=Ctkc5dU2;I0tagbK+?i2Fbz=V*kWxZj##L@oifbCuhNXHJ^C^S?F;wrLJo@^-SZa_XTl4&t$)Cw;L^$6 z!QH_@H^Egnzq`ws48{fo53aAdOTKsS-S;5C*Z`r!3U6@Xpn_G@(*NNzaoFns71!I{ zyV^Q<7`^Xw$LqJ(XMKcT_IE;v{Y`Km5Hk!6VwM4@&AIdoDL)q#n&G}$wCfuAJirV1 zl_emSXra)em3$ zQU4b=n&3s_Mg+0s41rSUb?#zKewl$&M}_2*vrU_7+_W{=XRSMSw4of8Piyo>acpjc zst4q?x!*r9Oz9zYv#qIGR63Lv+z(YBN%>ec{bZiMPweXx*E1Z+RVD5uC&`S;gPm~~ zPb}K?+?gT014>rxdsF)^}bJe^2A);~T;Dzr=%j;cuoQ=3?HO3If2&2&mq+JNP@ Wl+P1`A1}bghLjfVI-xxq=lBAUv7J-^ literal 0 HcmV?d00001 From 414a06c70febf61e1f760b5fbf317ba6d1cc566e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 2 Mar 2025 20:54:00 +0100 Subject: [PATCH 039/392] Bugfix: tab title was not updated when current cell is renamed --- .../laybasic/gsiDeclLayLayoutViewBase.cc | 2 +- src/laybasic/laybasic/layLayoutViewBase.cc | 99 ++++++++++--------- src/laybasic/laybasic/layLayoutViewBase.h | 7 +- 3 files changed, 57 insertions(+), 51 deletions(-) diff --git a/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc b/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc index da561762c..15925208b 100644 --- a/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc +++ b/src/laybasic/laybasic/gsiDeclLayLayoutViewBase.cc @@ -607,7 +607,7 @@ LAYBASIC_PUBLIC Class decl_LayoutViewBase ("lay", "LayoutVi "\n" "See \\set_title and \\title for a description about how titles are handled." ) + - gsi::method ("title", static_cast (&lay::LayoutViewBase::title), + gsi::method ("title", static_cast (&lay::LayoutViewBase::title), "@brief Returns the view's title string\n" "\n" "@return The title string\n" diff --git a/src/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc index 38fa9ab69..cd9534e00 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.cc +++ b/src/laybasic/laybasic/layLayoutViewBase.cc @@ -539,7 +539,7 @@ void LayoutViewBase::update_event_handlers () cellview (i)->layout ().dbu_changed_event.add (this, &LayoutViewBase::signal_bboxes_changed); cellview (i)->layout ().prop_ids_changed_event.add (this, &LayoutViewBase::signal_prop_ids_changed); cellview (i)->layout ().layer_properties_changed_event.add (this, &LayoutViewBase::signal_layer_properties_changed); - cellview (i)->layout ().cell_name_changed_event.add (this, &LayoutViewBase::signal_cell_name_changed); + cellview (i)->layout ().cell_name_changed_event.add (this, &LayoutViewBase::signal_cell_name_changed, i); cellview (i)->apply_technology_with_sender_event.add (this, &LayoutViewBase::signal_apply_technology); } @@ -685,13 +685,30 @@ LayoutViewBase::is_dirty () const return m_dirty; } -std::string +const std::string & LayoutViewBase::title () const +{ + return m_current_title; +} + +void +LayoutViewBase::update_title () { if (! m_title.empty ()) { - return m_title; + + if (m_title != m_current_title) { + m_current_title = m_title; + emit_title_changed (); + } + } else if (cellviews () == 0) { - return tl::to_string (tr ("")); + + static std::string empty_title = tl::to_string (tr ("")); + if (m_current_title != empty_title) { + m_current_title = empty_title; + emit_title_changed (); + } + } else { int cv_index = active_cellview_index (); @@ -714,27 +731,25 @@ LayoutViewBase::title () const t += " ..."; } - return t; - + if (t != m_current_title) { + m_current_title = t; + emit_title_changed (); + } } } void LayoutViewBase::set_title (const std::string &t) { - if (m_title != t) { - m_title = t; - emit_title_changed (); - } + m_title = t; + update_title (); } void LayoutViewBase::reset_title () { - if (! m_title.empty ()) { - m_title = ""; - emit_title_changed (); - } + m_title.clear (); + update_title (); } bool @@ -1977,9 +1992,9 @@ LayoutViewBase::replace_layer_node (unsigned int index, const LayerPropertiesCon manager ()->clear (); } - if (index == current_layer_list ()) { - begin_layer_updates (); - } + if (index == current_layer_list ()) { + begin_layer_updates (); + } LayerPropertiesIterator non_const_iter (get_properties (index), iter.uint ()); *non_const_iter = node; @@ -2342,9 +2357,13 @@ LayoutViewBase::signal_bboxes_changed () } void -LayoutViewBase::signal_cell_name_changed () +LayoutViewBase::signal_cell_name_changed (unsigned int cv_index) { - cell_visibility_changed_event (); // HINT: that is not what actually is intended, but it serves the function ... + cellview_changed_event (int (cv_index)); + + // Because the title reflects the active cell, emit a title changed event + update_title (); + redraw_later (); // needs redraw } @@ -2489,10 +2508,7 @@ LayoutViewBase::erase_cellview (unsigned int index) finish_cellviews_changed (); update_content (); - - if (m_title.empty ()) { - emit_title_changed (); - } + update_title (); } void @@ -2522,9 +2538,7 @@ LayoutViewBase::clear_cellviews () finish_cellviews_changed (); - if (m_title.empty ()) { - emit_title_changed (); - } + update_title (); } const CellView & @@ -2592,9 +2606,7 @@ LayoutViewBase::set_layout (const lay::CellView &cv, unsigned int cvindex) // the layouts are released as far as possible. This is important for reload () for example. update_content_for_cv (cvindex); - if (m_title.empty ()) { - emit_title_changed (); - } + update_title (); } void @@ -4866,9 +4878,7 @@ LayoutViewBase::cellview_changed (unsigned int index) cellview_changed_event (index); - if (m_title.empty ()) { - emit_title_changed (); - } + update_title (); } const lay::CellView & @@ -4961,10 +4971,8 @@ LayoutViewBase::enable_active_cellview_changed_event (bool enable, bool silent) active_cellview_changed_with_index_event (*i); } - // Because the title reflects the active one, emit a title changed event - if (title_string ().empty ()) { - emit_title_changed (); - } + // Because the title reflects the active cell, emit a title changed event + update_title (); } @@ -4987,10 +4995,8 @@ LayoutViewBase::active_cellview_changed (int index) active_cellview_changed_event (); active_cellview_changed_with_index_event (index); - // Because the title reflects the active one, emit a title changed event - if (title_string ().empty ()) { - emit_title_changed (); - } + // Because the title reflects the active cell, emit a title changed event + update_title (); } else { m_active_cellview_changed_events.insert (index); @@ -5936,14 +5942,11 @@ LayoutViewBase::update_content_for_cv (int /*cellview_index*/) void LayoutViewBase::rename_cellview (const std::string &name, int cellview_index) { - if (cellview_index >= 0 && cellview_index < int (m_cellviews.size ())) { - if ((*cellview_iter (cellview_index))->name () != name) { - (*cellview_iter (cellview_index))->rename (name); - update_content_for_cv (cellview_index); - if (m_title.empty ()) { - emit_title_changed (); - } - } + if (cellview_index >= 0 && cellview_index < int (m_cellviews.size ()) && + (*cellview_iter (cellview_index))->name () != name) { + (*cellview_iter (cellview_index))->rename (name); + update_content_for_cv (cellview_index); + update_title (); } } diff --git a/src/laybasic/laybasic/layLayoutViewBase.h b/src/laybasic/laybasic/layLayoutViewBase.h index 51531e9be..ee40f9f85 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.h +++ b/src/laybasic/laybasic/layLayoutViewBase.h @@ -2614,7 +2614,7 @@ public: /** * @brief Gets the window title of the view */ - std::string title () const; + const std::string &title () const; /** * @brief Sets the window title to an explicit string @@ -2837,7 +2837,7 @@ private: void signal_bboxes_changed (); void signal_prop_ids_changed (); void signal_layer_properties_changed (); - void signal_cell_name_changed (); + void signal_cell_name_changed (unsigned int cv_index); void signal_annotations_changed (); void signal_plugin_enabled_changed (); void signal_apply_technology (lay::LayoutHandle *layout_handle); @@ -2853,6 +2853,7 @@ private: lay::AnnotationShapes m_annotation_shapes; std::vector > m_hidden_cells; std::string m_title; + std::string m_current_title; tl::vector m_rdbs; tl::vector m_l2ndbs; std::string m_def_lyp_file; @@ -3005,6 +3006,8 @@ private: void init_layer_properties (LayerProperties &props, const LayerPropertiesList &lp_list) const; void merge_dither_pattern (lay::LayerPropertiesList &props); + void update_title (); + protected: /** * @brief Constructor for calling from a LayoutView From 2e150980d53819cfa6b2dad8fdd0f1a635cc32bf Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 2 Mar 2025 22:12:18 +0100 Subject: [PATCH 040/392] Bugfix: 'Save all' was not updating the dirty state in the panel tabs --- src/laybasic/laybasic/layLayoutViewBase.cc | 26 +++++++++++++--------- src/layview/layview/layLayoutView_qt.cc | 2 -- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc index cd9534e00..79ec6acaa 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.cc +++ b/src/laybasic/laybasic/layLayoutViewBase.cc @@ -3788,21 +3788,25 @@ LayoutViewBase::timer () emit_dirty_changed (); } - if (m_prop_changed) { - do_prop_changed (); - m_prop_changed = false; - } + if (is_activated ()) { - tl::Clock current_time = tl::Clock::current (); - if ((current_time - m_last_checked).seconds () > animation_interval) { - m_last_checked = current_time; - if (m_animated) { - set_view_ops (); - do_set_phase (int (m_phase)); + if (m_prop_changed) { + do_prop_changed (); + m_prop_changed = false; + } + + tl::Clock current_time = tl::Clock::current (); + if ((current_time - m_last_checked).seconds () > animation_interval) { + m_last_checked = current_time; if (m_animated) { - ++m_phase; + set_view_ops (); + do_set_phase (int (m_phase)); + if (m_animated) { + ++m_phase; + } } } + } } diff --git a/src/layview/layview/layLayoutView_qt.cc b/src/layview/layview/layLayoutView_qt.cc index bc4df652f..d361b4601 100644 --- a/src/layview/layview/layLayoutView_qt.cc +++ b/src/layview/layview/layLayoutView_qt.cc @@ -1459,7 +1459,6 @@ LayoutView::activate () (*p)->browser_interface ()->show (); } } - mp_timer->start (timer_interval); m_activated = true; update_content (); } @@ -1479,7 +1478,6 @@ LayoutView::deactivate () } free_resources (); - mp_timer->stop (); m_activated = false; } From ab10c920b1a841695c453a264709f566cfbb9392 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 2 Mar 2025 22:17:43 +0100 Subject: [PATCH 041/392] Preparations for 0.29.12. --- COPYRIGHT | 5 ++--- Changelog | 19 +++++++++++++++++++ Changelog.Debian | 7 +++++++ version.sh | 4 ++-- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/COPYRIGHT b/COPYRIGHT index 8af0ce071..d01b4e3df 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,12 +1,11 @@ -klayout is packaged by Peter C.S. Scholtens -and Matthias Köfferlein +klayout is packaged by Matthias Köfferlein and was obtained from https://www.klayout.org/downloads/source/klayout-%VERSION%.tar.gz Authors: Matthias Köfferlein Copyright: - Copyright (C) 2006-2022 by Matthias Köfferlein. + Copyright (C) 2006-2025 by Matthias Köfferlein. 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 diff --git a/Changelog b/Changelog index 6bfd1f1b2..41d3e9a7c 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,22 @@ +0.29.12 (2025-03-02): +* Bug: %GITHUB%/issues/1976 Crash on cross mode, lw > 1 and oversampling +* Bug: %GITHUB%/issues/1987 Build failure against Qt6.8 +* Enhancement: %GITHUB%/issues/1989 API enhancements: several new split_... methods + on Edges, Region, EdgePairs and Texts, delivering a pair of containers with + selected and unselected objects. +* Bugfix: 'Save All' was not updating the dirty flag in the inactive tabs +* Bugfix: Fixing a crash when editing PCell parameters while the macro editor is open +* Bugfix: Fixed a potential Crash on "save all" +* Bugfix: Fixed a bug when returning a Region into a layout with cells unselected +* Bugfix: Tab title were not updated on cell rename +* Bugfix: Fixed a crash on certain layout queries + - For example "instances of cell .*.* where inst.trans.rot == 2" was crashing +* Bugfix: Fixing two problems with layer mapping + - Mapping "[*/*] 100/0:0/0" (for example) created 0/0 two times when the input contains 100/0 and 0/0. Now + it is a single layer only + - The mapping table generated from strings now uses layer indexes from a range that should not collide + with existing layer indexes. + 0.29.11 (2025-01-17): * Bug: %GITHUB%/issues/1948 Crash by instantiate a Cell in a Library-Cell * Bug: %GITHUB%/issues/1953 Callback_impl & coerce_parameters_impl parameter display issue diff --git a/Changelog.Debian b/Changelog.Debian index dd2ebb144..85f58621a 100644 --- a/Changelog.Debian +++ b/Changelog.Debian @@ -1,3 +1,10 @@ +klayout (0.29.12-1) unstable; urgency=low + + * New features and bugfixes + - See changelog + + -- Matthias Köfferlein Sun, 02 Mar 2025 21:16:41 +0100 + klayout (0.29.11-1) unstable; urgency=low * New features and bugfixes diff --git a/version.sh b/version.sh index 48d9e093d..bfa5bd2dd 100644 --- a/version.sh +++ b/version.sh @@ -2,10 +2,10 @@ # This script is sourced to define the main version parameters # The main version -KLAYOUT_VERSION="0.29.11" +KLAYOUT_VERSION="0.29.12" # The version used for PyPI (don't use variables here!) -KLAYOUT_PYPI_VERSION="0.29.11" +KLAYOUT_PYPI_VERSION="0.29.12" # The build date KLAYOUT_VERSION_DATE=$(date "+%Y-%m-%d") From fb9e0c4602276e2708ceae36a1f8fe6e99ee4626 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 2 Mar 2025 22:24:04 +0100 Subject: [PATCH 042/392] Fixed a compile issue --- src/db/db/dbEdgePairs.h | 2 +- src/db/db/dbEdges.h | 2 +- src/db/db/dbRegion.h | 2 +- src/db/db/dbTexts.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index 36b8960f8..691140cb6 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -355,7 +355,7 @@ public: std::pair split_filter (const EdgePairFilterBase &filter) const { std::pair p = mp_delegate->filtered_pair (filter); - return std::pair (EdgePairs (p.first), EdgePairs (p.second)); + return std::make_pair (EdgePairs (p.first), EdgePairs (p.second)); } /** diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index bb57ada29..5469cb649 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -477,7 +477,7 @@ public: std::pair split_filter (const EdgeFilterBase &filter) const { std::pair p = mp_delegate->filtered_pair (filter); - return std::pair (Edges (p.first), Edges (p.second)); + return std::make_pair (Edges (p.first), Edges (p.second)); } /** diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 893931949..1548c65ab 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -581,7 +581,7 @@ public: std::pair split_filter (const PolygonFilterBase &filter) const { std::pair p = mp_delegate->filtered_pair (filter); - return std::pair (Region (p.first), Region (p.second)); + return std::make_pair (Region (p.first), Region (p.second)); } /** diff --git a/src/db/db/dbTexts.h b/src/db/db/dbTexts.h index 03594f1fd..283538d24 100644 --- a/src/db/db/dbTexts.h +++ b/src/db/db/dbTexts.h @@ -342,7 +342,7 @@ public: std::pair split_filter (const TextFilterBase &filter) const { std::pair p = mp_delegate->filtered_pair (filter); - return std::pair (Texts (p.first), Texts (p.second)); + return std::make_pair (Texts (p.first), Texts (p.second)); } /** From 20111ce08d83b0498750991e6b866adf0de00704 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 3 Mar 2025 01:05:04 +0100 Subject: [PATCH 043/392] Fixed issue #1993 ('with_area' modifies result of 'join') Root cause was a held layout lock inside the TilingProcessor. As that object was not always released inside the Ruby DRC script, the lock was held sometimes longer than needed and messed with the hierarchy system. --- src/db/db/dbOriginalLayerEdgePairs.cc | 2 +- src/db/db/dbOriginalLayerEdges.cc | 2 +- src/db/db/dbOriginalLayerRegion.cc | 6 +----- src/db/db/dbOriginalLayerTexts.cc | 2 +- src/db/db/dbRecursiveShapeIterator.cc | 7 +++++++ src/db/db/dbRecursiveShapeIterator.h | 10 ++++++++++ src/db/db/dbTilingProcessor.cc | 2 +- src/db/unit_tests/dbTilingProcessorTests.cc | 2 ++ src/drc/drc/built-in-macros/_drc_layer.rb | 2 ++ 9 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/db/db/dbOriginalLayerEdgePairs.cc b/src/db/db/dbOriginalLayerEdgePairs.cc index cf9b99c27..3544512fe 100644 --- a/src/db/db/dbOriginalLayerEdgePairs.cc +++ b/src/db/db/dbOriginalLayerEdgePairs.cc @@ -181,7 +181,7 @@ OriginalLayerEdgePairs::begin_iter () const bool OriginalLayerEdgePairs::empty () const { - return m_iter.at_end (); + return m_iter.at_end_no_lock (); } const db::EdgePair * diff --git a/src/db/db/dbOriginalLayerEdges.cc b/src/db/db/dbOriginalLayerEdges.cc index 1cb19d028..da181c17d 100644 --- a/src/db/db/dbOriginalLayerEdges.cc +++ b/src/db/db/dbOriginalLayerEdges.cc @@ -218,7 +218,7 @@ OriginalLayerEdges::begin_merged_iter () const bool OriginalLayerEdges::empty () const { - return m_iter.at_end (); + return m_iter.at_end_no_lock (); } bool diff --git a/src/db/db/dbOriginalLayerRegion.cc b/src/db/db/dbOriginalLayerRegion.cc index d089f4001..d5ba1cf13 100644 --- a/src/db/db/dbOriginalLayerRegion.cc +++ b/src/db/db/dbOriginalLayerRegion.cc @@ -330,11 +330,7 @@ OriginalLayerRegion::begin_merged_iter () const bool OriginalLayerRegion::empty () const { - // NOTE: we should to make sure the iterator isn't validated as this would spoil the usability or OriginalLayerRegion upon - // layout changes - db::RecursiveShapeIterator iter = m_iter; - - return iter.at_end (); + return m_iter.at_end_no_lock (); } bool diff --git a/src/db/db/dbOriginalLayerTexts.cc b/src/db/db/dbOriginalLayerTexts.cc index 6555eb4dc..9d8622f43 100644 --- a/src/db/db/dbOriginalLayerTexts.cc +++ b/src/db/db/dbOriginalLayerTexts.cc @@ -181,7 +181,7 @@ OriginalLayerTexts::begin_iter () const bool OriginalLayerTexts::empty () const { - return m_iter.at_end (); + return m_iter.at_end_no_lock (); } const db::Text * diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index fda051d39..c92cd4bc4 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -598,6 +598,13 @@ RecursiveShapeIterator::at_end () const return m_shape.at_end () || is_inactive (); } +bool +RecursiveShapeIterator::at_end_no_lock () const +{ + RecursiveShapeIterator copy (*this); + return copy.at_end (); +} + std::vector RecursiveShapeIterator::path () const { diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index ae337a29e..26ee32b7e 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -709,6 +709,16 @@ public: */ bool at_end () const; + /** + * @brief End of iterator predicate + * + * Returns true, if the iterator is at the end of the sequence + * + * This version does not lock the layout and can be used after initialization + * to detect empty sequences. + */ + bool at_end_no_lock () const; + /** * @brief Gets the translated property ID * diff --git a/src/db/db/dbTilingProcessor.cc b/src/db/db/dbTilingProcessor.cc index b25ce543a..131c3bb4f 100644 --- a/src/db/db/dbTilingProcessor.cc +++ b/src/db/db/dbTilingProcessor.cc @@ -876,7 +876,7 @@ TilingProcessor::execute (const std::string &desc) if (tot_box.empty ()) { for (std::vector::const_iterator i = m_inputs.begin (); i != m_inputs.end (); ++i) { - if (! i->iter.at_end ()) { + if (! i->iter.at_end_no_lock ()) { if (scale_to_dbu ()) { double dbu_value = i->iter.layout () ? i->iter.layout ()->dbu () : dbu (); tot_box += i->iter.bbox ().transformed (db::CplxTrans (dbu_value) * db::CplxTrans (i->trans)); diff --git a/src/db/unit_tests/dbTilingProcessorTests.cc b/src/db/unit_tests/dbTilingProcessorTests.cc index e31a885e5..18a741fce 100644 --- a/src/db/unit_tests/dbTilingProcessorTests.cc +++ b/src/db/unit_tests/dbTilingProcessorTests.cc @@ -148,7 +148,9 @@ TEST(2) tp.queue ("_output(o1, _tile ? (i1 & i2 & _tile) : (i1 & i2), false)"); tp.queue ("!_tile && _output(o2, i1.outside(i2), false)"); tp.queue ("_tile && _output(o3, _tile, false)"); + EXPECT_EQ (ly.under_construction (), false); tp.execute ("test"); + EXPECT_EQ (ly.under_construction (), false); EXPECT_EQ (to_s (ly, top, o1), "box (60,10;70,20);box (10,10;30,30)"); EXPECT_EQ (to_s (ly, top, o2), "box (50,40;80,70)"); diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index c384d82c6..25f47c22d 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -4624,6 +4624,8 @@ TP_SCRIPT res end + tp._destroy + DRCLayer::new(@engine, res) end From 7a2fc402fe2d92fc490b0fd48c4a991c1c0730ac Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 3 Mar 2025 01:18:53 +0100 Subject: [PATCH 044/392] Trying to fix github actions --- .github/workflows/build.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index de811ab93..79a5ae3bc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -78,6 +78,7 @@ jobs: ccache -s - uses: actions/upload-artifact@v4 with: + name: artifact-${{ matrix.os }}-${{ matrix.cibw_arch }}-${{ strategy.job-index }} path: ./wheelhouse/*.whl # The following was taken from https://cibuildwheel.readthedocs.io/en/stable/deliver-to-pypi/ @@ -92,15 +93,16 @@ jobs: - uses: actions/upload-artifact@v4 with: + name: artifact-${{ matrix.os }}-${{ matrix.cibw_arch }}-${{ strategy.job-index }} path: dist/*.tar.gz upload_to_test_pypy: needs: [build, make_sdist] runs-on: ubuntu-latest steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: - name: artifact + name: artifact-${{ matrix.os }}-${{ matrix.cibw_arch }}-${{ strategy.job-index }} path: dist - uses: pypa/gh-action-pypi-publish@v1.12.4 @@ -115,9 +117,9 @@ jobs: runs-on: ubuntu-latest if: github.event_name == 'release' && github.event.action == 'published' steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: - name: artifact + name: artifact-${{ matrix.os }}-${{ matrix.cibw_arch }}-${{ strategy.job-index }} path: dist - uses: pypa/gh-action-pypi-publish@v1.12.4 From f7a76033bb3abc52fff5a758214a90b284dab86a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 3 Mar 2025 18:04:01 +0100 Subject: [PATCH 045/392] Updated changelog --- Changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog b/Changelog index 41d3e9a7c..7af49841b 100644 --- a/Changelog +++ b/Changelog @@ -4,6 +4,7 @@ * Enhancement: %GITHUB%/issues/1989 API enhancements: several new split_... methods on Edges, Region, EdgePairs and Texts, delivering a pair of containers with selected and unselected objects. +* Bug: %GITHUB%/issues/1993 Tiling processor kept layout locks, causing DRC issues with "with_density" * Bugfix: 'Save All' was not updating the dirty flag in the inactive tabs * Bugfix: Fixing a crash when editing PCell parameters while the macro editor is open * Bugfix: Fixed a potential Crash on "save all" From ac1c656bc05e8cc0753fbcc14ff8d65496be6c98 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 3 Mar 2025 18:34:43 +0100 Subject: [PATCH 046/392] Fixed unit tests --- src/db/unit_tests/dbLayoutQueryTests.cc | 2 +- testdata/algo/dss_bug_au.gds | Bin 1208 -> 996 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/unit_tests/dbLayoutQueryTests.cc b/src/db/unit_tests/dbLayoutQueryTests.cc index 08d2587c5..ae09ecb57 100644 --- a/src/db/unit_tests/dbLayoutQueryTests.cc +++ b/src/db/unit_tests/dbLayoutQueryTests.cc @@ -1592,6 +1592,6 @@ TEST(65) s = q2s_var (iq, "cell_name"); EXPECT_EQ (s, "c1,c5x"); s = q2s_var (iq, "inst_elements"); - EXPECT_EQ (s, "(cell_index=0 r0 *1 10,-20),(cell_index=4 r0 *1 10,-20)"); + EXPECT_EQ (s, "cell_index=0 r0 *1 10,-20,cell_index=4 r0 *1 10,-20"); } } diff --git a/testdata/algo/dss_bug_au.gds b/testdata/algo/dss_bug_au.gds index 528f82c7c5564b45bfe8e88384e1c65edf29cb0b..c8a1f3215941734fbf49ed72cc21a23a04024f9d 100644 GIT binary patch delta 277 zcmdnN`Gj4GfsKKQDS|(8CIOOGH`&+F$9?+iOUqz$xTf8IAzTy`!P?(=Jd&5n02v= Io3OM10J7gB1^@s6 delta 338 zcmaFDzJpVVfsKKQDS|3=A@?I32~n0k*~vVu>;?b4(dnxEUDOgn-sf jmSVD*{Dm4wQWGPHB1w+`FpYgvo2Qg1m+e1oEa`- From 7b4e8a346299545daa02668ae5589c04950d6fd2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 4 Mar 2025 19:50:33 +0100 Subject: [PATCH 047/392] Refined solution for DSS remapping bug --- src/db/db/dbDeepShapeStore.cc | 58 ++++++++++++++++----- src/db/unit_tests/dbDeepShapeStoreTests.cc | 40 ++++++++++++++ testdata/algo/dss_bug2.gds | Bin 0 -> 852 bytes testdata/algo/dss_bug2_au.gds | Bin 0 -> 1072 bytes 4 files changed, 85 insertions(+), 13 deletions(-) create mode 100644 testdata/algo/dss_bug2.gds create mode 100644 testdata/algo/dss_bug2_au.gds diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index 23a28ef30..c6bbb6aee 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -1233,7 +1233,6 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout if (! new_pairs.empty ()) { // the variant's originals we are going to delete - std::set cells_to_delete; std::vector > new_variants; // We now need to fix the cell map from the hierarchy builder, so we can import back from the modified layout. @@ -1248,7 +1247,6 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout // create the variant clone in the original layout too and delete this cell VariantsCollectorBase::copy_shapes (*into_layout, np->second, icm->second.original_cell); new_variants.push_back (std::make_pair (np->second, icm->second.original_cell)); - cells_to_delete.insert (icm->second.original_cell); // forget the original cell (now separated into variants) and map the variants back into the // DSS layout @@ -1279,26 +1277,60 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout std::vector mapped = cm->second.target_cells (); std::sort (mapped.begin (), mapped.end ()); - // Copy the variant instances - but only those for cells which are not going to be - // deleted and those not handled by the cell mapping object. + // Copy the variant instances - but only those for cells which are not handled by the cell mapping object. for (auto vv = new_variants.begin (); vv != new_variants.end (); ++vv) { + const db::Cell &from = into_layout->cell (vv->second); db::Cell &to = into_layout->cell (vv->first); for (db::Cell::const_iterator i = from.begin (); ! i.at_end (); ++i) { - if (cells_to_delete.find (i->cell_index ()) == cells_to_delete.end ()) { - auto m = std::lower_bound (mapped.begin (), mapped.end (), i->cell_index ()); - if (m == mapped.end () || *m != i->cell_index ()) { - to.insert (*i); - } + auto m = std::lower_bound (mapped.begin (), mapped.end (), i->cell_index ()); + if (m == mapped.end () || *m != i->cell_index ()) { + to.insert (*i); } } + + } + + // clean up instances of variant original cells + + std::map > delete_instances_of; + + into_layout->force_update (); + + for (auto vv = new_variants.begin (); vv != new_variants.end (); ++vv) { + const db::Cell &to = into_layout->cell (vv->first); + for (auto p = to.begin_parent_cells (); p != to.end_parent_cells (); ++p) { + delete_instances_of [*p].insert (vv->second); + } } - } + std::vector insts_to_delete; + for (auto di = delete_instances_of.begin (); di != delete_instances_of.end (); ++di) { + db::Cell &in = into_layout->cell (di->first); + insts_to_delete.clear (); + for (auto i = in.begin (); ! i.at_end (); ++i) { + if (di->second.find (i->cell_index ()) != di->second.end ()) { + insts_to_delete.push_back (*i); + } + } + in.erase_insts (insts_to_delete); + } + + // remove variant original cells unless they are still used + + into_layout->force_update (); + + std::set cells_to_delete; + for (auto vv = new_variants.begin (); vv != new_variants.end (); ++vv) { + if (into_layout->cell (vv->second).parent_cells () == 0) { + cells_to_delete.insert (vv->second); + } + } + + if (! cells_to_delete.empty ()) { + into_layout->delete_cells (cells_to_delete); + } - if (! cells_to_delete.empty ()) { - // delete the variant original cells - into_layout->delete_cells (cells_to_delete); } } diff --git a/src/db/unit_tests/dbDeepShapeStoreTests.cc b/src/db/unit_tests/dbDeepShapeStoreTests.cc index c0a56db08..4a01e194c 100644 --- a/src/db/unit_tests/dbDeepShapeStoreTests.cc +++ b/src/db/unit_tests/dbDeepShapeStoreTests.cc @@ -301,3 +301,43 @@ TEST(6_RestoreWithCellSelection) db::compare_layouts (_this, ly, tl::testdata () + "/algo/dss_bug_au.gds"); } +TEST(7_RestoreWithCellSelection2) +{ + db::Layout ly; + + { + std::string fn (tl::testdata ()); + fn += "/algo/dss_bug2.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + + db::Cell &top_cell = ly.cell (*ly.begin_top_down ()); + + db::RecursiveShapeIterator in_it (ly, top_cell, l2); + db::RecursiveShapeIterator other_it (ly, top_cell, l2); + + std::set us; + us.insert (ly.cell_by_name ("X").second); + in_it.unselect_cells (us); + + std::set us2; + us2.insert (top_cell.cell_index ()); + other_it.unselect_cells (us2); + other_it.select_cells (us); + + db::DeepShapeStore dss; + + db::Region in_region (in_it, dss); + db::Region other_region (other_it, dss); + in_region += other_region; + + ly.clear_layer (l2); + in_region.insert_into (&ly, top_cell.cell_index (), l2); + + db::compare_layouts (_this, ly, tl::testdata () + "/algo/dss_bug2_au.gds"); +} + diff --git a/testdata/algo/dss_bug2.gds b/testdata/algo/dss_bug2.gds new file mode 100644 index 0000000000000000000000000000000000000000..1c4b8c47be0aa6b1c8fa10bf93cd744935bd29a8 GIT binary patch literal 852 zcma)4u}%U(5Pi41H*63MB9*zy+Qb|&8WTez*cgdgT!DOuA7G~+Kx1QPYm9}ZmGKKK zEGdX~v?4gp-Ejh^!d)_Xm)Y!__udW&N;Tm*D)$ZzgFGzClmD=a8V5C?tljF~#rEOT zO>bxaVdMI$6~J?6+#_oEn}D$ZEh;#mB?Vx;sG0dTDe3g2x(O!z5Mh?(ehxF|5K-NS zSpaM)o@iIXX%4)i7S>&<`(K!qx$++~^YD!M^Er*N=83Oil-0v`lI6PwhdK+WX z?{~6#Zb~xZnMzczfL0xGSn%9hOECPVGEkR*n+H-DWiaCK=W*yN;Jg4S%$Vb8tTuU} zIeKv}vjTB3jnS;Xl4V&>v}I!i)t%g38)MQ(J5#cjC^$Y5wGx+PE4mBe7U@C69w~@V RB!4+!{&v@T>fBe%d4LnY`;oWoY@4Y}nsX78nWnN*!K^_Gx55B`wx>_j#^R`>u zKHb>8zie-oZ&xnP8#M&(-}`*6tKr&GxpD|N9-yZcEa({ppfGET$W^jfX>&a%n$(TL zndANp{`K-AugUvliI&Eb{&^sxhxG-fEC{cU`2@ z5;5A_6Zv@QYf|sDQt>wF!YG{=r2__%@f5eXZhmN*lVs;3(&gOQcy6-R3l@9%fm>=& z%`@zF0$4Rxhc$Bhjf3zqqshBPRoC3R*w>_vW?ByQIIYf^^BR*>odIr^Zo?WS<;12( yIIf3XWa39)MANY4UsBsYU|vR`7diX0Ham4A7Ey=z3uodNk+(bEkf(1Fj)PBU7U2H? literal 0 HcmV?d00001 From 465f13adc63ebc5fb04bc779213f37dd6c161c22 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 4 Mar 2025 19:50:52 +0100 Subject: [PATCH 048/392] Corrected naming of artifacts in build action --- .github/workflows/build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 94fe97524..9b7295218 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,14 +22,18 @@ jobs: include: - os: "macos-13" # intel runner cibuild: "*macosx*" + cibw_arch: "macos_x86_64" macos-arch: "x86_64" - os: "macos-14" # M1 runner cibuild: "*macosx*" + cibw_arch: "macos_arm64" macos-arch: "arm64" - os: "ubuntu-latest" cibuild: "*manylinux*" + cibw_arch: "manylinux" - os: "ubuntu-latest" cibuild: "*musllinux*" + cibw_arch: "musllinux" steps: - name: Free Disk Space (Ubuntu) if: matrix.os == 'ubuntu-latest' From 70af6bd7c9987ce2ed113d7f564b8b49590bd494 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 4 Mar 2025 20:37:58 +0100 Subject: [PATCH 049/392] Another refinement for DSS bug solution --- src/db/db/dbDeepShapeStore.cc | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index c6bbb6aee..9a2cd9599 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -1320,10 +1320,28 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout into_layout->force_update (); - std::set cells_to_delete; + std::set vars; for (auto vv = new_variants.begin (); vv != new_variants.end (); ++vv) { - if (into_layout->cell (vv->second).parent_cells () == 0) { - cells_to_delete.insert (vv->second); + vars.insert (vv->second); + } + + std::set cells_to_delete; + + bool more = true; + while (more) { + more = false; + for (auto v = vars.begin (); v != vars.end (); ++v) { + if (cells_to_delete.find (*v) == cells_to_delete.end ()) { + const db::Cell &vc = into_layout->cell (*v); + bool used = false; + for (auto p = vc.begin_parent_cells (); p != vc.end_parent_cells () && ! used; ++p) { + used = (cells_to_delete.find (*p) == cells_to_delete.end ()); + } + if (! used) { + cells_to_delete.insert (*v); + more = true; + } + } } } From 605fa44ca67c217ddf308dd839145e97f587e1eb Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 5 Mar 2025 10:26:45 +0100 Subject: [PATCH 050/392] Another refinement for DSS bug fix. --- src/db/db/dbDeepShapeStore.cc | 34 +++++++++++------ src/db/unit_tests/dbDeepShapeStoreTests.cc | 41 +++++++++++++++++++++ testdata/algo/dss_bug3.gds | Bin 0 -> 926 bytes testdata/algo/dss_bug3_au.gds | Bin 0 -> 1026 bytes 4 files changed, 63 insertions(+), 12 deletions(-) create mode 100644 testdata/algo/dss_bug3.gds create mode 100644 testdata/algo/dss_bug3_au.gds diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index 9a2cd9599..687008b9c 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -1244,7 +1244,7 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout std::map::const_iterator icm = cm_skipped_variants.find (var_org); if (icm != cm_skipped_variants.end ()) { - // create the variant clone in the original layout too and delete this cell + // create the variant clone in the original layout too VariantsCollectorBase::copy_shapes (*into_layout, np->second, icm->second.original_cell); new_variants.push_back (std::make_pair (np->second, icm->second.original_cell)); @@ -1269,22 +1269,32 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout // copy cell instances for the new variants - // collect the cells that are handled during cell mapping - - // we do not need to take care of them when creating variants, - // but there may be others inside "into_layout" which are - // not present in the DSS and for which we need to copy the - // instances. - std::vector mapped = cm->second.target_cells (); - std::sort (mapped.begin (), mapped.end ()); + std::map variant_to_org; + for (auto vv = new_variants.begin (); vv != new_variants.end (); ++vv) { + variant_to_org.insert (std::make_pair (vv->first, vv->second)); + } // Copy the variant instances - but only those for cells which are not handled by the cell mapping object. for (auto vv = new_variants.begin (); vv != new_variants.end (); ++vv) { - const db::Cell &from = into_layout->cell (vv->second); - db::Cell &to = into_layout->cell (vv->first); + const db::Cell &from = into_layout->cell (vv->second); // original + db::Cell &to = into_layout->cell (vv->first); // variant + + // Collect and copy the cells which are not mapped already. + // Skip variant original cells if their variants are included. + std::set dont_copy; + + for (auto c = to.begin_child_cells (); ! c.at_end (); ++c) { + auto v2o = variant_to_org.find (*c); + if (v2o != variant_to_org.end ()) { + dont_copy.insert (v2o->second); + } else { + dont_copy.insert (*c); + } + } + for (db::Cell::const_iterator i = from.begin (); ! i.at_end (); ++i) { - auto m = std::lower_bound (mapped.begin (), mapped.end (), i->cell_index ()); - if (m == mapped.end () || *m != i->cell_index ()) { + if (dont_copy.find (i->cell_index ()) == dont_copy.end ()) { to.insert (*i); } } diff --git a/src/db/unit_tests/dbDeepShapeStoreTests.cc b/src/db/unit_tests/dbDeepShapeStoreTests.cc index 4a01e194c..d14be57fe 100644 --- a/src/db/unit_tests/dbDeepShapeStoreTests.cc +++ b/src/db/unit_tests/dbDeepShapeStoreTests.cc @@ -341,3 +341,44 @@ TEST(7_RestoreWithCellSelection2) db::compare_layouts (_this, ly, tl::testdata () + "/algo/dss_bug2_au.gds"); } +TEST(8_RestoreWithCellSelection3) +{ + db::Layout ly; + + { + std::string fn (tl::testdata ()); + fn += "/algo/dss_bug3.gds"; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly); + } + + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + + db::Cell &top_cell = ly.cell (*ly.begin_top_down ()); + + db::RecursiveShapeIterator in_it (ly, top_cell, l2); + db::RecursiveShapeIterator other_it (ly, top_cell, l2); + + std::set us; + us.insert (ly.cell_by_name ("X").second); + us.insert (ly.cell_by_name ("C3").second); + in_it.unselect_cells (us); + + std::set us2; + us2.insert (top_cell.cell_index ()); + other_it.unselect_cells (us2); + other_it.select_cells (us); + + db::DeepShapeStore dss; + + db::Region in_region (in_it, dss); + db::Region other_region (other_it, dss); + in_region += other_region; + + ly.clear_layer (l2); + in_region.insert_into (&ly, top_cell.cell_index (), l2); + + db::compare_layouts (_this, ly, tl::testdata () + "/algo/dss_bug3_au.gds"); +} + diff --git a/testdata/algo/dss_bug3.gds b/testdata/algo/dss_bug3.gds new file mode 100644 index 0000000000000000000000000000000000000000..39c289ac5c4cd031cad63edc79dd6e2f9bbcf6d5 GIT binary patch literal 926 zcmbW0KQ9D97{;HO*_oST$>Aa)TdO2;rw|H>qaY*{TO74q)S^&{LPH^#BV0w5AE6vVU2WTq2x^}s= z{rvF0yz#Vfe|Hcf(8c?VG3$%)EMUeY1kAVuFyBy76rC{69075}2s38$j_@V`uZGer zZFr;1GU@W!DVIwiXeeeoWW4-hd~T{s4(l59n71EAgC3wR7}puIdB@7STK~~iUT>GK zr1iF7W(Di>1KoKf-IsGfUIFzF;0I#|(lgp+XQ)>|H|a#41uiBc^l3tWj(a)In9Y+F zySx8~YAyR^j%h)jg-T_RsC2B~Qsv4w-AcM%MX5C=+})$rk@zN3wV&K{`nH+Wnyj#U U?Adv~XZ)8Bj2?@dFJC+lpVU2ctpET3 literal 0 HcmV?d00001 diff --git a/testdata/algo/dss_bug3_au.gds b/testdata/algo/dss_bug3_au.gds new file mode 100644 index 0000000000000000000000000000000000000000..45c7459465a4b3dc0aba46c97a8eeda0cbcd074c GIT binary patch literal 1026 zcmcIju}Z^G6g}@Hd40xKs@6p^7AHYc2hl;WcCaWY*r7U0aTW(Bad6Q=#I+7?{(ysD zAby3ro0DVXxk;l9r8Wo#ZqDPrX^|=G0h9Yod(^|{eYlfknh{PrD|`w;*&*W*<8=u0)V6W$@EA4Uj2zEPsUwKE_1?U7GrmZdj)im6cL=b zW8t+Ql%4*5p6RgHcG9a%y*92>=MC^GbS8^26ZQJBUO7i&6zgkt{K-zUI8w2w^3LQ< zwF}8Q#WYXMqnPKhwYx{netgn7sbX@QsA?;zknGSGs<_C`jo Date: Wed, 5 Mar 2025 18:41:28 +0100 Subject: [PATCH 051/392] Updating doc, py includes --- src/doc/doc/images/drc_textpoly1.png | Bin 3343 -> 5631 bytes src/doc/doc/images/drc_textpoly2.png | Bin 3343 -> 5600 bytes src/pymod/distutils_src/klayout/dbcore.pyi | 591 +++++++++++++++++++-- 3 files changed, 550 insertions(+), 41 deletions(-) diff --git a/src/doc/doc/images/drc_textpoly1.png b/src/doc/doc/images/drc_textpoly1.png index 3e6df4d40ec153b40f5c9a2bc5553d0fb2c6f43a..487cfcc9ca818fc3947998e6294cada21108eca8 100644 GIT binary patch literal 5631 zcmd5=X;f2Z8V)i_>#5M86|}MqN?q6#lr4~mAiJoDMT!y>G}Tc+5($t^hdK;JjaUHz zA$3$z8Cw*rOxVIyL1g<$K=e zdA~1vM|j|p#h)xjp-@YLgZv{>ra!@n@g)+Vm{S5N#jaNV~ ziCgy&{SF>Ix&alreMjKY=U(7T8|Y=k!FVESjiZaxru7~U;A8!I7qo}dMo$+94<}Dg zkF{yOK7lCI(&S)&-^jGfBmFxs@B;ETKZ*==zL_t)ei&b5vd#6kRrU)*90s;{tQOwO zxc0a2Lz-Pb4X%0sxJGNjrkt~{c5q8|8)! zKb31Ky%Xl1p$TA40?jf>!3HV+sjZGcZ2T+-{jlQip8d*AN$wr=%8gicY!X1-M$$Io zhJCnHW)%>HTSgJ9u$`OlV(Im3RyzNg??Tg+8S{sKux@#3YnOW_cWrjUnTS^PF>HRH z3oz767e5+Psal=u6^b8*_B8MOVDH1j=MGA>Qz|9X`>89Agx2IA3fz#qjm^~^-@i?^ zXgPr1!qzr*lk&oFFUE3iT4#v+Hz|B7bPX7?f}1bs9jOMQd!DHmXg(LEt%(>HEl=_c z&DLxTC|h-5gUf6L?=@fI_mU`7TGm_rz0-Koka#Z3 z*3RYATz!mCRv~t9FpX$phi>KEMvHF<-XE{c-H;9CrLV-!bq4T z1ZAHIzV@%xgTLGRgm{z=aGU&iOt#3cJn}|F-t;d1sf^EDh1U_g%sNJtdLiB_})%l5rv2z<6 z4ZI%s&4&eIT}y`fK+4#|DZEs5e%% zC2LGI|LMiVf2sRt<5~O<6~3@pkNaLvpF^(Xc@7Iyj0gXdpy(B2-=!f!QeoEG^j0|zATaM}}f$LL_kVhYlHFaGM2H6r&t9@j_lPk9eJr@x! zcGz{VJ(YLX=%%osBrqR6TI49pS3i2{?lJ+tV4<6K}Z)@j-8zJlDDi* zJ%I^!tmLB$!dSoi>~5X&kD8z4Eh$E^gj!;8ag%10J!H(f(|~q$hYDhrRW20fu#VrF zA>K+~TV|6-NJT<`Fn8z)T!Qh5u92f=@_VA(A>+D3>XIV1XYwa2O!l-pqpSH>XP5v? z?eTuygi%V+_U9kfdNHq=MN3OU%XTfWuMVxg^{6cq4x#Ebw+4rEmmDZ9d6|57C<$0% zs&Z(~O}DF(zfAuqXP2wHQF#2I!QWH|MfvE)L$*8jcj!Mw1cWF1xq)Z}1)vgqnW0&|YFzFa zBDx^H*!WYk;b(Gg$4ahcTKini@2&k#*3s7Ps8AH28^JH>%6)YU<_CznGrxw!ciKY1M+>K|dqtDQw0T2z1j-pAoTas3+^m_}1{K zh2%B-6-5OiaM-vw*WQ1#8YJmTkGOgv#fyb0$7`n_l!C>U?R6sTVRjZpQbrEPo|dluzI#m=S8|Ckx;S{4=RLYanSum1o5oq>b&kB*E5$D%>M^@LVp zNZe4W*vzdcJsAe@FC@HB2nF$MM-QJew$aA1(zmS&ZYNl7(=)_U4h%z=iiFhI{pTDM zNGFYujtGT~hmU&4ZQ{VK_$!i~qvBg+rg)wzwyO2DqT94@tLD|ogOBJTTnXWh;XoSL zl;gLXAaCkEd8cL3hRzO0lE-ycK_Y&J5@w13`oVcIC}x8#Ab#kl)A0z95y-=<#5^(I zPa9$nbrfop>3-ufptv+StWQddt-+S6R9`-5)e4k-=H6av(99mP>Vd}Ju;q+U zjre=u#TmnGBz*TGV7MSjVAE~#yz#6X@X`3-X=djN^2^@&@h5z!CW0hYM9H*wGwRBn zQz|lK4;VdkqfsK8H4RX}a*lq&rPdI86++22MO25mLKyXUI0wI_Xh=t7^MdiV^^74k)xmY!k>VkV zu-m>+vV?Xz>e9?U?l~^P)H^>D*ZO)DlK|DkV);Tk24sbb=(?pTS?>O3e5ZaE>qBOJ zFWI90cxo~BNims;PF^6XbtMqEmf?qwe|sdcA?*_*$;`a6V%2!`nX3~ahZ4Y4kSgn2 z5{AlM!HvRVLs4>{qLJdp(QdZ&In5r@(YjkZW~K5(c^Sz)p#0qQFWo6PW+s-F%M$%; z4d?`sHS{mWCYg({G(m-Ky4xh-xDG=V2qZv#1wf`dakP_Ib!{n45@nsiHTT@Nl)?_J z$C0_3F(B`7s??JtGGBx6aM*R zgfynmn;<=Mv$|D;ONH3#SUT=YXhm`)!rCddL`J_21AAUr#yf`57Wf-Y&3+7Zsdpv+ zRIh{a?)G-`?S@^pEAD%PtotexFkT?V1ZzbVYX$|($+Wob&-2=J_HAehLIP=xM`(TN z32eiP89sn;&%4g!I|#EnXS*kXO1jIVjB%FVk$aD4?!#JtMFnXSTJ!FGj)uGmXVty~f^1&X9fxQ^juXuzG@Uppp(HLadEYZ;oG& zk4S+%A5XSrKJHPi`aw@(*^uh@HmU;>q`j%xp{cKzI$I`e^C1O zUoA93R1EQaZ>=Lz*ul7&aXulFPqWEV5HYvyT^Rc8^$Y>GKAh~S?#FOVVJRJ={$3bW z`Z9o#E}zs)e@xD8S6SVU7J6JmJ%DK7J0=RPwk%$)#yMI2s%!JHj>!A2hC`%dh{c-m zLa>!90A+~ynS`!US1n^z027MJd%g7Oc#5V0P1g$4gmiVUbtg?(ftkp6PN`Y<_#UEI z{jZ=7WZhXA01=32M+2I7tieOnpRSu=90E$klbMEO+ZpCQIMcG{&H8MFeNes=`f&iF za2VcOW_T0aiQdA%*w*1!(ngpP#vXgFnFl^aAv4djukWUoVs(!!x?HKIeW!f%} zd+cP7rV{2fuG+S(!W)`|kN|ks$-|4XDQ5BbC$FFn7Cm@?daa;{GPGO*p{)?uVCgoe z&zh<3f8VkC#@2t!A+PNwd|U&ij(6+{IKZ^n7naO2cig-7QBOsc+VGK3k+rpTE*yex z^^K96uzVjx#x6K~eY&sC(oZiUny%(i)k>vOr&W*izPuV(7cbpmxC)NXooWhedN zQq`$f?l5NVlcKd#oDVQM(NwRDVW`{(_;?N6r&)<96iOJ~Ao=frg=O+8$ueb3J)TW6 zwsXtZcn7I!+O6hVs<3WiSIlKLeaak_Rm;EK#^m3M5i~~c9b4FQI1otb6_f&6HA!tb zkQ7&Jh$lWWkK`1+em~NR<1&_kyT3+qd7uC57Z!C_pEEGy)$+{WM$;E<{s(Rxqi6}4 zOhVf{O94)=Yh1cVilSfP9AgI>2fTIg4p`!HOfd-R1|LtA{0)r!Dsa)+wP#SK~Ut|2mg{qd>EZqFpolYh`XgX&jLV5~5 zE_3?a>(l0a_v)Xg%dHGr>J|FxqWEe^Y)(4Yg*{KuH3c(JJx}PpIOB{X1wS}$o%Z|s z^n97U|IeMb0S3p^RaX}4hk;Vuht|@zdDs&-1D*_s$1h^&6HOVT{>r{F=pZXaPPj+Eguw?Ti?3E++ z-GRG#N7tN0USq80#Qr??m=9HFetms?eJfwXj&tAsFIS!aS$>z2ya%Kmh-jnLgx3F@ zeem@EKOet-E@Y^`TA#;Wlepj4N?%|9xH!Z0zrV{4_{JSSQ2zDI>Al8Ydp1AWuK09* z^=5aw35b>+q?Ps4%kIm{y&HG_->=WG?swtlGxvWs-hD0oL#ytQen@}$m-+i%|KIWd z`ug~L?-}klZnyj(`u9Toihq~UTWdeJ)c{-n3=Wa$yF1VtWp6qs?ff+7?VG*(f>A}B#Zm;%)&^#KA3 zM1>Gt3Mg8mV95#y!KX|m5KB~y2?QAuBa#pTgpldofrz!}57t}#k(ImdS@-OH_WsWI z?Y&QJ^}!m>`(_>h0EU}AJ^TP*)(G_b=mYSL#~sg3@bBXzo+bKItc3UD zuLK2J_I`cVx}p&@0d(JSb8|cGGfv7`@by{G&CBp=Hn=P=z& z7L6XF%>I+xbk0M+;rTP34J-2CO#$gclqzf5#Xz~-+=Qwwrs-m_NxM?WN%gXaR2#*h zC9Wzo%h@hrqH~-m$}Y=6ZOjPG*fQzxW&h!196_YD(>0Vg3LdRft|oQVUDlZ`9{NR9 zKQhRpm(K5G8?#BHqk^8m96@bTLgGPJs&-7(LDlBd#xnsmW7I}8Nalp!*Cfj5Q6vpL zLH!-`rS7<(BdVg4aetFq^djZAMTCpEqxdS3p_EqpglAGxo!o6VR@7h1=IX9TeOf!B zW;BPoWHEkT)CqoI8-b@@#te#NUsi|5~f01NV=2+bPFnGFfk97LPV{sRAlrsua}O`L!h7n9?{* zSL*8bVM1Jpgt=286xM}rAx9oQe$L#D&VQaOvFq$UuojaT$+*9=)3#HHJY$nWle^qN zn3mU~d57JS+>KrlC$JSHc38u6l5M)^)eU@ejMtw1N3R}=td097dl7eukjv;dDjgYV zAB)3egIRcggk-R-4S<(Q1dzvLn9G*lt5E;q99z)0`-TkqvNFn%48hW}N&C2QTVuU(d z-dX^eCx$X+(}?lnSV#S*D#`^0YAHeu%?-1k46rPyPai9n|K1cW4Y8*z*ufpfe=ZCx z!sergF7S$Zd~0DE+lJs&Jlc7l0iEWKlmGe+IG4=hlh$(#7bvY8w#8dH*k@TQ+AKLM zQ}as6kzo~JFyS-sq>oswshp>Ab%&C{c$hs##5+RC_;s<+m$@`;#KZ0Ob@}E)de5V) zV?Hryb#a2f9;kM}!)Jagtl&1V%)8XYC}>u zI!Cix7jUsqCJ!qaAIVq4XL)@t#Z`ppLz4)Vyu0$CjGVu2(zEq3KT)=BiB0*ptO_^{ z;$j{b4hi@vEFhsO$}@7C1-0JEURSU+OLxMf7K0Tdp@feN=U_6b!IC3hbZnXiD~}rbsUDw0)lLiqojlN;;@r&-wCEDG8S~fARQ) zg4vo$vL&e}nDNgzf|l#^Yx+0&1b^TwxERujwF4|p1Y2BMV&W}#p{w%rlK%|eS z=%N(_Vy8Vd!bIm%x3Vyewy{V)^Yq(m$ZiH>F-5)wI;rJX80>)p{%9pv!9r@rr|=9u(e#gBK*AoG*aFmF^yZm)CPr?ljF^ z%WkvLXRl9OT&E-DUa3VFp65(o6g~g z(A`0tpnEHx8V-*ehDTO)3MsyR>3Rr5a7T=rmR%^YE1G*{ljE;M7)Y))G3bJN^$?6@ z7=#hb?W%TSU&U8gA%&$yv(N+Y%zt4sJFLXY^l@bc+gl-0NCixtT1ljiv2~qAYkrXE z8gMn;ToH%fBUj0lC&a;zUXgnF(OEAc*)7RvwA99Qkd8H^mQ+`a&SeV5(*sqpqTD(+Sq>(8(H!-*51HznsBid~~ zu4)4=s_J?x?H1RVePUgUMjTD&M#*u9htea)>d!81jV6Uie0}%bWN;hrFI8#ecB4iP zD5!#HjuLF$#b&PP-N2Yn_f^4Ln*Slg4C_txVm@>v5&%Pn-Z>a#461Gf9FcqGNgh#7dc)a9x)4&)crgS9FhLhv1!mGI?h+LuzJ~R0@bT65Mjqt|Dj}ej~KoeC1Sz!Wopm}^tdg0A^W492J zB!KDC`}k(n0s0HKAs`nII^qiD5*HF&{ETStrMl{syBw(v3_a5$K9hq{1~0`lwFlgJ zOx7OK(Ach@PU}h624nnFaz9MFU|d@D!nG6O|80|1OV8G0Y-P{c%Ge@|_D|DxholAE zDHq;X_DtQZ%afA_(BSR+F2g|?7IL2hqQyeJ3WC=Eoq52l5Uzs$UinORfQHeklcrr9 z5P$EZBLkm(y|b<>_lqDx%crKVVGd8>FOn>|ZL%D6fI@u3JS=#y?fOe)3}sLF#+@*# zUl)+WBki4}lEm}QQMWY+5Wd1(N571@?Ks!`F|OeYm)Fb8gefs1_>a`Wx)=saiKT+X!F zo9RSi#zTjWEmmj+GsNYN=6r(N6)$i=aLq1h&zDfI2W)Le!P+v-G;HWh6xP7BTf1fe z40GBs-~m{(lY-0Hq=#QBPrfmRKt==2K^htJQ#b8V)qtXLmiihvMb87ba!6AKsqD^wIi*KLrn%d#ug&ZxffU zZNoITVY)o?#Sn4s#(TJ7yd)>@r66|WgMRHN0V%6*@n>njBk`iMOf%jZ6b_wy`Gbh% z>i0niA2)pRkO=1h0G~Z!96del&=9mg(<#zB8g%Xa0p0EDGd8=+q3mtmg4p`!HOfd-R1|LtA{0)r!Dsa)+wP#SK~Ut|2mg{qd>EZqFpolYh`XgX&jLV5~5 zE_3?a>(l0a_v)Xg%dHGr>J|FxqWEe^Y)(4Yg*{KuH3c(JJx}PpIOB{X1wS}$o%Z|s z^n97U|IeMb0S3p^RaX}4hk;Vuht|@zdDs&-1D*_s$1h^&6HOVT{>r{F=pZXaPPj+Eguw?Ti?3E++ z-GRG#N7tN0USq80#Qr??m=9HFetms?eJfwXj&tAsFIS!aS$>z2ya%Kmh-jnLgx3F@ zeem@EKOet-E@Y^`TA#;Wlepj4N?%|9xH!Z0zrV{4_{JSSQ2zDI>Al8Ydp1AWuK09* z^=5aw35b>+q?Ps4%kIm{y&HG_->=WG?swtlGxvWs-hD0oL#ytQen@}$m-+i%|KIWd z`ug~L?-}klZnyj(`u9Toihq~UTWdeJ)c{-n3=Wa$yF1VtWp6qs?f EdgePairs: + def filtered(self, filter: EdgePairFilter) -> EdgePairs: r""" @brief Applies a generic filter and returns a filtered copy See \EdgePairFilter for a description of this feature. @@ -22596,6 +22597,14 @@ class EdgePairs(ShapeCollection): Starting with version 0.27, the method is called 'count' for consistency with \Region. 'size' is still provided as an alias. """ ... + def split_filter(self, filter: EdgePairFilter) -> List[EdgePairs]: + r""" + @brief Applies a generic filter and returns a copy with all matching shapes and one with the non-matching ones + See \EdgePairFilter for a description of this feature. + + This method has been introduced in version 0.29.12. + """ + ... def split_inside(self, other: Region) -> List[EdgePairs]: r""" @brief Selects the edge pairs from this edge pair collection which are and are not inside (completely covered by) polygons from the other region @@ -22635,6 +22644,200 @@ class EdgePairs(ShapeCollection): This method has been introduced in version 0.29.6 """ ... + @overload + def split_with_abs_angle(self, angle: float, inverse: bool) -> List[EdgePairs]: + r""" + @brief Like \with_abs_angle, but returning two edge pair collections + The first edge pair collection will contain all matching shapes, the other the non-matching ones. + Note that 'inverse' controls the way each edge is checked, not overall. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_abs_angle(self, min_angle: float, max_angle: float, inverse: bool, include_min_angle: Optional[bool] = ..., include_max_angle: Optional[bool] = ...) -> List[EdgePairs]: + r""" + @brief Like \with_abs_angle, but returning two edge pair collections + The first edge pair collection will contain all matching shapes, the other the non-matching ones. + Note that 'inverse' controls the way each edge is checked, not overall. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_abs_angle_both(self, angle: float, inverse: bool) -> List[EdgePairs]: + r""" + @brief Like \with_abs_angle_both, but returning two edge pair collections + The first edge pair collection will contain all matching shapes, the other the non-matching ones. + Note that 'inverse' controls the way each edge is checked, not overall. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_abs_angle_both(self, min_angle: float, max_angle: float, inverse: bool, include_min_angle: Optional[bool] = ..., include_max_angle: Optional[bool] = ...) -> List[EdgePairs]: + r""" + @brief Like \with_abs_angle_both, but returning two edge pair collections + The first edge pair collection will contain all matching shapes, the other the non-matching ones. + Note that 'inverse' controls the way each edge is checked, not overall. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_angle(self, angle: float, inverse: bool) -> List[EdgePairs]: + r""" + @brief Like \with_angle, but returning two edge pair collections + The first edge pair collection will contain all matching shapes, the other the non-matching ones. + Note that 'inverse' controls the way each edge is checked, not overall. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_angle(self, min_angle: float, max_angle: float, inverse: bool, include_min_angle: Optional[bool] = ..., include_max_angle: Optional[bool] = ...) -> List[EdgePairs]: + r""" + @brief Like \with_angle, but returning two edge pair collections + The first edge pair collection will contain all matching shapes, the other the non-matching ones. + Note that 'inverse' controls the way each edge is checked, not overall. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_angle(self, type: Edges.EdgeType, inverse: bool) -> List[EdgePairs]: + r""" + @brief Like \with_angle, but returning two edge pair collections + The first edge pair collection will contain all matching shapes, the other the non-matching ones. + Note that 'inverse' controls the way each edge is checked, not overall. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_angle_both(self, angle: float, inverse: bool) -> List[EdgePairs]: + r""" + @brief Like \with_angle_both, but returning two edge pair collections + The first edge pair collection will contain all matching shapes, the other the non-matching ones. + Note that 'inverse' controls the way each edge is checked, not overall. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_angle_both(self, min_angle: float, max_angle: float, inverse: bool, include_min_angle: Optional[bool] = ..., include_max_angle: Optional[bool] = ...) -> List[EdgePairs]: + r""" + @brief Like \with_angle_both, but returning two edge pair collections + The first edge pair collection will contain all matching shapes, the other the non-matching ones. + Note that 'inverse' controls the way each edge is checked, not overall. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_angle_both(self, type: Edges.EdgeType, inverse: bool) -> List[EdgePairs]: + r""" + @brief Like \with_angle_both, but returning two edge pair collections + The first edge pair collection will contain all matching shapes, the other the non-matching ones. + Note that 'inverse' controls the way each edge is checked, not overall. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_area(self, area: int) -> List[EdgePairs]: + r""" + @brief Like \with_area, but returning two edge pair collections + The first edge pair collection will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_area(self, min_area: int, max_area: int) -> List[EdgePairs]: + r""" + @brief Like \with_area, but returning two edge pair collections + The first edge pair collection will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_distance(self, distance: int) -> List[EdgePairs]: + r""" + @brief Like \with_distance, but returning two edge pair collections + The first edge pair collection will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_distance(self, min_distance: Any, max_distance: Any) -> List[EdgePairs]: + r""" + @brief Like \with_distance, but returning two edge pair collections + The first edge pair collection will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_internal_angle(self, angle: float) -> List[EdgePairs]: + r""" + @brief Like \with_internal_angle, but returning two edge pair collections + The first edge pair collection will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_internal_angle(self, min_angle: float, max_angle: float, include_min_angle: Optional[bool] = ..., include_max_angle: Optional[bool] = ...) -> List[EdgePairs]: + r""" + @brief Like \with_internal_angle, but returning two edge pair collections + The first edge pair collection will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_length(self, length: int, inverse: bool) -> List[EdgePairs]: + r""" + @brief Like \with_length, but returning two edge pair collections + The first edge pair collection will contain all matching shapes, the other the non-matching ones. + Note that 'inverse' controls the way each edge is checked, not overall. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_length(self, min_length: Any, max_length: Any, inverse: bool) -> List[EdgePairs]: + r""" + @brief Like \with_length, but returning two edge pair collections + The first edge pair collection will contain all matching shapes, the other the non-matching ones. + Note that 'inverse' controls the way each edge is checked, not overall. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_length_both(self, length: int, inverse: bool) -> List[EdgePairs]: + r""" + @brief Like \with_length_both, but returning two edge pair collections + The first edge pair collection will contain all matching shapes, the other the non-matching ones. + Note that 'inverse' controls the way each edge is checked, not overall. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_length_both(self, min_length: Any, max_length: Any, inverse: bool) -> List[EdgePairs]: + r""" + @brief Like \with_length_both, but returning two edge pair collections + The first edge pair collection will contain all matching shapes, the other the non-matching ones. + Note that 'inverse' controls the way each edge is checked, not overall. + + This method has been introduced in version 0.29.12. + """ + ... def swap(self, other: EdgePairs) -> None: r""" @brief Swap the contents of this collection with the contents of another collection @@ -25741,7 +25944,7 @@ class Edges(ShapeCollection): This method has been introduced in version 0.28.4. """ ... - def filtered(self, filtered: EdgeFilter) -> Edges: + def filtered(self, filter: EdgeFilter) -> Edges: r""" @brief Applies a generic filter and returns a filtered copy See \EdgeFilter for a description of this feature. @@ -26678,6 +26881,14 @@ class Edges(ShapeCollection): 'zero_distance_mode' has been added in version 0.29. """ ... + def split_filter(self, filter: EdgeFilter) -> List[Edges]: + r""" + @brief Applies a generic filter and returns a copy with all matching shapes and one with the non-matching ones + See \EdgeFilter for a description of this feature. + + This method has been introduced in version 0.29.12. + """ + ... @overload def split_inside(self, other: Edges) -> List[Edges]: r""" @@ -26740,6 +26951,69 @@ class Edges(ShapeCollection): This method provides a faster way to compute both outside and non-outside edges compared to using separate methods. It has been introduced in version 0.28. """ ... + @overload + def split_with_abs_angle(self, angle: float) -> List[Edges]: + r""" + @brief Like \with_abs_angle, but returning two edge collections + The first edge collection will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_abs_angle(self, min_angle: float, max_angle: float, include_min_angle: Optional[bool] = ..., include_max_angle: Optional[bool] = ...) -> List[Edges]: + r""" + @brief Like \with_abs_angle, but returning two edge collections + The first edge collection will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_angle(self, angle: float) -> List[Edges]: + r""" + @brief Like \with_angle, but returning two edge collections + The first edge collection will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_angle(self, min_angle: float, max_angle: float, include_min_angle: Optional[bool] = ..., include_max_angle: Optional[bool] = ...) -> List[Edges]: + r""" + @brief Like \with_angle, but returning two edge collections + The first edge collection will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_angle(self, type: Edges.EdgeType) -> List[Edges]: + r""" + @brief Like \with_angle, but returning two edge collections + The first edge collection will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_length(self, length: int) -> List[Edges]: + r""" + @brief Like \with_length, but returning two edge collections + The first edge collection will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_length(self, min_length: Any, max_length: Any) -> List[Edges]: + r""" + @brief Like \with_length, but returning two edge collections + The first edge collection will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... def start_segments(self, length: int, fraction: float) -> Edges: r""" @brief Returns edges representing a part of the edge after the start point @@ -30301,10 +30575,10 @@ class Instance: Getter: @brief Gets the basic \CellInstArray object associated with this instance reference. Setter: - @brief Changes the \CellInstArray object to the given one. - This method replaces the instance by the given CellInstArray object. + @brief Returns the basic cell instance array object by giving a micrometer unit object. + This method replaces the instance by the given CellInstArray object and it internally transformed into database units. - This method has been introduced in version 0.22 + This method has been introduced in version 0.25 """ cplx_trans: ICplxTrans r""" @@ -30312,9 +30586,10 @@ class Instance: @brief Gets the complex transformation of the instance or the first instance in the array This method is always valid compared to \trans, since simple transformations can be expressed as complex transformations as well. Setter: - @brief Sets the complex transformation of the instance or the first instance in the array + @brief Sets the complex transformation of the instance or the first instance in the array (in micrometer units) + This method sets the transformation the same way as \cplx_trans=, but the displacement of this transformation is given in micrometer units. It is internally translated into database units. - This method has been introduced in version 0.23. + This method has been introduced in version 0.25. """ da: DVector r""" @@ -30443,10 +30718,9 @@ class Instance: @brief Gets the transformation of the instance or the first instance in the array The transformation returned is only valid if the array does not represent a complex transformation array Setter: - @brief Sets the transformation of the instance or the first instance in the array (in micrometer units) - This method sets the transformation the same way as \cplx_trans=, but the displacement of this transformation is given in micrometer units. It is internally translated into database units. + @brief Sets the transformation of the instance or the first instance in the array - This method has been introduced in version 0.25. + This method has been introduced in version 0.23. """ @classmethod def new(cls) -> Instance: @@ -41403,15 +41677,15 @@ class NetPinRef: @overload def net(self) -> Net: r""" - @brief Gets the net this pin reference is attached to (non-const version). - - This constness variant has been introduced in version 0.26.8 + @brief Gets the net this pin reference is attached to. """ ... @overload def net(self) -> Net: r""" - @brief Gets the net this pin reference is attached to. + @brief Gets the net this pin reference is attached to (non-const version). + + This constness variant has been introduced in version 0.26.8 """ ... def pin(self) -> Pin: @@ -41701,17 +41975,17 @@ class NetTerminalRef: @overload def device(self) -> Device: r""" - @brief Gets the device reference. + @brief Gets the device reference (non-const version). Gets the device object that this connection is made to. + + This constness variant has been introduced in version 0.26.8 """ ... @overload def device(self) -> Device: r""" - @brief Gets the device reference (non-const version). + @brief Gets the device reference. Gets the device object that this connection is made to. - - This constness variant has been introduced in version 0.26.8 """ ... def device_class(self) -> DeviceClass: @@ -53329,7 +53603,7 @@ class Region(ShapeCollection): This method has been introduced in version 0.28.4. """ ... - def filtered(self, filtered: PolygonFilter) -> Region: + def filtered(self, filter: PolygonFilter) -> Region: r""" @brief Applies a generic filter and returns a filtered copy See \PolygonFilter for a description of this feature. @@ -55005,6 +55279,14 @@ class Region(ShapeCollection): This method has been introduced in version 0.27. """ ... + def split_filter(self, filter: PolygonFilter) -> List[Region]: + r""" + @brief Applies a generic filter and returns a copy with all matching shapes and one with the non-matching ones + See \PolygonFilter for a description of this feature. + + This method has been introduced in version 0.29.12. + """ + ... def split_inside(self, other: Region) -> List[Region]: r""" @brief Returns the polygons of this region which are completely inside polygons from the other region and the ones which are not at the same time @@ -55080,6 +55362,210 @@ class Region(ShapeCollection): This method has been introduced in version 0.27. """ ... + def split_rectangles(self) -> List[Region]: + r""" + @brief Combined results of \rectangles and \non_rectangles + This method returns a list with two Regions, the first is the result of \rectangles, the second the result of \non_rectangles. Using this method is faster when you need both. + + This method has been introduced in version 0.29.12. + """ + ... + def split_rectilinear(self) -> List[Region]: + r""" + @brief Combined results of \rectilinear and \non_rectilinear + This method returns a list with two Regions, the first is the result of \rectilinear, the second the result of \non_rectilinear. Using this method is faster when you need both. + + This method has been introduced in version 0.29.12. + """ + ... + def split_squares(self) -> List[Region]: + r""" + @brief Combined results of \squares and \non_squares + This method returns a list with two Regions, the first is the result of \squares, the second the result of \non_squares. Using this method is faster when you need both. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_area(self, area: int) -> List[Region]: + r""" + @brief Like \with_area, but returning two regions + The first region will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_area(self, min_area: Any, max_area: Any) -> List[Region]: + r""" + @brief Like \with_area, but returning two regions + The first region will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_area_ratio(self, min_ratio: Any, max_ratio: Any, min_included: Optional[bool] = ..., max_included: Optional[bool] = ...) -> List[Region]: + r""" + @brief Like \with_area_ratio, but returning two regions + The first region will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_area_ratio(self, ratio: float) -> List[Region]: + r""" + @brief Like \with_area_ratio, but returning two regions + The first region will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_bbox_aspect_ratio(self, min_ratio: Any, max_ratio: Any, min_included: Optional[bool] = ..., max_included: Optional[bool] = ...) -> List[Region]: + r""" + @brief Like \with_bbox_aspect_ratio, but returning two regions + The first region will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_bbox_aspect_ratio(self, ratio: float) -> List[Region]: + r""" + @brief Like \with_bbox_aspect_ratio, but returning two regions + The first region will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_bbox_height(self, height: int) -> List[Region]: + r""" + @brief Like \with_bbox_height, but returning two regions + The first region will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_bbox_height(self, min_height: Any, max_height: Any) -> List[Region]: + r""" + @brief Like \with_bbox_height, but returning two regions + The first region will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_bbox_max(self, dim: int) -> List[Region]: + r""" + @brief Like \with_bbox_max, but returning two regions + The first region will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_bbox_max(self, min_dim: Any, max_dim: Any) -> List[Region]: + r""" + @brief Like \with_bbox_max, but returning two regions + The first region will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_bbox_min(self, dim: int) -> List[Region]: + r""" + @brief Like \with_bbox_min, but returning two regions + The first region will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_bbox_min(self, min_dim: Any, max_dim: Any) -> List[Region]: + r""" + @brief Like \with_bbox_min, but returning two regions + The first region will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_bbox_width(self, min_width: Any, max_width: Any) -> List[Region]: + r""" + @brief Like \with_bbox_width, but returning two regions + The first region will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_bbox_width(self, width: int) -> List[Region]: + r""" + @brief Like \with_bbox_width, but returning two regions + The first region will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_holes(self, min_nholes: Any, max_nholes: Any) -> List[Region]: + r""" + @brief Like \with_holes, but returning two regions + The first region will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_holes(self, nholes: int) -> List[Region]: + r""" + @brief Like \with_holes, but returning two regions + The first region will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_perimeter(self, min_perimeter: Any, max_perimeter: Any) -> List[Region]: + r""" + @brief Like \with_perimeter, but returning two regions + The first region will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_perimeter(self, perimeter: int) -> List[Region]: + r""" + @brief Like \with_perimeter, but returning two regions + The first region will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_relative_height(self, min_ratio: Any, max_ratio: Any, min_included: Optional[bool] = ..., max_included: Optional[bool] = ...) -> List[Region]: + r""" + @brief Like \with_relative_height, but returning two regions + The first region will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + @overload + def split_with_relative_height(self, ratio: float) -> List[Region]: + r""" + @brief Like \with_relative_height, but returning two regions + The first region will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... def squares(self) -> Region: r""" @brief Returns all polygons which are squares @@ -55487,7 +55973,7 @@ class Region(ShapeCollection): """ ... @overload - def with_holes(self, min_bholes: Any, max_nholes: Any, inverse: bool) -> Region: + def with_holes(self, min_nholes: Any, max_nholes: Any, inverse: bool) -> Region: r""" @brief Filter the polygons by their number of holes Filters the polygons of the region by number of holes. If "inverse" is false, only polygons which have a hole count larger or equal to "min_nholes" and less than "max_nholes" are returned. If "inverse" is true, polygons having a hole count less than "min_nholes" or larger or equal than "max_nholes" are returned. @@ -56911,11 +57397,10 @@ class Shape: Starting with version 0.23, this method returns nil, if the shape does not represent an edge. Setter: - @brief Replaces the shape by the given edge - This method replaces the shape by the given edge. This method can only be called for editable layouts. It does not change the user properties of the shape. - Calling this method will invalidate any iterators. It should not be called inside a loop iterating over shapes. + @brief Replaces the shape by the given edge (in micrometer units) + This method replaces the shape by the given edge, like \edge= with a \Edge argument does. This version translates the edge from micrometer units to database units internally. - This method has been introduced in version 0.22. + This method has been introduced in version 0.25. """ edge_pair: Any r""" @@ -56964,11 +57449,10 @@ class Shape: Starting with version 0.23, this method returns nil, if the shape does not represent a path. Setter: - @brief Replaces the shape by the given path object - This method replaces the shape by the given path object. This method can only be called for editable layouts. It does not change the user properties of the shape. - Calling this method will invalidate any iterators. It should not be called inside a loop iterating over shapes. + @brief Replaces the shape by the given path (in micrometer units) + This method replaces the shape by the given path, like \path= with a \Path argument does. This version translates the path from micrometer units to database units internally. - This method has been introduced in version 0.22. + This method has been introduced in version 0.25. """ path_bgnext: int r""" @@ -60872,15 +61356,6 @@ class SubCircuit(NetlistObject): """ ... @overload - def circuit(self) -> Circuit: - r""" - @brief Gets the circuit the subcircuit lives in (non-const version). - This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \circuit_ref. - - This constness variant has been introduced in version 0.26.8 - """ - ... - @overload def circuit(self) -> Circuit: r""" @brief Gets the circuit the subcircuit lives in. @@ -60888,9 +61363,12 @@ class SubCircuit(NetlistObject): """ ... @overload - def circuit_ref(self) -> Circuit: + def circuit(self) -> Circuit: r""" - @brief Gets the circuit referenced by the subcircuit. + @brief Gets the circuit the subcircuit lives in (non-const version). + This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \circuit_ref. + + This constness variant has been introduced in version 0.26.8 """ ... @overload @@ -60903,6 +61381,12 @@ class SubCircuit(NetlistObject): """ ... @overload + def circuit_ref(self) -> Circuit: + r""" + @brief Gets the circuit referenced by the subcircuit. + """ + ... + @overload def connect_pin(self, pin: Pin, net: Net) -> None: r""" @brief Connects the given pin to the specified net. @@ -61569,7 +62053,8 @@ class Text: Setter: @brief Sets the horizontal alignment - This is the version accepting integer values. It's provided for backward compatibility. + This property specifies how the text is aligned relative to the anchor point. + This property has been introduced in version 0.22 and extended to enums in 0.28. """ size: int r""" @@ -63548,6 +64033,30 @@ class Texts(ShapeCollection): Starting with version 0.27, the method is called 'count' for consistency with \Region. 'size' is still provided as an alias. """ ... + def split_filter(self, filter: TextFilter) -> List[Texts]: + r""" + @brief Applies a generic filter and returns a copy with all matching shapes and one with the non-matching ones + See \TextFilter for a description of this feature. + + This method has been introduced in version 0.29.12. + """ + ... + def split_with_match(self, pattern: str) -> List[Texts]: + r""" + @brief Like \with_match, but returning two text collections + The first text collection will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... + def split_with_text(self, text: str) -> List[Texts]: + r""" + @brief Like \with_text, but returning two text collections + The first text collection will contain all matching shapes, the other the non-matching ones. + + This method has been introduced in version 0.29.12. + """ + ... def swap(self, other: Texts) -> None: r""" @brief Swap the contents of this collection with the contents of another collection From e06498763a5265f4b328afb7685d41c0733cc7da Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 5 Mar 2025 19:12:38 +0100 Subject: [PATCH 052/392] Fixing issue #1997. --- Changelog | 1 + src/tl/tl/tlString.cc | 3 ++- src/tl/unit_tests/tlStringTests.cc | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Changelog b/Changelog index 7af49841b..fdab73e4d 100644 --- a/Changelog +++ b/Changelog @@ -5,6 +5,7 @@ on Edges, Region, EdgePairs and Texts, delivering a pair of containers with selected and unselected objects. * Bug: %GITHUB%/issues/1993 Tiling processor kept layout locks, causing DRC issues with "with_density" +* Bug: %GITHUB%/issues/1997 Can not find a file in Open Recent menu (a string unescaping bug) * Bugfix: 'Save All' was not updating the dirty flag in the inactive tabs * Bugfix: Fixing a crash when editing PCell parameters while the macro editor is open * Bugfix: Fixed a potential Crash on "save all" diff --git a/src/tl/tl/tlString.cc b/src/tl/tl/tlString.cc index 28e597464..d57dee236 100644 --- a/src/tl/tl/tlString.cc +++ b/src/tl/tl/tlString.cc @@ -702,7 +702,8 @@ inline char unescape_char (const char * &cp) { if (safe_isdigit (*cp)) { int c = 0; - while (*cp && safe_isdigit (*cp)) { + unsigned int n = 0; + while (*cp && safe_isdigit (*cp) && n++ < 3) { c = c * 8 + int (*cp - '0'); ++cp; } diff --git a/src/tl/unit_tests/tlStringTests.cc b/src/tl/unit_tests/tlStringTests.cc index 3697ed4f1..634b38e6e 100644 --- a/src/tl/unit_tests/tlStringTests.cc +++ b/src/tl/unit_tests/tlStringTests.cc @@ -504,6 +504,7 @@ TEST(10) EXPECT_EQ (escape_string ("'a\n\003"), "'a\\n\\003"); EXPECT_EQ (escape_string ("'a\n\003"), "'a\\n\\003"); EXPECT_EQ (unescape_string (escape_string ("'a\n\003")), "'a\n\003"); + EXPECT_EQ (unescape_string (escape_string ("'a\n\0031")), "'a\n\0031"); } TEST(11) From 903bd7022381cde32aabec236cfcce0abb495510 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 6 Mar 2025 21:09:11 +0100 Subject: [PATCH 053/392] Fixed merge issues --- src/db/db/dbAsIfFlatEdgePairs.cc | 5 ++--- src/db/db/dbAsIfFlatEdges.cc | 2 +- src/db/db/dbAsIfFlatRegion.cc | 2 +- src/db/db/dbAsIfFlatTexts.cc | 2 +- src/db/db/dbDeepRegion.cc | 2 +- src/db/unit_tests/dbLayoutQueryTests.cc | 4 ++-- testdata/algo/dss_bug_au.gds | Bin 1208 -> 996 bytes 7 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/db/db/dbAsIfFlatEdgePairs.cc b/src/db/db/dbAsIfFlatEdgePairs.cc index aa4037f18..29dc42390 100644 --- a/src/db/db/dbAsIfFlatEdgePairs.cc +++ b/src/db/db/dbAsIfFlatEdgePairs.cc @@ -256,11 +256,10 @@ AsIfFlatEdgePairs::filtered_pair (const EdgePairFilterBase &filter) const { std::unique_ptr new_edge_pairs_true (new FlatEdgePairs ()); std::unique_ptr new_edge_pairs_false (new FlatEdgePairs ()); - db::PropertyMapper pm (new_edge_pairs_true->properties_repository (), properties_repository ()); for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { - FlatEdgePairs *dest = filter.selected (*p) ? new_edge_pairs_true.get () : new_edge_pairs_false.get (); - insert_ep (dest, *p, pm (p.prop_id ())); + FlatEdgePairs *dest = filter.selected (*p, p.prop_id ()) ? new_edge_pairs_true.get () : new_edge_pairs_false.get (); + insert_ep (dest, *p, p.prop_id ()); } return std::make_pair (new_edge_pairs_true.release (), new_edge_pairs_false.release ()); diff --git a/src/db/db/dbAsIfFlatEdges.cc b/src/db/db/dbAsIfFlatEdges.cc index 8bb36fce2..6ab1eac43 100644 --- a/src/db/db/dbAsIfFlatEdges.cc +++ b/src/db/db/dbAsIfFlatEdges.cc @@ -730,7 +730,7 @@ AsIfFlatEdges::filtered_pair (const EdgeFilterBase &filter) const std::unique_ptr new_region_false (new FlatEdges ()); for (EdgesIterator p (begin_merged ()); ! p.at_end (); ++p) { - if (filter.selected (*p)) { + if (filter.selected (*p, p.prop_id ())) { new_region_true->insert (*p); } else { new_region_false->insert (*p); diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index a3e0dcfb6..f05e76fcd 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -440,7 +440,7 @@ AsIfFlatRegion::filtered_pair (const PolygonFilterBase &filter) const std::unique_ptr new_region_false (new FlatRegion ()); for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { - if (filter.selected (*p)) { + if (filter.selected (*p, p.prop_id ())) { new_region_true->insert (*p); } else { new_region_false->insert (*p); diff --git a/src/db/db/dbAsIfFlatTexts.cc b/src/db/db/dbAsIfFlatTexts.cc index ba2d0bdf5..6b7f887e9 100644 --- a/src/db/db/dbAsIfFlatTexts.cc +++ b/src/db/db/dbAsIfFlatTexts.cc @@ -178,7 +178,7 @@ AsIfFlatTexts::filtered_pair (const TextFilterBase &filter) const std::unique_ptr new_texts_false (new FlatTexts ()); for (TextsIterator p (begin ()); ! p.at_end (); ++p) { - if (filter.selected (*p)) { + if (filter.selected (*p, p.prop_id ())) { new_texts_true->insert (*p); } else { new_texts_false->insert (*p); diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 75f3732d4..3bce3d9f0 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1660,7 +1660,7 @@ DeepRegion::apply_filter (const PolygonFilterBase &filter, bool with_true, bool 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 (*v), si->prop_id ())) { + if (filter.selected (poly.transformed (tr), si->prop_id ())) { if (st_true) { st_true->insert (*si); } diff --git a/src/db/unit_tests/dbLayoutQueryTests.cc b/src/db/unit_tests/dbLayoutQueryTests.cc index 9ecc708d9..79b059a57 100644 --- a/src/db/unit_tests/dbLayoutQueryTests.cc +++ b/src/db/unit_tests/dbLayoutQueryTests.cc @@ -1596,7 +1596,7 @@ TEST(65) } } -TEST(65) +TEST(66) { db::Layout g; init_layout (g); @@ -1609,6 +1609,6 @@ TEST(65) s = q2s_var (iq, "cell_name"); EXPECT_EQ (s, "c1,c5x"); s = q2s_var (iq, "inst_elements"); - EXPECT_EQ (s, "cell_index=0 r0 *1 10,-20,cell_index=4 r0 *1 10,-20"); + EXPECT_EQ (s, "(cell_index=0 r0 *1 10,-20),(cell_index=4 r0 *1 10,-20)"); } } diff --git a/testdata/algo/dss_bug_au.gds b/testdata/algo/dss_bug_au.gds index 528f82c7c5564b45bfe8e88384e1c65edf29cb0b..c8a1f3215941734fbf49ed72cc21a23a04024f9d 100644 GIT binary patch delta 277 zcmdnN`Gj4GfsKKQDS|(8CIOOGH`&+F$9?+iOUqz$xTf8IAzTy`!P?(=Jd&5n02v= Io3OM10J7gB1^@s6 delta 338 zcmaFDzJpVVfsKKQDS|3=A@?I32~n0k*~vVu>;?b4(dnxEUDOgn-sf jmSVD*{Dm4wQWGPHB1w+`FpYgvo2Qg1m+e1oEa`- From c0f2bdd3d6d025019b2b9abdc5fb8fdd1aeec25d Mon Sep 17 00:00:00 2001 From: Kazunari Sekigawa Date: Sat, 22 Feb 2025 10:09:17 +0900 Subject: [PATCH 054/392] Use Ruby 3.3.7 from MacPorts --- macbuild/build4mac_env.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macbuild/build4mac_env.py b/macbuild/build4mac_env.py index c1084a66b..3be1c22de 100755 --- a/macbuild/build4mac_env.py +++ b/macbuild/build4mac_env.py @@ -192,7 +192,7 @@ RubySequoia = { 'exe': '/System/Library/Frameworks/Ruby.framework/Versions # install with 'sudo port install ruby33' # [Key Type Name] = 'MP33' Ruby33MacPorts = { 'exe': '/opt/local/bin/ruby3.3', - 'inc': '/opt/local/include/ruby-3.3.6', + 'inc': '/opt/local/include/ruby-3.3.7', 'lib': '/opt/local/lib/libruby.3.3.dylib' } From 86202fa23c29c41a0014022eaf89a6442bd4a152 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 9 Mar 2025 19:22:04 +0100 Subject: [PATCH 055/392] WIP: refactoring of LayoutToNetlist for more consistent support of Texts for layers --- src/db/db/dbAsIfFlatRegion.cc | 12 +- src/db/db/dbDeepRegion.cc | 8 +- src/db/db/dbLayoutToNetlist.cc | 198 +++++++++--- src/db/db/dbLayoutToNetlist.h | 127 ++++++-- src/db/db/gsiDeclDbLayoutToNetlist.cc | 284 ++++++++++++++---- .../dbLayoutToNetlistReaderTests.cc | 186 ++++++------ src/db/unit_tests/dbLayoutToNetlistTests.cc | 126 ++++---- .../dbLayoutToNetlistWriterTests.cc | 44 +-- src/layui/layui/layNetlistBrowserPage.cc | 2 +- 9 files changed, 673 insertions(+), 314 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index f05e76fcd..339af0235 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -1879,7 +1879,7 @@ AsIfFlatRegion::add (const Region &other) const } static void -deliver_shapes_of_nets_recursive (db::Shapes &out, const db::Circuit *circuit, const LayoutToNetlist *l2n, const db::Region *of_layer, NetPropertyMode prop_mode, const tl::Variant &net_prop_name, const db::ICplxTrans &tr, const std::set *net_filter) +deliver_shapes_of_nets_recursive (db::Shapes &out, const db::Circuit *circuit, const LayoutToNetlist *l2n, unsigned int lid, NetPropertyMode prop_mode, const tl::Variant &net_prop_name, const db::ICplxTrans &tr, const std::set *net_filter) { db::CplxTrans dbu_trans (l2n->internal_layout ()->dbu ()); auto dbu_trans_inv = dbu_trans.inverted (); @@ -1888,14 +1888,14 @@ deliver_shapes_of_nets_recursive (db::Shapes &out, const db::Circuit *circuit, c if (! net_filter || net_filter->find (n.operator-> ()) != net_filter->end ()) { db::properties_id_type prop_id = db::NetBuilder::make_netname_propid (prop_mode, net_prop_name, *n); - l2n->shapes_of_net (*n, *of_layer, true, out, prop_id, tr); + l2n->shapes_of_net (*n, lid, true, out, prop_id, tr); } // dive into subcircuits for (auto sc = circuit->begin_subcircuits (); sc != circuit->end_subcircuits (); ++sc) { const db::Circuit *circuit_ref = sc->circuit_ref (); db::ICplxTrans tr_ref = tr * (dbu_trans_inv * sc->trans () * dbu_trans); - deliver_shapes_of_nets_recursive (out, circuit_ref, l2n, of_layer, prop_mode, net_prop_name, tr_ref, net_filter); + deliver_shapes_of_nets_recursive (out, circuit_ref, l2n, lid, prop_mode, net_prop_name, tr_ref, net_filter); } } @@ -1909,9 +1909,9 @@ AsIfFlatRegion::nets (LayoutToNetlist *l2n, NetPropertyMode prop_mode, const tl: } std::unique_ptr result (new db::FlatRegion ()); - std::unique_ptr region_for_layer (l2n->layer_by_original (this)); + tl::optional li = l2n->layer_by_original (this); - if (! region_for_layer) { + if (! li.has_value ()) { throw tl::Exception (tl::to_string (tr ("The given layer is not an original layer used in netlist extraction"))); } @@ -1927,7 +1927,7 @@ AsIfFlatRegion::nets (LayoutToNetlist *l2n, NetPropertyMode prop_mode, const tl: net_filter_set.insert (net_filter->begin (), net_filter->end ()); } - deliver_shapes_of_nets_recursive (result->raw_polygons (), top_circuit, l2n, region_for_layer.get (), prop_mode, net_prop_name, db::ICplxTrans (), net_filter ? &net_filter_set : 0); + deliver_shapes_of_nets_recursive (result->raw_polygons (), top_circuit, l2n, li.value (), prop_mode, net_prop_name, db::ICplxTrans (), net_filter ? &net_filter_set : 0); return result.release (); } diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 3bce3d9f0..a86e2ef4a 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -757,13 +757,13 @@ DeepRegion::nets (LayoutToNetlist *l2n, NetPropertyMode prop_mode, const tl::Var DeepLayer result = deep_layer ().derived (); - std::unique_ptr region_for_layer (l2n->layer_by_original (this)); - if (! region_for_layer) { + tl::optional li = l2n->layer_by_original (this); + if (! li.has_value ()) { throw tl::Exception (tl::to_string (tr ("The given layer is not an original layer used in netlist extraction"))); } - std::map lmap; - lmap.insert (std::make_pair (result.layer (), region_for_layer.get ())); + std::map lmap; + lmap.insert (std::make_pair (result.layer (), li.value ())); net_builder.build_nets (nets, lmap, prop_mode, net_prop_name); diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 802133935..3c5e06d0c 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -93,7 +93,7 @@ LayoutToNetlist::LayoutToNetlist () LayoutToNetlist::~LayoutToNetlist () { // NOTE: do this in this order because of unregistration of the layers - m_named_regions.clear (); + m_named_dls.clear (); m_dlrefs.clear (); mp_internal_dss.reset (0); mp_netlist.reset (0); @@ -175,6 +175,16 @@ db::Region *LayoutToNetlist::make_layer (unsigned int layer_index, const std::st return region.release (); } +db::Texts *LayoutToNetlist::make_text_layer (const std::string &n) +{ + db::RecursiveShapeIterator si (m_iter); + si.shape_flags (db::ShapeIterator::Nothing); + + std::unique_ptr texts (new db::Texts (si, dss ())); + register_layer (*texts, n); + return texts.release (); +} + db::Texts *LayoutToNetlist::make_text_layer (unsigned int layer_index, const std::string &n) { db::RecursiveShapeIterator si (m_iter); @@ -190,7 +200,7 @@ db::Region *LayoutToNetlist::make_polygon_layer (unsigned int layer_index, const { db::RecursiveShapeIterator si (m_iter); si.set_layer (layer_index); - si.shape_flags (db::ShapeIterator::Paths | db::ShapeIterator::Polygons | db::ShapeIterator::Boxes); + si.shape_flags (db::ShapeIterator::Regions); std::unique_ptr region (new db::Region (si, dss ())); register_layer (*region, n); @@ -850,10 +860,10 @@ void LayoutToNetlist::mem_stat (MemStatistics *stat, MemStatistics::purpose_t pu db::mem_stat (stat, purpose, cat, m_net_clusters, true, (void *) this); db::mem_stat (stat, purpose, cat, mp_netlist, true, (void *) this); db::mem_stat (stat, purpose, cat, m_dlrefs, true, (void *) this); - db::mem_stat (stat, purpose, cat, m_named_regions, true, (void *) this); + db::mem_stat (stat, purpose, cat, m_named_dls, true, (void *) this); db::mem_stat (stat, purpose, cat, m_name_of_layer, true, (void *) this); - db::mem_stat (stat, purpose, cat, m_region_by_original, true, (void *) this); - db::mem_stat (stat, purpose, cat, m_region_of_layer, true, (void *) this); + db::mem_stat (stat, purpose, cat, m_dl_by_original, true, (void *) this); + db::mem_stat (stat, purpose, cat, m_dl_of_layer, true, (void *) this); db::mem_stat (stat, purpose, cat, m_joined_net_names, true, (void *) this); db::mem_stat (stat, purpose, cat, m_joined_net_names_per_cell, true, (void *) this); db::mem_stat (stat, purpose, cat, m_joined_nets, true, (void *) this); @@ -899,6 +909,26 @@ db::Cell *LayoutToNetlist::internal_top_cell () return &dss ().initial_cell (m_layout_index); } +const db::Layout *LayoutToNetlist::original_layout () const +{ + return m_iter.layout (); +} + +const db::Cell *LayoutToNetlist::original_top_cell () const +{ + return m_iter.top_cell (); +} + +db::Layout *LayoutToNetlist::original_layout () +{ + return const_cast (m_iter.layout ()); +} + +db::Cell *LayoutToNetlist::original_top_cell () +{ + return const_cast (m_iter.top_cell ()); +} + void LayoutToNetlist::ensure_layout () const { if (! dss ().is_valid_layout_index (m_layout_index)) { @@ -927,7 +957,7 @@ std::string LayoutToNetlist::make_new_name (const std::string &stem) name += std::string ("$"); name += tl::to_string (n - m); - if (m_named_regions.find (name) == m_named_regions.end ()) { + if (m_named_dls.find (name) == m_named_dls.end ()) { n -= m; } @@ -948,39 +978,69 @@ std::string LayoutToNetlist::name (unsigned int l) const 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 ()) { + std::map::const_iterator l = m_named_dls.find (name); + if (l == m_named_dls.end ()) { return 0; } else { return new db::Region (new db::DeepRegion (l->second)); } } +db::Texts *LayoutToNetlist::texts_by_name (const std::string &name) +{ + std::map::const_iterator l = m_named_dls.find (name); + if (l == m_named_dls.end ()) { + return 0; + } else { + return new db::Texts (new db::DeepTexts (l->second)); + } +} + +tl::optional LayoutToNetlist::layer_index_by_name (const std::string &name) const +{ + std::map::const_iterator l = m_named_dls.find (name); + if (l == m_named_dls.end ()) { + return tl::optional (); + } else { + return tl::optional (l->second.layer ()); + } +} + db::Region *LayoutToNetlist::layer_by_index (unsigned int index) { - auto n = m_region_of_layer.find (index); - if (n == m_region_of_layer.end ()) { + auto n = m_dl_of_layer.find (index); + if (n == m_dl_of_layer.end ()) { return 0; } else { return new db::Region (new db::DeepRegion (n->second)); } } -db::Region *LayoutToNetlist::layer_by_original (const ShapeCollectionDelegateBase *original_delegate) +db::Texts *LayoutToNetlist::texts_by_index (unsigned int index) { - auto n = m_region_by_original.find (tl::id_of (original_delegate)); - if (n == m_region_by_original.end ()) { + auto n = m_dl_of_layer.find (index); + if (n == m_dl_of_layer.end ()) { + return 0; + } else { + return new db::Texts (new db::DeepTexts (n->second)); + } +} + +tl::optional LayoutToNetlist::layer_by_original (const ShapeCollectionDelegateBase *original_delegate) +{ + auto n = m_dl_by_original.find (tl::id_of (original_delegate)); + if (n == m_dl_by_original.end ()) { DeepShapeCollectionDelegateBase *dl = const_cast (original_delegate)->deep (); if (dl && dl->deep_layer ().store () == mp_dss.get ()) { // implicitly original because the collection is inside our DSS - return new db::Region (new db::DeepRegion (dl->deep_layer ())); + return tl::optional (dl->deep_layer ().layer ()); } else { - return 0; + return tl::optional (); } } else { - return new db::Region (new db::DeepRegion (n->second)); + return tl::optional (n->second.layer ()); } } @@ -1009,11 +1069,11 @@ std::string LayoutToNetlist::name (const ShapeCollection &coll) const unsigned int LayoutToNetlist::register_layer (const ShapeCollection &collection, const std::string &n_in) { - if (m_region_by_original.find (tl::id_of (collection.get_delegate ())) != m_region_by_original.end ()) { + if (m_dl_by_original.find (tl::id_of (collection.get_delegate ())) != m_dl_by_original.end ()) { throw tl::Exception (tl::to_string (tr ("The layer is already registered"))); } - if (! n_in.empty () && m_named_regions.find (n_in) != m_named_regions.end ()) { + if (! n_in.empty () && m_named_dls.find (n_in) != m_named_dls.end ()) { throw tl::Exception (tl::to_string (tr ("Layer name is already used: ")) + n_in); } @@ -1043,9 +1103,9 @@ unsigned int LayoutToNetlist::register_layer (const ShapeCollection &collection, unsigned int layer = dl.layer (); - m_region_by_original [tl::id_of (collection.get_delegate ())] = dl; - m_region_of_layer [layer] = dl; - m_named_regions [n] = dl; + m_dl_by_original [tl::id_of (collection.get_delegate ())] = dl; + m_dl_of_layer [layer] = dl; + m_named_dls [n] = dl; m_name_of_layer [layer] = n; return layer; @@ -1075,7 +1135,7 @@ bool LayoutToNetlist::is_persisted_impl (const db::ShapeCollection &coll) const return true; } else { // explicitly persisted through "register" - return m_region_by_original.find (tl::id_of (coll.get_delegate ())) != m_region_by_original.end (); + return m_dl_by_original.find (tl::id_of (coll.get_delegate ())) != m_dl_by_original.end (); } } @@ -1126,10 +1186,10 @@ db::CellMapping LayoutToNetlist::const_cell_mapping_into (const db::Layout &layo return cm; } -std::map +std::map LayoutToNetlist::create_layermap (db::Layout &target_layout, int ln) const { - std::map lm; + std::map lm; if (! internal_layout ()) { return lm; } @@ -1144,13 +1204,13 @@ LayoutToNetlist::create_layermap (db::Layout &target_layout, int ln) const for (std::set::const_iterator l = layers_to_copy.begin (); l != layers_to_copy.end (); ++l) { const db::LayerProperties &lp = source_layout.get_properties (*l); - unsigned int tl; if (! lp.is_null ()) { - tl = target_layout.insert_layer (lp); - } else { - tl = target_layout.insert_layer (db::LayerProperties (ln++, 0, name (*l))); + unsigned int tl = target_layout.insert_layer (lp); + lm.insert (std::make_pair (tl, *l)); + } else if (ln >= 0) { + unsigned int tl = target_layout.insert_layer (db::LayerProperties (ln++, 0, name (*l))); + lm.insert (std::make_pair (tl, *l)); } - lm.insert (std::make_pair (tl, const_cast (this)->layer_by_index (*l))); } return lm; @@ -1179,16 +1239,43 @@ static bool deliver_shape (const db::NetShape &, StopOnFirst, const Tr &, db::pr } template -static bool deliver_shape (const db::NetShape &s, db::Region ®ion, const Tr &tr, db::properties_id_type /*propid*/) +static bool deliver_shape (const db::NetShape &s, db::Region ®ion, const Tr &tr, db::properties_id_type propid) { if (s.type () == db::NetShape::Polygon) { db::PolygonRef pr = s.polygon_ref (); if (pr.obj ().is_box ()) { - region.insert (pr.obj ().box ().transformed (pr.trans ()).transformed (tr)); + if (propid) { + region.insert (db::BoxWithProperties (pr.obj ().box ().transformed (pr.trans ()).transformed (tr), propid)); + } else { + region.insert (pr.obj ().box ().transformed (pr.trans ()).transformed (tr)); + } } else { - region.insert (pr.obj ().transformed (pr.trans ()).transformed (tr)); + if (propid) { + region.insert (db::PolygonWithProperties (pr.obj ().transformed (pr.trans ()).transformed (tr), propid)); + } else { + region.insert (pr.obj ().transformed (pr.trans ()).transformed (tr)); + } + } + + } + + return true; +} + +template +static bool deliver_shape (const db::NetShape &s, db::Texts &texts, const Tr &tr, db::properties_id_type propid) +{ + if (s.type () == db::NetShape::Text) { + + db::TextRef pr = s.text_ref (); + + db::Text text (pr.obj ().transformed (pr.trans ()).transformed (tr)); + if (propid) { + texts.insert (db::TextWithProperties (text, propid)); + } else { + texts.insert (text); } } @@ -1407,9 +1494,14 @@ LayoutToNetlist::shapes_of_terminal (const db::NetTerminalRef &terminal, const d return result; } -void LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to, db::properties_id_type propid, const ICplxTrans &trans) const +void LayoutToNetlist::shapes_of_net (const db::Net &net, const db::ShapeCollection &of_layer, bool recursive, db::Shapes &to, db::properties_id_type propid, const ICplxTrans &trans) const { unsigned int lid = layer_of (of_layer); + shapes_of_net (net, lid, recursive, to, propid, trans); +} + +void LayoutToNetlist::shapes_of_net (const db::Net &net, unsigned int lid, bool recursive, db::Shapes &to, db::properties_id_type propid, const ICplxTrans &trans) const +{ const db::Circuit *circuit = net.circuit (); tl_assert (circuit != 0); @@ -1419,14 +1511,14 @@ void LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_la deliver_shapes_of_net (recursive, mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lmap, trans, propid); } -db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, const db::ICplxTrans &trans) const +template +Coll *LayoutToNetlist::shapes_of_net_with_layer_index (const db::Net &net, unsigned int lid, bool recursive, const db::ICplxTrans &trans) const { - unsigned int lid = layer_of (of_layer); const db::Circuit *circuit = net.circuit (); tl_assert (circuit != 0); - std::unique_ptr res (new db::Region ()); - std::map lmap; + std::unique_ptr res (new Coll ()); + std::map lmap; lmap [lid] = res.get (); deliver_shapes_of_net (recursive, mp_netlist.get (), m_net_clusters, circuit->cell_index (), net.cluster_id (), lmap, trans, 0); @@ -1434,8 +1526,14 @@ db::Region *LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region return res.release (); } +// explicit instantiations +template +DB_PUBLIC db::Region *LayoutToNetlist::shapes_of_net_with_layer_index (const db::Net &net, unsigned int lid, bool recursive, const db::ICplxTrans &trans) const; +template +DB_PUBLIC db::Texts *LayoutToNetlist::shapes_of_net_with_layer_index (const db::Net &net, unsigned int lid, bool recursive, const db::ICplxTrans &trans) const; + void -LayoutToNetlist::build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, NetPropertyMode net_prop_mode, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix) const +LayoutToNetlist::build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, NetPropertyMode net_prop_mode, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix) const { NetBuilder builder (&target, this); builder.set_hier_mode (hier_mode); @@ -1446,7 +1544,7 @@ LayoutToNetlist::build_net (const db::Net &net, db::Layout &target, db::Cell &ta } void -LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, NetPropertyMode net_prop_mode, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const +LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, NetPropertyMode net_prop_mode, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const { NetBuilder builder (&target, cmap, this); builder.set_hier_mode (hier_mode); @@ -1458,7 +1556,7 @@ LayoutToNetlist::build_all_nets (const db::CellMapping &cmap, db::Layout &target } void -LayoutToNetlist::build_nets (const std::vector *nets, const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, NetPropertyMode net_prop_mode, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const +LayoutToNetlist::build_nets (const std::vector *nets, const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, NetPropertyMode net_prop_mode, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const { NetBuilder builder (&target, cmap, this); builder.set_hier_mode (hier_mode); @@ -2152,7 +2250,7 @@ NetBuilder::prepare_build_nets () const } void -NetBuilder::build_net (db::Cell &target_cell, const db::Net &net, const std::map &lmap, NetPropertyMode net_prop_mode, const tl::Variant &netname_prop) const +NetBuilder::build_net (db::Cell &target_cell, const db::Net &net, const std::map &lmap, NetPropertyMode net_prop_mode, const tl::Variant &netname_prop) const { prepare_build_nets (); @@ -2163,13 +2261,13 @@ NetBuilder::build_net (db::Cell &target_cell, const db::Net &net, const std::map } void -NetBuilder::build_all_nets (const std::map &lmap, NetPropertyMode net_prop_mode, const tl::Variant &netname_prop) const +NetBuilder::build_all_nets (const std::map &lmap, NetPropertyMode net_prop_mode, const tl::Variant &netname_prop) const { build_nets (0, lmap, net_prop_mode, netname_prop); } void -NetBuilder::build_nets (const std::vector *nets, const std::map &lmap, NetPropertyMode prop_mode, const tl::Variant &netname_prop) const +NetBuilder::build_nets (const std::vector *nets, const std::map &lmap, NetPropertyMode prop_mode, const tl::Variant &netname_prop) const { prepare_build_nets (); @@ -2239,7 +2337,7 @@ NetBuilder::build_nets (const std::vector *nets, const std::map &lmap, const std::string &add_net_cell_name_prefix, db::properties_id_type netname_propid, const db::ICplxTrans &tr) const +NetBuilder::build_net_rec (const db::Net &net, db::Cell &target_cell, const std::map &lmap, const std::string &add_net_cell_name_prefix, db::properties_id_type netname_propid, const db::ICplxTrans &tr) const { const db::Circuit *circuit = net.circuit (); tl_assert (circuit != 0); @@ -2248,7 +2346,7 @@ NetBuilder::build_net_rec (const db::Net &net, db::Cell &target_cell, const std: } void -NetBuilder::build_net_rec (db::cell_index_type ci, size_t cid, db::Cell &tc, const std::map &lmap, const db::Net *net, const std::string &add_net_cell_name_prefix, db::properties_id_type netname_propid, const db::ICplxTrans &tr) const +NetBuilder::build_net_rec (db::cell_index_type ci, size_t cid, db::Cell &tc, const std::map &lmap, const db::Net *net, const std::string &add_net_cell_name_prefix, db::properties_id_type netname_propid, const db::ICplxTrans &tr) const { db::Cell *target_cell = &tc; @@ -2261,9 +2359,9 @@ NetBuilder::build_net_rec (db::cell_index_type ci, size_t cid, db::Cell &tc, con StopOnFirst sof; std::map sof_lmap; - for (std::map::const_iterator l = lmap.begin (); l != lmap.end (); ++l) { + for (std::map::const_iterator l = lmap.begin (); l != lmap.end (); ++l) { if (l->second) { - sof_lmap.insert (std::make_pair (mp_source->layer_of (*l->second), &sof)); + sof_lmap.insert (std::make_pair (l->second, &sof)); } } @@ -2283,9 +2381,9 @@ NetBuilder::build_net_rec (db::cell_index_type ci, size_t cid, db::Cell &tc, con } std::map target_lmap; - for (std::map::const_iterator l = lmap.begin (); l != lmap.end (); ++l) { + for (std::map::const_iterator l = lmap.begin (); l != lmap.end (); ++l) { if (l->second) { - target_lmap.insert (std::make_pair (mp_source->layer_of (*l->second), &target_cell->shapes (l->first))); + target_lmap.insert (std::make_pair (l->second, &target_cell->shapes (l->first))); } } @@ -2354,7 +2452,7 @@ NetBuilder::build_net_rec (db::cell_index_type ci, size_t cid, db::Cell &tc, con } void -NetBuilder::build_net_rec (const db::Net &net, db::cell_index_type circuit_cell, const std::map &lmap, const std::string &add_net_cell_name_prefix, db::properties_id_type netname_propid, const ICplxTrans &tr) const +NetBuilder::build_net_rec (const db::Net &net, db::cell_index_type circuit_cell, const std::map &lmap, const std::string &add_net_cell_name_prefix, db::properties_id_type netname_propid, const ICplxTrans &tr) const { if (! m_cmap.has_mapping (circuit_cell)) { diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index a76f44784..4b35c7914 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -30,6 +30,7 @@ #include "dbLayoutToNetlistEnums.h" #include "dbLog.h" #include "tlGlobPattern.h" +#include "tlOptional.h" namespace db { @@ -355,6 +356,13 @@ public: return is_persisted_impl (coll); } + /** + * @brief Gets the layer index from a name + * + * If the name is not valid, the optional value will not be set. + */ + tl::optional layer_index_by_name (const std::string &name) const; + /** * @brief Gets the region (layer) with the given name * If the name is not valid, this method returns 0. Otherwise it @@ -363,20 +371,34 @@ public: */ db::Region *layer_by_name (const std::string &name); + /** + * @brief Gets the texts (layer) with the given name + * If the name is not valid, this method returns 0. Otherwise it + * will return a new'd Texts object referencing the layer with + * the given name. It must be deleted by the caller. + */ + db::Texts *texts_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 Gets the texts (layer) by index + * If the index is not valid, this method returns 0. Otherwise it + * will return a new'd Texts object referencing the layer with + * the given name. It must be deleted by the caller. + */ + db::Texts *texts_by_index (unsigned int index); + /** * @brief Gets the internal layer from the original layer */ - db::Region *layer_by_original (const ShapeCollection &original_layer) + tl::optional layer_by_original (const ShapeCollection &original_layer) { return layer_by_original (original_layer.get_delegate ()); } @@ -385,7 +407,7 @@ public: * @brief Gets the layer from the original layer's delegate * Returns 0 if the original layer was not registered as an input_layer. */ - db::Region *layer_by_original (const ShapeCollectionDelegateBase *original_delegate); + tl::optional layer_by_original (const ShapeCollectionDelegateBase *original_delegate); /** * @brief Iterates over the layer indexes and names managed by this object (begin) @@ -409,6 +431,12 @@ public: */ db::Region *make_layer (const std::string &name = std::string ()); + /** + * @brief Creates a new empty texts collection + * This method returns a new'd object which must be deleted by the caller. + */ + db::Texts *make_text_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. @@ -746,6 +774,26 @@ public: */ db::Cell *internal_top_cell (); + /** + * @brief Gets the original layout + */ + const db::Layout *original_layout () const; + + /** + * @brief Gets the original top cell + */ + const db::Cell *original_top_cell () const; + + /** + * @brief Gets the original layout (non-const version) + */ + db::Layout *original_layout (); + + /** + * @brief Gets the original top cell (non-const version) + */ + db::Cell *original_top_cell (); + /** * @brief Gets the connectivity object */ @@ -795,8 +843,9 @@ public: * original layers as kept inside the LayoutToNetlist database. * It will return a layer mapping table suitable for use with build_all_nets, build_nets etc. * Layers without original layer information will be given layer numbers ln, ln+1 etc. + * unless ln is < 0, in which case such layers will not be produced. */ - std::map create_layermap (db::Layout &target_layout, int ln) const; + std::map create_layermap (db::Layout &target_layout, int ln = -1) const; /** * @brief gets the netlist extracted (0 if no extraction happened yet) @@ -855,7 +904,21 @@ public: std::map shapes_of_terminal (const db::NetTerminalRef &terminal, const db::ICplxTrans &trans = db::ICplxTrans ()) const; /** - * @brief Returns all shapes of a specific net and layer. + * @brief Returns all polygons or texts of a specific net and layer. + * + * If "recursive" is true, the returned region will contain the shapes of + * all subcircuits too. + * + * This version takes a layer index. + * + * This methods returns a new'd Coll object. It's the responsibility of the caller + * to delete this object. + */ + template + Coll *shapes_of_net_with_layer_index (const db::Net &net, unsigned int lid, bool recursive, const db::ICplxTrans &trans = db::ICplxTrans ()) const; + + /** + * @brief Returns all polygons of a specific net and layer. * * If "recursive" is true, the returned region will contain the shapes of * all subcircuits too. @@ -863,7 +926,24 @@ public: * 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 db::ICplxTrans &trans = db::ICplxTrans ()) const; + db::Region *shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, const db::ICplxTrans &trans = db::ICplxTrans ()) const + { + return shapes_of_net_with_layer_index (net, layer_of (of_layer), recursive, trans); + } + + /** + * @brief Returns all texts 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 Texts object. It's the responsibility of the caller + * to delete this object. + */ + db::Texts *shapes_of_net (const db::Net &net, const db::Texts &of_layer, bool recursive, const db::ICplxTrans &trans = db::ICplxTrans ()) const + { + return shapes_of_net_with_layer_index (net, layer_of (of_layer), recursive, trans); + } /** * @brief Delivers all shapes of a specific net and layer to the given Shapes container. @@ -876,7 +956,14 @@ public: * * propid is an optional properties ID which is attached to the shapes if not 0. */ - void shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to, properties_id_type propid = 0, const db::ICplxTrans &trans = db::ICplxTrans ()) const; + void shapes_of_net (const db::Net &net, const db::ShapeCollection &of_layer, bool recursive, db::Shapes &to, properties_id_type propid = 0, const db::ICplxTrans &trans = db::ICplxTrans ()) const; + + /** + * @brief Delivers all shapes of a specific net and layer to the given Shapes container. + * + * This version takes a layer index for the layer + */ + void shapes_of_net (const db::Net &net, unsigned int lid, bool recursive, db::Shapes &to, properties_id_type propid = 0, const db::ICplxTrans &trans = db::ICplxTrans ()) const; /** * @brief Builds a net representation in the given layout and cell @@ -909,7 +996,7 @@ public: * @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, NetPropertyMode prop_mode, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix) const; + void build_net (const db::Net &net, db::Layout &target, db::Cell &target_cell, const std::map &lmap, NetPropertyMode prop_mode, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *cell_name_prefix, const char *device_cell_name_prefix) const; /** * @brief Builds a full hierarchical representation of the nets @@ -949,12 +1036,12 @@ public: * @param circuit_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, NetPropertyMode prop_mode, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const; + void build_all_nets (const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, NetPropertyMode prop_mode, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const; /** * @brief Like build_all_nets, but with the ability to select some nets */ - void build_nets (const std::vector *nets, const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, NetPropertyMode prop_mode, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, const char *circuit_cell_name_prefix, const char *device_cell_name_prefix) const; + void build_nets (const std::vector *nets, const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const char *net_cell_name_prefix, NetPropertyMode prop_mode, const tl::Variant &netname_prop, BuildNetHierarchyMode hier_mode, 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 @@ -1101,10 +1188,10 @@ private: db::hier_clusters m_net_clusters; std::unique_ptr mp_netlist; std::set m_dlrefs; - std::map m_named_regions; + std::map m_named_dls; std::map m_name_of_layer; - std::map m_region_by_original; - std::map m_region_of_layer; + std::map m_dl_by_original; + std::map m_dl_of_layer; bool m_netlist_extracted; bool m_is_flat; double m_device_scaling; @@ -1234,17 +1321,17 @@ public: /** * @brief See \LayoutToNetlist for details of this function */ - void build_net (db::Cell &target_cell, const db::Net &net, const std::map &lmap, NetPropertyMode prop_mode, const tl::Variant &netname_prop) const; + void build_net (db::Cell &target_cell, const db::Net &net, const std::map &lmap, NetPropertyMode prop_mode, const tl::Variant &netname_prop) const; /** * @brief See \LayoutToNetlist for details of this function */ - void build_all_nets (const std::map &lmap, NetPropertyMode prop_mode, const tl::Variant &netname_prop) const; + void build_all_nets (const std::map &lmap, NetPropertyMode prop_mode, const tl::Variant &netname_prop) const; /** * @brief See \LayoutToNetlist for details of this function */ - void build_nets (const std::vector *nets, const std::map &lmap, NetPropertyMode prop_mode, const tl::Variant &netname_prop) const; + void build_nets (const std::vector *nets, const std::map &lmap, NetPropertyMode prop_mode, const tl::Variant &netname_prop) const; /** * @brief A helper function to create a property ID for a given net, net property name and net property mode @@ -1293,9 +1380,9 @@ private: bool m_has_device_cell_name_prefix; std::string m_device_cell_name_prefix; - void build_net_rec (const db::Net &net, cell_index_type circuit_cell, const std::map &lmap, const std::string &add_net_cell_name_prefix, db::properties_id_type netname_propid, const ICplxTrans &tr) const; - void build_net_rec (const db::Net &net, db::Cell &target_cell, const std::map &lmap, const std::string &add_net_cell_name_prefix, db::properties_id_type netname_propid, const ICplxTrans &tr) const; - void build_net_rec (db::cell_index_type ci, size_t cid, db::Cell &target_cell, const std::map &lmap, const Net *net, const std::string &add_net_cell_name_prefix, db::properties_id_type netname_propid, const ICplxTrans &tr) const; + void build_net_rec (const db::Net &net, cell_index_type circuit_cell, const std::map &lmap, const std::string &add_net_cell_name_prefix, db::properties_id_type netname_propid, const ICplxTrans &tr) const; + void build_net_rec (const db::Net &net, db::Cell &target_cell, const std::map &lmap, const std::string &add_net_cell_name_prefix, db::properties_id_type netname_propid, const ICplxTrans &tr) const; + void build_net_rec (db::cell_index_type ci, size_t cid, db::Cell &target_cell, const std::map &lmap, const Net *net, const std::string &add_net_cell_name_prefix, db::properties_id_type netname_propid, const ICplxTrans &tr) const; void prepare_build_nets () const; db::Layout &target () const diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index ef9425d5b..facc04627 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -51,37 +51,93 @@ static db::LayoutToNetlist *make_l2n_flat (const std::string &topcell_name, doub 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 ()); + return 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 ()); + return 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 &netname_prop, db::BuildNetHierarchyMode hier_mode, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix) +static db::Layout *l2n_original_layout (db::LayoutToNetlist *l2n) +{ + return l2n->original_layout (); +} + +static db::Cell *l2n_original_top_cell (db::LayoutToNetlist *l2n) +{ + return l2n->original_top_cell (); +} + +static tl::Variant l2n_layer_index_by_name (const db::LayoutToNetlist *l2n, const std::string &name) +{ + tl::optional index = l2n->layer_index_by_name (name); + if (index.has_value ()) { + return tl::Variant (index.value ()); + } else { + return tl::Variant (); + } +} + +static std::map layer_map_from_var (const db::LayoutToNetlist *l2n, db::Layout &target, const tl::Variant &lmap) +{ + if (lmap.is_nil ()) { + + return l2n->create_layermap (target); + + } else if (! lmap.is_array ()) { + + throw tl::Exception (tl::to_string (tr ("'lmap' argument needs to be nil or a hash"))); + + } else { + + std::map res; + + for (auto kv = lmap.begin_array (); kv != lmap.end_array (); ++kv) { + const tl::Variant &k = kv->first; + const tl::Variant &v = kv->second; + unsigned int ki = k.to_uint (); + unsigned int vi = 0; + if (v.is_user ()) { + if (dynamic_cast *> (v.user_cls ()) != 0) { + vi = l2n->layer_of (v.to_user ()); + } else if (dynamic_cast *> (v.user_cls ()) != 0) { + vi = l2n->layer_of (v.to_user ()); + } else { + throw tl::Exception (tl::to_string (tr ("'lmap' argument hash values need to be ints, Region or Texts objects"))); + } + } else { + vi = v.to_uint (); + } + res.insert (std::make_pair (ki, vi)); + } + + return res; + + } +} + +static void build_net (const db::LayoutToNetlist *l2n, const db::Net &net, db::Layout &target, db::Cell &target_cell, const tl::Variant &lmap, const tl::Variant &netname_prop, db::BuildNetHierarchyMode hier_mode, 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, db::NPM_AllProperties, netname_prop, hier_mode, circuit_cell_name_prefix.is_nil () ? 0 : p.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); + l2n->build_net (net, target, target_cell, layer_map_from_var (l2n, target, lmap), db::NPM_AllProperties, netname_prop, hier_mode, 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 &netname_prop, db::BuildNetHierarchyMode hier_mode, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix) +static void build_all_nets (const db::LayoutToNetlist *l2n, const db::CellMapping &cmap, db::Layout &target, const tl::Variant &lmap, const tl::Variant &net_cell_name_prefix, const tl::Variant &netname_prop, db::BuildNetHierarchyMode hier_mode, 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 (), db::NPM_AllProperties, netname_prop, hier_mode, circuit_cell_name_prefix.is_nil () ? 0 : cp.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); + l2n->build_all_nets (cmap, target, layer_map_from_var (l2n, target, lmap), net_cell_name_prefix.is_nil () ? 0 : np.c_str (), db::NPM_AllProperties, netname_prop, hier_mode, circuit_cell_name_prefix.is_nil () ? 0 : cp.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); } -static void build_nets (const db::LayoutToNetlist *l2n, const std::vector &nets, const db::CellMapping &cmap, db::Layout &target, const std::map &lmap, const tl::Variant &net_cell_name_prefix, const tl::Variant &netname_prop, db::BuildNetHierarchyMode hier_mode, const tl::Variant &circuit_cell_name_prefix, const tl::Variant &device_cell_name_prefix) +static void build_nets (const db::LayoutToNetlist *l2n, const std::vector &nets, const db::CellMapping &cmap, db::Layout &target, const tl::Variant &lmap, const tl::Variant &net_cell_name_prefix, const tl::Variant &netname_prop, db::BuildNetHierarchyMode hier_mode, 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_nets (&nets, cmap, target, lmap, net_cell_name_prefix.is_nil () ? 0 : np.c_str (), db::NPM_AllProperties, netname_prop, hier_mode, circuit_cell_name_prefix.is_nil () ? 0 : cp.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); + l2n->build_nets (&nets, cmap, target, layer_map_from_var (l2n, target, lmap), net_cell_name_prefix.is_nil () ? 0 : np.c_str (), db::NPM_AllProperties, netname_prop, hier_mode, circuit_cell_name_prefix.is_nil () ? 0 : cp.c_str (), device_cell_name_prefix.is_nil () ? 0 : dp.c_str ()); } static std::vector l2n_layer_names (const db::LayoutToNetlist *l2n) @@ -370,11 +426,16 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "\n" "This method has been introduced in version 0.29.3.\n" ) + + gsi::method_ext ("layer_index", &l2n_layer_index_by_name, + "@brief Gets the layer index for a given name or nil if the name is not valid.\n" + "\n" + "This method has been introduced in version 0.30." + ) + gsi::method ("layer_name", (std::string (db::LayoutToNetlist::*) (unsigned int) const) &db::LayoutToNetlist::name, gsi::arg ("l"), "@brief Gets the name of the given layer (by index)\n" "See \\layer_index for a description of the layer index.\n" ) + - gsi::method ("register", (unsigned int (db::LayoutToNetlist::*) (const db::ShapeCollection &collection, const std::string &)) &db::LayoutToNetlist::register_layer, gsi::arg ("l"), gsi::arg ("n", std::string ()), + gsi::method ("register", (unsigned int (db::LayoutToNetlist::*) (const db::ShapeCollection &collection, const std::string &)) &db::LayoutToNetlist::register_layer, gsi::arg ("l"), gsi::arg ("n", std::string (), "\"\""), "@brief Names the given layer\n" "@return The index of the layer registered\n" "'l' must be a \\Region or \\Texts object.\n" @@ -417,18 +478,53 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "\n" "This method has been introduced in version 0.29.2.\n" ) + - gsi::factory ("layer_by_name", &db::LayoutToNetlist::layer_by_name, gsi::arg ("name"), + gsi::factory ("polygons_by_name|#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 new Region object representing the named layer. It will refer to a layer inside the " "internal layout, or more specifically inside the \\DeepShapeStorage object (see \\dss and \\internal_layout).\n" "The method returns 'nil' if the name is not a valid layer name.\n" "See \\register and the make_... methods for a description of layer naming.\n" + "\n" + "It is in the responsibility of the user to use \\texts_by_index or \\polygons_by_index on the right layers. " + "A layer created for text purpose should not be used with \\polygons_by_index or vice versa.\n" + "\n" + "Starting with version 0.30, the preferred name for this method is \\polygons_by_index to " + "differentiate from \\texts_by_index." ) + - gsi::factory ("layer_by_index", &db::LayoutToNetlist::layer_by_index, gsi::arg ("index"), - "@brief Gets a layer object for the given index.\n" + gsi::factory ("texts_by_name", &db::LayoutToNetlist::texts_by_name, gsi::arg ("name"), + "@brief Gets a layer object for the given name.\n" + "The returned object is a new Texts object representing the named layer. It will refer to a layer inside the " + "internal layout, or more specifically inside the \\DeepShapeStorage object (see \\dss and \\internal_layout).\n" + "The method returns 'nil' if the name is not a valid layer name.\n" + "See \\register and the make_... methods for a description of layer naming.\n" + "\n" + "It is in the responsibility of the user to use \\texts_by_name or \\polygons_by_name on the right layers. " + "A layer created for text purpose should not be used with \\polygons_by_name or vice versa.\n" + "\n" + "This method has been introduced in version 0.30." + ) + + gsi::factory ("polygons_by_index|#layer_by_index", &db::LayoutToNetlist::layer_by_index, gsi::arg ("index"), + "@brief Gets a \\Region object for the given index.\n" "The returned object is a new Region object representing the layer with the given index. It will refer to a layer inside the " "internal layout, or more specifically inside the \\DeepShapeStorage object (see \\dss and \\internal_layout).\n" - "The method returns 'nil' if the index is not a valid layer index." + "The method returns 'nil' if the index is not a valid layer index.\n" + "\n" + "It is in the responsibility of the user to use \\texts_by_index or \\polygons_by_index on the right layers. " + "A layer created for text purpose should not be used with \\polygons_by_index or vice versa.\n" + "\n" + "Starting with version 0.30, the preferred name for this method is \\polygons_by_index to " + "differentiate from \\texts_by_index." + ) + + gsi::factory ("texts_by_index", &db::LayoutToNetlist::texts_by_index, gsi::arg ("index"), + "@brief Gets a \\Texts object for the given index.\n" + "The returned object is a new Texts object representing the layer with the given index. It will refer to a layer inside the " + "internal layout, or more specifically inside the \\DeepShapeStorage object (see \\dss and \\internal_layout).\n" + "The method returns 'nil' if the index is not a valid layer index.\n" + "\n" + "It is in the responsibility of the user to use \\texts_by_index or \\polygons_by_index on the right layers. " + "A layer created for text purpose should not be used with \\polygons_by_index or vice versa.\n" + "\n" + "This method has been introduced in version 0.30." ) + gsi::method ("is_persisted?", &db::LayoutToNetlist::is_persisted, gsi::arg ("layer"), "@brief Returns true, if the given layer is a persisted region.\n" @@ -446,14 +542,14 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "\n" "The variant for Texts collections has been added in version 0.27." ) + - gsi::factory ("make_layer", (db::Region *(db::LayoutToNetlist::*) (const std::string &)) &db::LayoutToNetlist::make_layer, gsi::arg ("name", std::string ()), + 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" "This method will create a new, empty layer inside the internal layout and register it.\n" "It returns a new Region object that represents the new layer. See the class description for more details.\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 ()), + 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 as special, property-annotated polygons.\n" @@ -464,13 +560,25 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "the DSS (see \\dss and \\internal_layout). It returns a new Region object that represents this layer copy. " "The new layer is already registered with the given name and can be used for \\connect for example.\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" + gsi::factory ("make_text_layer", (db::Texts *(db::LayoutToNetlist::*) (const std::string &)) &db::LayoutToNetlist::make_text_layer, gsi::arg ("name", std::string (), "\"\""), + "@brief Creates a new, empty hierarchical text collection\n" + "See \\make_layer for details.\n" + "\n" + "Starting with version 0.30, the original layer index is optional, allowing to create empty, internal text layers." + ) + + gsi::factory ("make_text_layer", (db::Texts *(db::LayoutToNetlist::*) (unsigned int, const std::string &)) &db::LayoutToNetlist::make_text_layer, gsi::arg ("layer_index"), gsi::arg ("name", std::string (), "\"\""), + "@brief Creates a new text collection representing an original layer taking texts only\n" "See \\make_layer for details.\n" "\n" "Starting with version 0.27, this method returns a \\Texts object." ) + - gsi::factory ("make_polygon_layer", &db::LayoutToNetlist::make_polygon_layer, gsi::arg ("layer_index"), gsi::arg ("name", std::string ()), + gsi::factory ("make_polygon_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" + "See \\make_layer for details.\n" + "\n" + "Starting with version 0.30, the original layer index is optional for consistency with \\make_layer." + ) + + gsi::factory ("make_polygon_layer", (db::Region *(db::LayoutToNetlist::*) (unsigned int, const std::string &)) &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 only\n" "See \\make_layer for details.\n" ) + @@ -661,7 +769,7 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "@ul\n" "@li \"\" no implicit connections.@/li\n" "@li \"*\" to make all labels candidates for implicit connections.@/li\n" - "@li \"VDD\" to make all 'VDD'' nets candidates for implicit connections.@/li\n" + "@li \"VDD\" to make all 'VDD' nets candidates for implicit connections.@/li\n" "@li \"VDD\" to make all 'VDD'+suffix nets candidates for implicit connections.@/li\n" "@li \"{VDD,VSS}\" to all VDD and VSS nets candidates for implicit connections.@/li\n" "@/ul\n" @@ -713,6 +821,16 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "\n" "This method has been introduced in version 0.28.13." ) + + gsi::method_ext ("original_layout", &l2n_original_layout, + "@brief Gets the original layout or nil is the LayoutToNetlist object is not attached to one.\n" + "\n" + "This method has been introduced in version 0.30." + ) + + gsi::method_ext ("original_top_cell", &l2n_original_top_cell, + "@brief Gets the original top cell or nil is the LayoutToNetlist object is not attached to an original layout.\n" + "\n" + "This method has been introduced in version 0.30." + ) + gsi::method_ext ("internal_layout", &l2n_internal_layout, "@brief Gets the internal layout\n" "The internal layout is where the LayoutToNetlist database stores the shapes for the nets. " @@ -729,17 +847,20 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "\n" "See the class description for details about the internal layout object." ) + - gsi::method ("layer_of", &db::LayoutToNetlist::layer_of, gsi::arg ("l"), + 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" + "\n" + "A generalized version of this method is \\layer_index." ) + - gsi::method ("layer_of", &db::LayoutToNetlist::layer_of, gsi::arg ("l"), + gsi::method ("#layer_of", &db::LayoutToNetlist::layer_of, gsi::arg ("l"), "@brief Gets the internal layer for a given text collection\n" "This method is required to derive the internal layer index - for example for\n" "investigating the cluster tree.\n" "\n" "The variant for Texts collections has been added in version 0.27.\n" + "A generalized version of this method is \\layer_index." ) + gsi::method ("cell_mapping_into", (db::CellMapping (db::LayoutToNetlist::*) (db::Layout &, db::Cell &, bool)) &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" @@ -760,7 +881,7 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", 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" + "If some required cells do not exist there, they will be flattened into the first existing parent.\n" ) + gsi::method ("netlist", &db::LayoutToNetlist::netlist, "@brief gets the netlist extracted (0 if no extraction happened yet)\n" @@ -804,19 +925,60 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "This method has been introduced in version 0.29.2." ) + gsi::factory ("shapes_of_net", (db::Region *(db::LayoutToNetlist::*) (const db::Net &, const db::Region &, bool, const db::ICplxTrans &) const) &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive", true), gsi::arg ("trans", db::ICplxTrans (), "unity"), - "@brief Returns all shapes of a specific net and layer.\n" - "If 'recursive'' is true, the returned region will contain the shapes of\n" + "@brief Returns all polygons of a specific net and layer.\n" + "If 'recursive' is true, the returned region will contain the shapes of\n" "all subcircuits too.\n" "\n" "The optional 'trans' parameter allows applying a transformation to all shapes. It has been introduced in version 0.28.4." ) + - gsi::method ("shapes_of_net", (void (db::LayoutToNetlist::*) (const db::Net &, const db::Region &, bool, db::Shapes &, db::properties_id_type, const db::ICplxTrans &) const) &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive"), gsi::arg ("to"), gsi::arg ("propid", db::properties_id_type (0), "0"), gsi::arg ("trans", db::ICplxTrans (), "unity"), - "@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" + gsi::factory ("shapes_of_net", (db::Texts *(db::LayoutToNetlist::*) (const db::Net &, const db::Texts &, bool, const db::ICplxTrans &) const) &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive", true), gsi::arg ("trans", db::ICplxTrans (), "unity"), + "@brief Returns all texts of a specific net and layer.\n" + "If 'recursive' is true, the returned text collection will contain the shapes of\n" "all subcircuits too.\n" - "\"prop_id\" is an optional properties ID. If given, this property set will be attached to the shapes." + "\n" + "The optional 'trans' parameter allows applying a transformation to all shapes.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + + gsi::factory ("polygons_of_net", (db::Region *(db::LayoutToNetlist::*) (const db::Net &, unsigned int, bool, const db::ICplxTrans &) const) &db::LayoutToNetlist::shapes_of_net_with_layer_index, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive", true), gsi::arg ("trans", db::ICplxTrans (), "unity"), + "@brief Returns all polygons of a specific net and layer.\n" + "If 'recursive' is true, the returned region will contain the shapes of\n" + "all subcircuits too.\n" + "\n" + "The optional 'trans' parameter allows applying a transformation to all shapes.\n" + "\n" + "This method is similar to \\shapes_of_net, but takes a layer index for the layer. It was introduced in version 0.30.\n" + ) + + gsi::factory ("texts_of_net", (db::Texts *(db::LayoutToNetlist::*) (const db::Net &, unsigned int, bool, const db::ICplxTrans &) const) &db::LayoutToNetlist::shapes_of_net_with_layer_index, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive", true), gsi::arg ("trans", db::ICplxTrans (), "unity"), + "@brief Returns all texts of a specific net and layer.\n" + "If 'recursive' is true, the returned region will contain the shapes of\n" + "all subcircuits too.\n" + "\n" + "The optional 'trans' parameter allows applying a transformation to all shapes.\n" + "\n" + "This method is similar to \\shapes_of_net, but takes a layer index for the layer. It was introduced in version 0.30.\n" + ) + + gsi::method ("shapes_of_net", (void (db::LayoutToNetlist::*) (const db::Net &, const db::ShapeCollection &, bool, db::Shapes &, db::properties_id_type, const db::ICplxTrans &) const) &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive"), gsi::arg ("to"), gsi::arg ("propid", db::properties_id_type (0), "0"), gsi::arg ("trans", db::ICplxTrans (), "unity"), + "@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" + "\n" + "'prop_id' is an optional properties ID. If given, this property set will be attached to the shapes." "\n" "The optional 'trans' parameter allows applying a transformation to all shapes. It has been introduced in version 0.28.4." + "\n" + "The 'of_layer' argument has been generalized in version 0.30 and can be a layer index, a \\Region layer or a \\Texts layer." + ) + + gsi::method ("shapes_of_net", (void (db::LayoutToNetlist::*) (const db::Net &, unsigned int, bool, db::Shapes &, db::properties_id_type, const db::ICplxTrans &) const) &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive"), gsi::arg ("to"), gsi::arg ("propid", db::properties_id_type (0), "0"), gsi::arg ("trans", db::ICplxTrans (), "unity"), + "@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" + "\n" + "'prop_id' is an optional properties ID. If given, this property set will be attached to the shapes." + "\n" + "The optional 'trans' parameter allows applying a transformation to all shapes. It has been introduced in version 0.28.4." + "\n" + "The 'of_layer' argument has been generalized in version 0.30 and can be a layer index, a \\Region layer or a \\Texts layer." ) + gsi::method_ext ("build_net", &build_net, gsi::arg ("net"), gsi::arg ("target"), gsi::arg ("target_cell"), gsi::arg ("lmap"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::BNH_Flatten, "BNH_Flatten"), 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" @@ -828,9 +990,9 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "of the property is the net name.\n" "\n" "'lmap' defines which layers are to be produced. It is map, where the keys are layer indexes in the " - "target layout and the values are Region objects indicating the layer where shapes are to be taken from. " - "Use \\layer_by_name or \\layer_by_index to get the Region object corresponding to a layer stored inside " - "the LayoutToNetlist database. See \\build_all_nets for a code sample.\n" + "target layout and the values are Region or Texts objects or layer indexes, indicating the layer where shapes are to be taken from. " + "'lmap' can also be left nil, in which case, a layer mapping will be provided based on the layer info attributes of " + "the layers (see \\layer_info).\n" "\n" "Also see \\build_all_nets for a description of the 'hier_mode' argument.\n" "\n" @@ -840,13 +1002,15 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "\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 lmap The layer mapping (see description of this method)\n" "@param hier_mode See description of this method\n" "@param netname_prop An (optional) property name to which to attach the net name\n" "@param cell_name_prefix Chooses recursive mode if non-null\n" "@param device_cell_name_prefix See above\n" + "\n" + "The 'lmap' argument has been generalized in version 0.30 and became optional." ) + - 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 ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::BNH_Flatten, "BNH_Flatten"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"), + gsi::method_ext ("build_all_nets", &build_all_nets, gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap", tl::Variant (), "auto"), gsi::arg ("net_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::BNH_Flatten, "BNH_Flatten"), 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" @@ -858,22 +1022,12 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "of the property is the net name.\n" "\n" "'lmap' defines which layers are to be produced. It is map, where the keys are layer indexes in the " - "target layout and the values are Region objects indicating the layer where shapes are to be taken from. " - "Use \\layer_by_name or \\layer_by_index to get the Region object corresponding to a layer stored inside " - "the LayoutToNetlist database.\n" + "target layout and the values are Region or Texts objects or layer indexes, indicating the layer where shapes are to be taken from. " + "'lmap' can also be left nil, in which case, a layer mapping will be provided based on the layer info attributes of " + "the layers (see \\layer_info).\n" "\n" - "You can use the following code to replicate the layers inside the LayoutToNetlist object into a fresh target layout " - "and create the layer map for the 'lmap' argument:\n" - "\n" - "@code\n" - "l2n = ... some LayoutToNetlist object\n" - "target = ... some fresh target layout\n" - "lmap = {}\n" - "l2n.layer_indexes().each do |layer_index|\n" - " new_layer_index = target.layer(l2n.layer_info(li))\n" - " lmap[new_layer_index] = l2n.layer_by_index(li)\n" - "end\n" - "@/code\n" + "'cmap' specifies the cell mapping. Use \\create_cell_mapping or \\const_create_cell_mapping to " + "define the target cells in the target layout and to derive a cell mapping.\n" "\n" "The method has three net annotation modes:\n" "@ul\n" @@ -900,14 +1054,16 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "using a name like device_cell_name_prefix + device name. Otherwise the device shapes are\n" "treated as part of the net.\n" "\n" - "@param cmap The mapping of internal layout to target layout for the circuit mapping\n" + "@param cmap The cell mapping (see description of this method)\n" "@param target The target layout\n" - "@param lmap Target layer indexes (keys) and net regions (values)\n" + "@param lmap The layer mapping (see description of this method)\n" "@param hier_mode See description of this method\n" "@param netname_prop An (optional) property name to which to attach the net name\n" "@param circuit_cell_name_prefix See method description\n" "@param net_cell_name_prefix See method description\n" "@param device_cell_name_prefix See above\n" + "\n" + "The 'lmap' argument has been generalized in version 0.30 and became optional." ) + gsi::method_ext ("build_nets", &build_nets, gsi::arg ("nets"), gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap"), gsi::arg ("net_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::BNH_Flatten, "BNH_Flatten"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"), "@brief Like \\build_all_nets, but with the ability to select some nets." @@ -1074,7 +1230,7 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "a new LayoutToNetlist object, there will be no connection to any external layout, but all the " "essential netlist and geometry information will be available.\n" "\n" - "The LayoutToNetlist object is also the entry point for netlist-driven algorithms such as antenna checks.\n" + "The \\LayoutToNetlist object is also the entry point for netlist-driven algorithms such as antenna checks.\n" "\n" "The use model of the LayoutToNetlist object consists of five steps which need to be executed in this order.\n" "\n" @@ -1136,8 +1292,26 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "@li Helper functions: \\cell_mapping_into, \\const_cell_mapping_into @/li\n" "@/ul\n" "\n" - "The \\LayoutToNetlist object is also the entry point for connectivity-aware DRC checks, " - "such as antenna checks.\n" + "Layers stored inside the LayoutToNetlist object are addressed in three ways:\n" + "\n" + "@ul\n" + "@li Through a layer index: this is an integer number that addresses the layer. Technically this is " + " the layer index inside the internal layout (see \\internal_layout). To get a layer index from " + " a shape collection or name use \\layer_index. To get all layer indexes available, use \\layer_indexes. " + " Note, that \\make_layer, \\make_polygon_layer and \\make_text_layer also take a layer index, but " + " this the layer index of layer in the \\original_layout. @/li\n" + "@li Through a name: Alternatively to the layer index, a layer can be addressed by name, provided one was " + " assigned. To assign a name, specify one when creating a layer with \\register, \\make_layer, \\make_polygon_layer " + " or \\make_text_layer. To get the layer index of a named layer, use \\layer_index. To get the name " + " of a layer use \\layer_name. To get all names of layers, use \\layer_names. @/li\n" + "@li As a shape collection: a layer can also be represented by a shape collection. A shape collection is " + " either a \\Region or a \\Texts object. These objects do not only represent a layer, but can be used " + " to derive new layers. In order to assign names, use \\register. To create a new layer and a " + " corresponding shape collection object, use \\make_layer, \\make_polygon_layer or \\make_text_layer. " + " The get the shape collection for a given layer index, use \\layer_index, to get layer name from " + " a shape collection, use \\layer_name. To get a shape collection from a layer index, use " + " \\layer_by_index, \\polygons_by_index or \\texts_by_index. @li\n" + "@/ul\n" "\n" "This class has been introduced in version 0.26." ); diff --git a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc index 251fbe70b..91531d4de 100644 --- a/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistReaderTests.cc @@ -64,15 +64,15 @@ TEST(1_ReaderBasic) 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"); + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_index_by_name ("psd").value (); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_index_by_name ("nsd").value (); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_index_by_name ("poly").value (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_index_by_name ("diff_cont").value (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_index_by_name ("poly_cont").value (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_index_by_name ("metal1").value (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_index_by_name ("via1").value (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_index_by_name ("metal2").value (); l2n.build_all_nets (cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_Disconnected, 0, "DEVICE_"); @@ -88,15 +88,15 @@ TEST(1_ReaderBasic) ly2.dbu (l2n.internal_layout ()->dbu ()); db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); - 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"); + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_index_by_name ("psd").value (); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_index_by_name ("nsd").value (); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_index_by_name ("poly").value (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_index_by_name ("diff_cont").value (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_index_by_name ("poly_cont").value (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_index_by_name ("metal1").value (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_index_by_name ("via1").value (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_index_by_name ("metal2").value (); std::vector nets; nets.push_back (l2n.netlist ()->circuit_by_name ("RINGO")->net_by_name ("VSS")); @@ -118,15 +118,15 @@ TEST(1_ReaderBasic) ly2.dbu (l2n.internal_layout ()->dbu ()); db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); - 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"); + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_index_by_name ("psd").value (); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_index_by_name ("nsd").value (); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_index_by_name ("poly").value (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_index_by_name ("diff_cont").value (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_index_by_name ("poly_cont").value (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_index_by_name ("metal1").value (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_index_by_name ("via1").value (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_index_by_name ("metal2").value (); std::vector nets; nets.push_back (l2n.netlist ()->circuit_by_name ("RINGO")->net_by_name ("VSS")); @@ -148,15 +148,15 @@ TEST(1_ReaderBasic) ly2.dbu (l2n.internal_layout ()->dbu ()); db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); - 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"); + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_index_by_name ("psd").value (); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_index_by_name ("nsd").value (); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_index_by_name ("poly").value (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_index_by_name ("diff_cont").value (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_index_by_name ("poly_cont").value (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_index_by_name ("metal1").value (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_index_by_name ("via1").value (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_index_by_name ("metal2").value (); std::vector nets; nets.push_back (l2n.netlist ()->circuit_by_name ("RINGO")->net_by_name ("VSS")); @@ -178,15 +178,15 @@ TEST(1_ReaderBasic) ly2.dbu (l2n.internal_layout ()->dbu ()); db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); - 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"); + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_index_by_name ("psd").value (); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_index_by_name ("nsd").value (); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_index_by_name ("poly").value (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_index_by_name ("diff_cont").value (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_index_by_name ("poly_cont").value (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_index_by_name ("metal1").value (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_index_by_name ("via1").value (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_index_by_name ("metal2").value (); std::vector nets; nets.push_back (l2n.netlist ()->circuit_by_name ("RINGO")->net_by_name ("VSS")); @@ -209,15 +209,15 @@ TEST(1_ReaderBasic) ly2.dbu (l2n.internal_layout ()->dbu ()); db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); - 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"); + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_index_by_name ("psd").value (); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_index_by_name ("nsd").value (); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_index_by_name ("poly").value (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_index_by_name ("diff_cont").value (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_index_by_name ("poly_cont").value (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_index_by_name ("metal1").value (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_index_by_name ("via1").value (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_index_by_name ("metal2").value (); std::vector nets; nets.push_back (l2n.netlist ()->circuit_by_name ("RINGO")->net_by_name ("VSS")); @@ -290,18 +290,18 @@ TEST(1c_ReaderBasicShortWithProps) ly2.dbu (l2n.internal_layout ()->dbu ()); db::Cell &top2 = ly2.cell (ly2.add_cell ("TOP")); - std::map lmap; - lmap [ly2.insert_layer (db::LayerProperties (3, 0))] = l2n.layer_by_name ("poly"); - lmap [ly2.insert_layer (db::LayerProperties (3, 1))] = l2n.layer_by_name ("poly_lbl"); - 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 (6, 1))] = l2n.layer_by_name ("metal1_lbl"); - 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"); - lmap [ly2.insert_layer (db::LayerProperties (8, 1))] = l2n.layer_by_name ("metal2_lbl"); - 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"); + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (3, 0))] = l2n.layer_index_by_name ("poly").value (); + lmap [ly2.insert_layer (db::LayerProperties (3, 1))] = l2n.layer_index_by_name ("poly_lbl").value (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0))] = l2n.layer_index_by_name ("diff_cont").value (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0))] = l2n.layer_index_by_name ("poly_cont").value (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0))] = l2n.layer_index_by_name ("metal1").value (); + lmap [ly2.insert_layer (db::LayerProperties (6, 1))] = l2n.layer_index_by_name ("metal1_lbl").value (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0))] = l2n.layer_index_by_name ("via1").value (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0))] = l2n.layer_index_by_name ("metal2").value (); + lmap [ly2.insert_layer (db::LayerProperties (8, 1))] = l2n.layer_index_by_name ("metal2_lbl").value (); + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_index_by_name ("psd").value (); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_index_by_name ("nsd").value (); db::CellMapping cm = l2n.cell_mapping_into (ly2, top2); @@ -347,19 +347,19 @@ TEST(2_ReaderWithGlobalNets) 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"); + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_index_by_name ("psd").value (); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_index_by_name ("nsd").value (); + lmap [ly2.insert_layer (db::LayerProperties (12, 0))] = l2n.layer_index_by_name ("rbulk").value (); + lmap [ly2.insert_layer (db::LayerProperties (13, 0))] = l2n.layer_index_by_name ("ptie").value (); + lmap [ly2.insert_layer (db::LayerProperties (14, 0))] = l2n.layer_index_by_name ("ntie").value (); + lmap [ly2.insert_layer (db::LayerProperties (1, 0)) ] = l2n.layer_index_by_name ("nwell").value (); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_index_by_name ("poly").value (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_index_by_name ("diff_cont").value (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_index_by_name ("poly_cont").value (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_index_by_name ("metal1").value (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_index_by_name ("via1").value (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_index_by_name ("metal2").value (); l2n.build_all_nets (cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); @@ -403,19 +403,19 @@ TEST(3_ReaderAbsoluteCoordinates) 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"); + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_index_by_name ("psd").value (); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_index_by_name ("nsd").value (); + lmap [ly2.insert_layer (db::LayerProperties (12, 0))] = l2n.layer_index_by_name ("rbulk").value (); + lmap [ly2.insert_layer (db::LayerProperties (13, 0))] = l2n.layer_index_by_name ("ptie").value (); + lmap [ly2.insert_layer (db::LayerProperties (14, 0))] = l2n.layer_index_by_name ("ntie").value (); + lmap [ly2.insert_layer (db::LayerProperties (1, 0)) ] = l2n.layer_index_by_name ("nwell").value (); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_index_by_name ("poly").value (); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_index_by_name ("diff_cont").value (); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_index_by_name ("poly_cont").value (); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_index_by_name ("metal1").value (); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_index_by_name ("via1").value (); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_index_by_name ("metal2").value (); l2n.build_all_nets (cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); @@ -461,7 +461,7 @@ TEST(4_ReaderCombinedDevices) db::CellMapping cm = l2n.cell_mapping_into (ly2, top2, true /*with device cells*/); - std::map lmap = l2n.create_layermap (ly2, 1000); + std::map lmap = l2n.create_layermap (ly2, 1000); l2n.build_all_nets (cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index 8a00d69fe..6012110d9 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -386,15 +386,15 @@ TEST(1_BasicExtraction) 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 (); + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_of (rpsd); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_of (rnsd); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_of (*rpoly); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_of (*rdiff_cont); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_of (*rpoly_cont); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_of (*rmetal1); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_of (*rvia1); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_of (*rmetal2); l2n.build_all_nets (cm, ly2, lmap, 0, db::NPM_NoProperties, tl::Variant (), db::BNH_Disconnected, 0, 0); @@ -412,15 +412,15 @@ TEST(1_BasicExtraction) 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 (); + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_of (rpsd); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_of (rnsd); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_of (*rpoly); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_of (*rdiff_cont); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_of (*rpoly_cont); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_of (*rmetal1); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_of (*rvia1); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_of (*rmetal2); l2n.build_all_nets (cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_Disconnected, 0, 0); @@ -438,15 +438,15 @@ TEST(1_BasicExtraction) 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 (); + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_of (rpsd); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_of (rnsd); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_of (*rpoly); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_of (*rdiff_cont); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_of (*rpoly_cont); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_of (*rmetal1); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_of (*rvia1); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_of (*rmetal2); l2n.build_all_nets (cm, ly2, lmap, 0, db::NPM_NoProperties, tl::Variant (), db::BNH_SubcircuitCells, "CIRCUIT_", 0); @@ -464,15 +464,15 @@ TEST(1_BasicExtraction) 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 (); + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_of (rpsd); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_of (rnsd); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_of (*rpoly); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_of (*rdiff_cont); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_of (*rpoly_cont); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_of (*rmetal1); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_of (*rvia1); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_of (*rmetal2); l2n.build_all_nets (cm, ly2, lmap, 0, db::NPM_AllProperties, tl::Variant (42), db::BNH_Flatten, 0, 0); @@ -490,15 +490,15 @@ TEST(1_BasicExtraction) 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 (); + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_of (rpsd); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_of (rnsd); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_of (*rpoly); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_of (*rdiff_cont); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_of (*rpoly_cont); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_of (*rmetal1); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_of (*rvia1); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_of (*rmetal2); l2n.build_all_nets (cm, ly2, lmap, 0, db::NPM_AllProperties, tl::Variant (42), db::BNH_SubcircuitCells, "CIRCUIT_", 0); @@ -516,15 +516,15 @@ TEST(1_BasicExtraction) 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 (); + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_of (rpsd); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_of (rnsd); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_of (*rpoly); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_of (*rdiff_cont); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_of (*rpoly_cont); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_of (*rmetal1); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_of (*rvia1); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_of (*rmetal2); l2n.build_all_nets (cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); @@ -627,15 +627,15 @@ TEST(1_BasicExtraction) 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 (); + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_of (rpsd); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_of (rnsd); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_of (*rpoly); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_of (*rdiff_cont); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_of (*rpoly_cont); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_of (*rmetal1); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_of (*rvia1); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_of (*rmetal2); l2n.build_all_nets (cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); diff --git a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc index 70325f6ef..fa9d305a8 100644 --- a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc @@ -197,15 +197,15 @@ TEST(1_WriterBasic) 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 (); + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_of (rpsd); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_of (rnsd); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_of (*rpoly); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_of (*rdiff_cont); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_of (*rpoly_cont); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_of (*rmetal1); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_of (*rvia1); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_of (*rmetal2); l2n.build_all_nets (cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_Disconnected, 0, "DEVICE_"); @@ -426,19 +426,19 @@ TEST(2_WriterWithGlobalNets) 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 (); + std::map lmap; + lmap [ly2.insert_layer (db::LayerProperties (10, 0))] = l2n.layer_of (rpsd); + lmap [ly2.insert_layer (db::LayerProperties (11, 0))] = l2n.layer_of (rnsd); + lmap [ly2.insert_layer (db::LayerProperties (12, 0))] = l2n.layer_of (*rbulk); + lmap [ly2.insert_layer (db::LayerProperties (13, 0))] = l2n.layer_of (rptie); + lmap [ly2.insert_layer (db::LayerProperties (14, 0))] = l2n.layer_of (rntie); + lmap [ly2.insert_layer (db::LayerProperties (1, 0)) ] = l2n.layer_of (*rnwell); + lmap [ly2.insert_layer (db::LayerProperties (3, 0)) ] = l2n.layer_of (*rpoly); + lmap [ly2.insert_layer (db::LayerProperties (4, 0)) ] = l2n.layer_of (*rdiff_cont); + lmap [ly2.insert_layer (db::LayerProperties (5, 0)) ] = l2n.layer_of (*rpoly_cont); + lmap [ly2.insert_layer (db::LayerProperties (6, 0)) ] = l2n.layer_of (*rmetal1); + lmap [ly2.insert_layer (db::LayerProperties (7, 0)) ] = l2n.layer_of (*rvia1); + lmap [ly2.insert_layer (db::LayerProperties (8, 0)) ] = l2n.layer_of (*rmetal2); l2n.build_all_nets (cm, ly2, lmap, "NET_", db::NPM_NoProperties, tl::Variant (), db::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_"); diff --git a/src/layui/layui/layNetlistBrowserPage.cc b/src/layui/layui/layNetlistBrowserPage.cc index fd3f4d938..ce725e086 100644 --- a/src/layui/layui/layNetlistBrowserPage.cc +++ b/src/layui/layui/layNetlistBrowserPage.cc @@ -1858,7 +1858,7 @@ NetlistBrowserPage::export_nets (const std::vector *nets) cm = database->cell_mapping_into (target_layout, target_layout.cell (target_top_index), *nets); } - std::map lm = database->create_layermap (target_layout, dialog->start_layer_number ()); + std::map lm = database->create_layermap (target_layout, dialog->start_layer_number ()); database->build_nets (nets, cm, target_layout, lm, dialog->net_prefix ().empty () ? 0 : dialog->net_prefix ().c_str (), From da88973df91a76e370b2987e9bb434e56f09350c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 9 Mar 2025 22:40:17 +0100 Subject: [PATCH 056/392] A number of new methods and variants in LayoutToNetlist The goal is to support texts as native objects in LayoutToNetlist. Primarily this means, that instead of using a Region to identify a layer, it is encouraged to use a layer index instead. Also, Region is not longer the only way to represent a layer, Texts is available too. It is possible now to retrieve the texts of a net using "texts_of_net". Also, the "lmap" parameter of "build_nets" became optional. If "nil", a layer map is created based on the layer info attribute of the layers. --- src/db/db/dbLayoutToNetlist.cc | 8 +- src/db/db/gsiDeclDbLayoutToNetlist.cc | 15 +- testdata/algo/l2n_lmap_au.gds | Bin 0 -> 5886 bytes testdata/algo/l2n_lmap_au_2.gds | Bin 0 -> 3008 bytes testdata/ruby/dbLayoutToNetlist.rb | 258 ++++++++++++++++++++++++-- 5 files changed, 253 insertions(+), 28 deletions(-) create mode 100644 testdata/algo/l2n_lmap_au.gds create mode 100644 testdata/algo/l2n_lmap_au_2.gds diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 3c5e06d0c..ecc43f376 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -2360,9 +2360,7 @@ NetBuilder::build_net_rec (db::cell_index_type ci, size_t cid, db::Cell &tc, con StopOnFirst sof; std::map sof_lmap; for (std::map::const_iterator l = lmap.begin (); l != lmap.end (); ++l) { - if (l->second) { - sof_lmap.insert (std::make_pair (l->second, &sof)); - } + sof_lmap.insert (std::make_pair (l->second, &sof)); } bool consider_cell = ! deliver_shapes_of_net (m_hier_mode == BNH_Flatten, mp_source->netlist (), mp_source->net_clusters (), ci, cid, sof_lmap, tr, 0); @@ -2382,9 +2380,7 @@ NetBuilder::build_net_rec (db::cell_index_type ci, size_t cid, db::Cell &tc, con std::map target_lmap; for (std::map::const_iterator l = lmap.begin (); l != lmap.end (); ++l) { - if (l->second) { - target_lmap.insert (std::make_pair (l->second, &target_cell->shapes (l->first))); - } + target_lmap.insert (std::make_pair (l->second, &target_cell->shapes (l->first))); } deliver_shapes_of_net (m_hier_mode == BNH_Flatten, mp_source->netlist (), mp_source->net_clusters (), ci, cid, target_lmap, tr, netname_propid); diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index facc04627..470d0a133 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -94,11 +94,14 @@ static std::map layer_map_from_var (const db::Layout std::map res; for (auto kv = lmap.begin_array (); kv != lmap.end_array (); ++kv) { + const tl::Variant &k = kv->first; const tl::Variant &v = kv->second; + unsigned int ki = k.to_uint (); - unsigned int vi = 0; if (v.is_user ()) { + + unsigned int vi = 0; if (dynamic_cast *> (v.user_cls ()) != 0) { vi = l2n->layer_of (v.to_user ()); } else if (dynamic_cast *> (v.user_cls ()) != 0) { @@ -106,10 +109,14 @@ static std::map layer_map_from_var (const db::Layout } else { throw tl::Exception (tl::to_string (tr ("'lmap' argument hash values need to be ints, Region or Texts objects"))); } - } else { - vi = v.to_uint (); + res.insert (std::make_pair (ki, vi)); + + } else if (! v.is_nil ()) { + + res.insert (std::make_pair (ki, v.to_uint ())); + } - res.insert (std::make_pair (ki, vi)); + } return res; diff --git a/testdata/algo/l2n_lmap_au.gds b/testdata/algo/l2n_lmap_au.gds new file mode 100644 index 0000000000000000000000000000000000000000..01cbacf518129934d5bb110a85cd94b4b50a85aa GIT binary patch literal 5886 zcmcgw&ud&&6h8AKZ+_%8#BmH1iWD7-g;;HVRJ2%XJCUZTNoiuxA&~w7wTKoGX+^LF zMJz?7i|V2X)3LTS4aagmh^cXg4i3nAdj66g7yd+wQc-;G(-v7a6I*22 zTkS_K%0$7AJZcq;{BTft_b#RC{|ZarZ9mQ;YfNlLO3;QSH>)PX3nTPiYVQi-;fE&7DX5(5|4` z858k8EI*|^oPP)Y@db;1g8vIKs=a+D;XlO(XO8)ZdxrTjs-5N%{FHX#|Bz@m_p@kM zQ00wfdbHNWVmR^c!ZQL7tg~%OF}*yMJ~H53||u zdG;}1QSJWOEj;Mk@LB)NS5&)S|I9;v*95u$3YSq!i)v5xpXzUgzfFbRsCiwGbzAse z^vf8a5P8gFMa*C-{LZsluyhyot`jNhIY2X6?7UEamB zhj^&nfl5um1F-lt_{|vA z4)pTbZ|OJjKQb!*71a*R;78Hsy70Mz9KWF24Z8fqS7!5Vs17eI#_H85`gcA?wfnnN zc>I!Lez-^YifZ>y3Fe`i_)0QHH<%xz+EZQH6+eFs*?;bU_*YbWYDx^nFAQ*=K)&7= zEG`4vsGmi(1Ks>NT?@a+ePK$lyZ*YK!5_!{SFmrKyC3m?si=0wEWYe#et1UsihQ1& zo5jcZ_)7D>+7=82-(G21i!Z?22~!fx>|$Q76Ppo|PYcfIgOMuo4a zcI|pQt8ssw)$9X&yf$_QoNb=ecD6a%#_hhrEw2fd-o;Dg1F$TrJ-8BCo)djy&8);XORQCKn2%S#?q7R+!@suo wuzzjxihu2;b^qG2=lpA@K1{6H>_QQ}Dx;O-4$N3LQzvVkr(xGRs#h%1f78LkB>(^b literal 0 HcmV?d00001 diff --git a/testdata/algo/l2n_lmap_au_2.gds b/testdata/algo/l2n_lmap_au_2.gds new file mode 100644 index 0000000000000000000000000000000000000000..80ed5a2b8d38e53e4b66302285db10ee933563e3 GIT binary patch literal 3008 zcmcIm&uddb5T3leY~D+r5Y+DLkoM-mLr?w*UcKe)A@SR+)0drWd{0XQVZzM!?ab~svl~+6c@JnR!#6zr5c3_Mvy9_T|FX+fIw7a+7%#rE2wdqgwCQrkC$6 ztxjJe^7HgpPFZl08KYSi|09d$BxgZg)y7KoexucGEVeIWD}I5fT();e6qT}rLH{vP zp-28PtP4~{p!7KaN0fOL;{qae#3P^7QICku>2(ILiNYOY3vb|E1OAU?f4F1vOyU>k z4L>?#_-xrKrEj zpX(Rh2mW)^kM7MpMg8brW7)5*RsTWxqWiG^F7j{U`x*H+V$1&iu9aW#!D(tf>aJ^k zY}rpui!Ztt`R7dcX1<#4HJ1I_8h%3eMoKN#Titf8W^Pp6yW%z;cI3nFs50){38*0^ z5mi`DvsPs*4eC@c4eIQzG^i_hGw|W*oUq56KhmJ?UQB~pc^0E0^GZSI6CP#EE2g7( z?Shla=%Zh~lzOh}mlrQ&$=1`XE7WvihLy#q;lv0fofx5`6C+e~VuXfHj8M>t5&AhX zLOmxYjdnOgt=&+I6xsz!>3=|p8Uxo_!VqbFgRVb2K%I03vXFog}v44*1dv7d`8S{Pm4GXU>fB*mh literal 0 HcmV?d00001 diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb index b643d4b73..30408b611 100644 --- a/testdata/ruby/dbLayoutToNetlist.rb +++ b/testdata/ruby/dbLayoutToNetlist.rb @@ -558,6 +558,58 @@ END end + def shape_to_s(s) + if s.is_box || s.is_polygon + s.polygon.to_s + elsif s.is_text + s.text.to_s + else + "nn" + end + end + + def compare_layouts(ly, au_file, tmp_file = nil) + + ret = true + + begin + ly_au = RBA::Layout::new + ly_au.read(au_file) + rescue + ly.write(tmp_file) + puts "Actual layout written to: #{tmp_file}" + raise + end + + lmap = {} + + [ ly, ly_au ].each_with_index do |l,i| + l.layer_indexes.each do |li| + info = l.get_info(li) + lmap[info] ||= [ nil, nil ] + lmap[info][i] = li + end + end + + lmap.each do |info,lis| + s = !lis[0] ? "" : ly .top_cell.begin_shapes_rec(lis[0]).each.collect { |i| shape_to_s(i.shape) }.sort.join("\n") + s_au = !lis[1] ? "" : ly_au.top_cell.begin_shapes_rec(lis[1]).each.collect { |i| shape_to_s(i.shape) }.sort.join("\n") + if s != s_au + puts "Layer #{info}:\nActual shapes:\n#{s}\nGolden layout shapes:\n#{s_au}" + ret = false + end + end + + if !ret && tmp_file + ly.write(tmp_file) + puts "Golden: #{au_file}" + puts "Actual: #{tmp_file}" + end + + return ret + + end + def test_14_BuildNets l2n = RBA::LayoutToNetlist::new @@ -585,17 +637,49 @@ END l2n.build_all_nets(cm, ly, lmap, "NET_", nil, RBA::LayoutToNetlist::BNH_Disconnected, nil, "DEVICE_") - ly_au = RBA::Layout::new au_file = File.join($ut_testsrc, "testdata", "algo", "l2n_reader_au_1.gds") - ly_au.read(au_file) + tmp_file = File.join($ut_testtmp, "l2n_reader_1.gds") - lmap.each do |li,v| - li_au = ly_au.layer(ly.get_info(li)) - ly_region = RBA::Region::new(ly.top_cell.begin_shapes_rec(li)) - ly_au_region = RBA::Region::new(ly_au.top_cell.begin_shapes_rec(li_au)) - info = ly.get_info(li).to_s + ":" - assert_equal(info + (ly_region ^ ly_au_region).to_s, info) - end + assert_equal(true, compare_layouts(ly, au_file, tmp_file)) + + # build_all_nets with int-to-int layer map + + ly = RBA::Layout::new + ly.create_cell("TOP") + + cm = l2n.cell_mapping_into(ly, ly.top_cell) + + lmap = { + ly.insert_layer(RBA::LayerInfo::new(10, 0)) => l2n.layer_index("psd"), + ly.insert_layer(RBA::LayerInfo::new(11, 0)) => l2n.layer_index("nsd"), + ly.insert_layer(RBA::LayerInfo::new(3, 0)) => l2n.layer_index("poly"), + ly.insert_layer(RBA::LayerInfo::new(4, 0)) => l2n.layer_index("diff_cont"), + ly.insert_layer(RBA::LayerInfo::new(5, 0)) => l2n.layer_index("poly_cont"), + ly.insert_layer(RBA::LayerInfo::new(6, 0)) => l2n.layer_index("metal1"), + ly.insert_layer(RBA::LayerInfo::new(7, 0)) => l2n.layer_index("via1"), + ly.insert_layer(RBA::LayerInfo::new(8, 0)) => l2n.layer_index("metal2") + } + + l2n.build_all_nets(cm, ly, lmap, "NET_", nil, RBA::LayoutToNetlist::BNH_Disconnected, nil, "DEVICE_") + + au_file = File.join($ut_testsrc, "testdata", "algo", "l2n_reader_au_1.gds") + tmp_file = File.join($ut_testtmp, "l2n_reader_1.gds") + + assert_equal(true, compare_layouts(ly, au_file, tmp_file)) + + # build_all_nets with auto layer map + + ly = RBA::Layout::new + ly.create_cell("TOP") + + cm = l2n.cell_mapping_into(ly, ly.top_cell) + + l2n.build_all_nets(cm, ly, nil, "NET_", nil, RBA::LayoutToNetlist::BNH_Disconnected, nil, "DEVICE_") + + au_file = File.join($ut_testsrc, "testdata", "algo", "l2n_lmap_au.gds") + tmp_file = File.join($ut_testtmp, "l2n_lmap.gds") + + assert_equal(true, compare_layouts(ly, au_file, tmp_file)) # build_nets @@ -622,17 +706,29 @@ END l2n.build_nets(nets, cm, ly, lmap, "NET_", nil, RBA::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_") - ly_au = RBA::Layout::new au_file = File.join($ut_testsrc, "testdata", "algo", "l2n_reader_au_1d.gds") - ly_au.read(au_file) + tmp_file = File.join($ut_testtmp, "l2n_reader_1d.gds") - lmap.each do |li,v| - li_au = ly_au.layer(ly.get_info(li)) - ly_region = RBA::Region::new(ly.top_cell.begin_shapes_rec(li)) - ly_au_region = RBA::Region::new(ly_au.top_cell.begin_shapes_rec(li_au)) - info = ly.get_info(li).to_s + ":" - assert_equal(info + (ly_region ^ ly_au_region).to_s, info) - end + assert_equal(true, compare_layouts(ly, au_file, tmp_file)) + + # build_nets + + ly = RBA::Layout::new + ly.create_cell("TOP") + + cm = l2n.cell_mapping_into(ly, ly.top_cell) + + nets = [ + l2n.netlist.circuit_by_name("RINGO").net_by_name("VSS"), + l2n.netlist.circuit_by_name("RINGO").net_by_name("VDD") + ] + + l2n.build_nets(nets, cm, ly, nil, "NET_", nil, RBA::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_") + + au_file = File.join($ut_testsrc, "testdata", "algo", "l2n_lmap_au_2.gds") + tmp_file = File.join($ut_testtmp, "l2n_lmap_2.gds") + + assert_equal(true, compare_layouts(ly, au_file, tmp_file)) end @@ -1027,6 +1123,132 @@ END end + def test_22_Layers + + 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, [])) + assert_equal(l2n.original_layout.object_id, ly.object_id) + assert_equal(l2n.original_top_cell.cell_index, ly.top_cell.cell_index) + + # only plain connectivity + + ractive = l2n.make_layer( ly.layer(2, 0), "active" ) + rpoly = l2n.make_polygon_layer( ly.layer(3, 0), "poly" ) + rlabels = l2n.make_text_layer( ly.layer(6, 1), "labels" ) + rptemp = l2n.make_polygon_layer( "poly_temp" ) + rltemp = l2n.make_text_layer ( "labels_temp" ) + + rsd = ractive - rpoly + l2n.register(rsd, "sd") + + li_ptemp = l2n.layer_index(rptemp) + assert_equal(l2n.layer_name(li_ptemp), "poly_temp") + assert_equal(l2n.polygons_by_index(li_ptemp).to_s, "") + + li_ltemp = l2n.layer_index(rltemp) + assert_equal(l2n.layer_name(li_ltemp), "labels_temp") + assert_equal(l2n.texts_by_index(li_ltemp).to_s, "") + + assert_equal(l2n.layer_name(ractive), "active") + assert_equal(l2n.layer_name(rpoly), "poly") + assert_equal(l2n.layer_name(rsd), "sd") + assert_equal(l2n.layer_name(rlabels), "labels") + + li_active = l2n.layer_index("active") + li_labels = l2n.layer_index("labels") + assert_equal(l2n.layer_index(ractive), li_active) + + ractive2 = l2n.layer_by_index(li_active) + assert_equal(ractive.to_s, ractive2.to_s) + + ractive2 = l2n.layer_by_name("active") + assert_equal(ractive.to_s, ractive2.to_s) + + ractive2 = l2n.polygons_by_index(li_active) + assert_equal(ractive.to_s, ractive2.to_s) + + ractive2 = l2n.polygons_by_name("active") + assert_equal(ractive.to_s, ractive2.to_s) + + rlabels2 = l2n.texts_by_index(li_labels) + assert_equal(rlabels.to_s, rlabels2.to_s) + + rlabels2 = l2n.texts_by_name("labels") + assert_equal(rlabels.to_s, rlabels2.to_s) + + end + + def test_23_ShapesOfNet + + ly = RBA::Layout::new + top = ly.create_cell("TOP") + + l1 = ly.layer(1, 0) + l2 = ly.layer(2, 0) + l3 = ly.layer(3, 0) + + top.shapes(l1).insert(RBA::DBox::new(0, 0, 1000, 10)) + top.shapes(l2).insert(RBA::DBox::new(0, 0, 10, 10)) + top.shapes(l2).insert(RBA::DBox::new(990, 0, 1000, 10)) + top.shapes(l3).insert(RBA::DText::new("A", RBA::DTrans::new(5, 5))) + top.shapes(l3).insert(RBA::DText::new("B", RBA::DTrans::new(995, 5))) + + l2n = RBA::LayoutToNetlist::new(RBA::RecursiveShapeIterator::new(ly, top, [])) + l1r = l2n.make_polygon_layer(l1, "L1") + l2r = l2n.make_polygon_layer(l2, "L1.pin") + l3r = l2n.make_text_layer(l3, "L1.label") + + l2n.connect(l1r) + l2n.connect(l1r, l2r) + l2n.connect(l2r) + l2n.connect(l2r, l3r) + + l2n.extract_netlist + + nl = l2n.netlist + net = nl.top_circuit.net_by_name("A,B") + + assert_equal(l2n.shapes_of_net(net, l1r).to_s, "(0,0;0,10000;1000000,10000;1000000,0)") + assert_equal(l2n.shapes_of_net(net, l2r).to_s, "(0,0;0,10000;10000,10000;10000,0);(990000,0;990000,10000;1000000,10000;1000000,0)") + assert_equal(l2n.shapes_of_net(net, l3r).to_s, "('A',m45 0,0);('B',m135 0,0)") + + assert_equal(l2n.polygons_of_net(net, l2n.layer_index(l1r)).to_s, "(0,0;0,10000;1000000,10000;1000000,0)") + assert_equal(l2n.polygons_of_net(net, l2n.layer_index(l2r)).to_s, "(0,0;0,10000;10000,10000;10000,0);(990000,0;990000,10000;1000000,10000;1000000,0)") + assert_equal(l2n.polygons_of_net(net, l2n.layer_index(l3r)).to_s, "") + + assert_equal(l2n.texts_of_net(net, l2n.layer_index(l1r)).to_s, "") + assert_equal(l2n.texts_of_net(net, l2n.layer_index(l2r)).to_s, "") + assert_equal(l2n.texts_of_net(net, l2n.layer_index(l3r)).to_s, "('A',m45 0,0);('B',m135 0,0)") + + shapes = RBA::Shapes::new + l2n.shapes_of_net(net, l1r, true, shapes) + assert_equal(shapes.each.collect(&:to_s).join("/"), "box (0,0;1000000,10000)") + + shapes = RBA::Shapes::new + l2n.shapes_of_net(net, l2r, true, shapes) + assert_equal(shapes.each.collect(&:to_s).join("/"), "box (0,0;10000,10000)/box (990000,0;1000000,10000)") + + shapes = RBA::Shapes::new + l2n.shapes_of_net(net, l2n.layer_index(l2r), true, shapes) + assert_equal(shapes.each.collect(&:to_s).join("/"), "box (0,0;10000,10000)/box (990000,0;1000000,10000)") + + shapes = RBA::Shapes::new + l2n.shapes_of_net(net, l3r, true, shapes) + assert_equal(shapes.each.collect(&:to_s).join("/"), "text ('A',m45 0,0)/text ('B',m135 0,0)") + + shapes = RBA::Shapes::new + # wrong container type, correct content (layer_by_index gives a Region) + l2n.shapes_of_net(net, l2n.layer_by_index(l2n.layer_index(l3r)), true, shapes) + assert_equal(shapes.each.collect(&:to_s).join("/"), "text ('A',m45 0,0)/text ('B',m135 0,0)") + + shapes = RBA::Shapes::new + l2n.shapes_of_net(net, l2n.layer_index(l3r), true, shapes) + assert_equal(shapes.each.collect(&:to_s).join("/"), "text ('A',m45 0,0)/text ('B',m135 0,0)") + + end + end load("test_epilogue.rb") From 2baaf322ebf98bf8d262823d0b99ea6366ad64f9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 9 Mar 2025 23:20:18 +0100 Subject: [PATCH 057/392] Fixed a few doc typos and default values for 'lmap' on build_nets --- src/db/db/gsiDeclDbLayoutToNetlist.cc | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 470d0a133..fdc17f2fb 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -829,12 +829,12 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "This method has been introduced in version 0.28.13." ) + gsi::method_ext ("original_layout", &l2n_original_layout, - "@brief Gets the original layout or nil is the LayoutToNetlist object is not attached to one.\n" + "@brief Gets the original layout or nil if the LayoutToNetlist object is not attached to one.\n" "\n" "This method has been introduced in version 0.30." ) + gsi::method_ext ("original_top_cell", &l2n_original_top_cell, - "@brief Gets the original top cell or nil is the LayoutToNetlist object is not attached to an original layout.\n" + "@brief Gets the original top cell or nil if the LayoutToNetlist object is not attached to an original layout.\n" "\n" "This method has been introduced in version 0.30." ) + @@ -987,7 +987,7 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "\n" "The 'of_layer' argument has been generalized in version 0.30 and can be a layer index, a \\Region layer or a \\Texts layer." ) + - gsi::method_ext ("build_net", &build_net, gsi::arg ("net"), gsi::arg ("target"), gsi::arg ("target_cell"), gsi::arg ("lmap"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::BNH_Flatten, "BNH_Flatten"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"), + gsi::method_ext ("build_net", &build_net, gsi::arg ("net"), gsi::arg ("target"), gsi::arg ("target_cell"), gsi::arg ("lmap", tl::Variant (), "nil"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::BNH_Flatten, "BNH_Flatten"), 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 puts the shapes of a net into the given target cell using a variety of options\n" @@ -1017,7 +1017,7 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "\n" "The 'lmap' argument has been generalized in version 0.30 and became optional." ) + - gsi::method_ext ("build_all_nets", &build_all_nets, gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap", tl::Variant (), "auto"), gsi::arg ("net_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::BNH_Flatten, "BNH_Flatten"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"), + gsi::method_ext ("build_all_nets", &build_all_nets, gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap", tl::Variant (), "nil"), gsi::arg ("net_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::BNH_Flatten, "BNH_Flatten"), 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" @@ -1072,8 +1072,10 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "\n" "The 'lmap' argument has been generalized in version 0.30 and became optional." ) + - gsi::method_ext ("build_nets", &build_nets, gsi::arg ("nets"), gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap"), gsi::arg ("net_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::BNH_Flatten, "BNH_Flatten"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"), + gsi::method_ext ("build_nets", &build_nets, gsi::arg ("nets"), gsi::arg ("cmap"), gsi::arg ("target"), gsi::arg ("lmap", tl::Variant (), "nil"), gsi::arg ("net_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("netname_prop", tl::Variant (), "nil"), gsi::arg ("hier_mode", db::BNH_Flatten, "BNH_Flatten"), gsi::arg ("circuit_cell_name_prefix", tl::Variant (), "nil"), gsi::arg ("device_cell_name_prefix", tl::Variant (), "nil"), "@brief Like \\build_all_nets, but with the ability to select some nets." + "\n" + "The 'lmap' argument has been generalized in version 0.30 and became optional." ) + gsi::method ("probe_net", (db::Net *(db::LayoutToNetlist::*) (const db::Region &, const db::DPoint &, std::vector *, db::Circuit *)) &db::LayoutToNetlist::probe_net, gsi::arg ("of_layer"), gsi::arg ("point"), gsi::arg ("sc_path_out", (std::vector *) 0, "nil"), gsi::arg ("initial_circuit", (db::Circuit *) 0, "nil"), "@brief Finds the net by probing a specific location on the given layer\n" @@ -1310,14 +1312,15 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "@li Through a name: Alternatively to the layer index, a layer can be addressed by name, provided one was " " assigned. To assign a name, specify one when creating a layer with \\register, \\make_layer, \\make_polygon_layer " " or \\make_text_layer. To get the layer index of a named layer, use \\layer_index. To get the name " - " of a layer use \\layer_name. To get all names of layers, use \\layer_names. @/li\n" + " of a layer use \\layer_name. To get the names of all layers, use \\layer_names. @/li\n" "@li As a shape collection: a layer can also be represented by a shape collection. A shape collection is " " either a \\Region or a \\Texts object. These objects do not only represent a layer, but can be used " - " to derive new layers. In order to assign names, use \\register. To create a new layer and a " + " to derive new layers or do computations on layers. " + " In order to assign names to derived shape collections, use \\register. To create a new layer and a " " corresponding shape collection object, use \\make_layer, \\make_polygon_layer or \\make_text_layer. " - " The get the shape collection for a given layer index, use \\layer_index, to get layer name from " + " To get the shape collection for a given layer index, use \\layer_index, to get layer name from " " a shape collection, use \\layer_name. To get a shape collection from a layer index, use " - " \\layer_by_index, \\polygons_by_index or \\texts_by_index. @li\n" + " \\layer_by_index, \\polygons_by_index or \\texts_by_index. @/li\n" "@/ul\n" "\n" "This class has been introduced in version 0.26." From 930e09809b650b5006c6ddf511110b2c0a5a7359 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 9 Mar 2025 23:20:57 +0100 Subject: [PATCH 058/392] Added an argument name --- src/db/db/gsiDeclDbLayoutToNetlist.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index fdc17f2fb..c3a565c50 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -433,7 +433,7 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "\n" "This method has been introduced in version 0.29.3.\n" ) + - gsi::method_ext ("layer_index", &l2n_layer_index_by_name, + gsi::method_ext ("layer_index", &l2n_layer_index_by_name, gsi::arg ("name"), "@brief Gets the layer index for a given name or nil if the name is not valid.\n" "\n" "This method has been introduced in version 0.30." From d6273f9bbbedd134926708bdc8c2de422cc4879d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 13 Mar 2025 00:01:28 +0100 Subject: [PATCH 059/392] [consider merging] avoiding a crash due to recursion in PCell recomputation with macro editor open (IDE generated processEvent calls and these may issue mouse events during execution of a mouse event response code) --- src/edt/edt/edtInstPropertiesPage.cc | 11 +++++++---- src/edt/edt/edtPropertiesPageUtils.cc | 5 +++++ src/edt/edt/edtServiceImpl.cc | 4 ++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/edt/edt/edtInstPropertiesPage.cc b/src/edt/edt/edtInstPropertiesPage.cc index 08cab1159..da06cb814 100644 --- a/src/edt/edt/edtInstPropertiesPage.cc +++ b/src/edt/edt/edtInstPropertiesPage.cc @@ -35,6 +35,7 @@ #include "layLayoutViewBase.h" #include "layCellSelectionForm.h" #include "layQtTools.h" +#include "layBusy.h" #include "tlExceptions.h" #include "tlString.h" @@ -870,19 +871,21 @@ InstPropertiesPage::do_apply (bool current_only, bool relative) db::Instance new_inst = pos->back ().inst_ptr; // Don't apply the same change twice - std::map::const_iterator i = insts_seen.find (pos->back ().inst_ptr); + std::map::const_iterator i = insts_seen.find (new_inst); if (i == insts_seen.end ()) { + db::Instance org_inst = new_inst; + const lay::CellView &cv = mp_service->view ()->cellview (pos->cv_index ()); db::Cell &cell = cv->layout ().cell (pos->cell_index ()); double dbu = cv->layout ().dbu (); - if (!current_only || pos->back ().inst_ptr == current) { - new_inst = applicator->do_apply_inst (cell, pos->back ().inst_ptr, dbu, relative_mode); + if (!current_only || org_inst == current) { + new_inst = applicator->do_apply_inst (cell, org_inst, dbu, relative_mode); } - insts_seen.insert (std::make_pair (pos->back ().inst_ptr, new_inst)); + insts_seen.insert (std::make_pair (org_inst, new_inst)); } else { new_inst = i->second; diff --git a/src/edt/edt/edtPropertiesPageUtils.cc b/src/edt/edt/edtPropertiesPageUtils.cc index 46ef34d5f..2bb359eae 100644 --- a/src/edt/edt/edtPropertiesPageUtils.cc +++ b/src/edt/edt/edtPropertiesPageUtils.cc @@ -28,6 +28,7 @@ #include "dbLayout.h" #include "dbLibrary.h" #include "dbPCellDeclaration.h" +#include "layBusy.h" #include @@ -630,6 +631,10 @@ ChangeTargetPCellApplicator::ChangeTargetPCellApplicator (db::pcell_id_type pcel db::Instance ChangeTargetPCellApplicator::do_apply_inst (db::Cell &cell, const db::Instance &instance, double /*dbu*/, bool /*relative*/) const { + // Prevent recursion due to processEvents produced by macro code when + // executing inside the IDE + lay::BusySection busy; + tl_assert (cell.layout ()); db::Layout *layout = cell.layout (); diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index 53a0456f8..ba5f77723 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -38,6 +38,7 @@ #include "layMarker.h" #include "layLayerProperties.h" #include "layLayoutViewBase.h" +#include "layBusy.h" #if defined(HAVE_QT) # include "layLayoutView.h" @@ -1722,6 +1723,9 @@ InstService::make_cell (const lay::CellView &cv) return std::make_pair (true, m_current_cell); } + // prevents recursion + lay::BusySection busy; + // NOTE: do this at the beginning: creating a transaction might delete transactions behind the // head transaction, hence releasing (thus: deleting) cells. To prevert interference, create // the transaction at the beginning. From 60f7c70f11c3a1204aa16c778538168632b1eeb2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 13 Mar 2025 23:35:58 +0100 Subject: [PATCH 060/392] Trying to fix github workflow --- .github/workflows/build.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9b7295218..354450062 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -97,7 +97,7 @@ jobs: - uses: actions/upload-artifact@v4 with: - name: artifact-${{ matrix.os }}-${{ matrix.cibw_arch }}-${{ strategy.job-index }} + name: artifact-sdist path: dist/*.tar.gz upload_to_test_pypy: @@ -106,7 +106,6 @@ jobs: steps: - uses: actions/download-artifact@v4 with: - name: artifact-${{ matrix.os }}-${{ matrix.cibw_arch }}-${{ strategy.job-index }} path: dist - uses: pypa/gh-action-pypi-publish@v1.12.4 @@ -123,7 +122,6 @@ jobs: steps: - uses: actions/download-artifact@v4 with: - name: artifact-${{ matrix.os }}-${{ matrix.cibw_arch }}-${{ strategy.job-index }} path: dist - uses: pypa/gh-action-pypi-publish@v1.12.4 From ed809952c3329944d82e0e8aa4abbf249c1b664d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 14 Mar 2025 01:12:13 +0100 Subject: [PATCH 061/392] Pinning setuptools to <76.0.0 for now as this version is broken for MSVC --- azure-pipelines.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ad83bd740..8d1ab246a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -92,7 +92,8 @@ jobs: displayName: 'Download and Extract KLayout bits' - script: | - python -m pip install --upgrade pip setuptools wheel + # setuptools 67.0.0 is not working as of now (pypa/setuptools#4885) + python -m pip install --upgrade pip "setuptools<76.0.0" wheel displayName: 'Update pip, setuptools and wheel' - script: | From 82d2adeb0560108cbd861847556a110901d9f68d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 14 Mar 2025 01:29:49 +0100 Subject: [PATCH 062/392] Maybe fixing Github action --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 354450062..f87194b2c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -106,6 +106,7 @@ jobs: steps: - uses: actions/download-artifact@v4 with: + merge-multiple: true path: dist - uses: pypa/gh-action-pypi-publish@v1.12.4 @@ -122,6 +123,7 @@ jobs: steps: - uses: actions/download-artifact@v4 with: + merge-multiple: true path: dist - uses: pypa/gh-action-pypi-publish@v1.12.4 From d60b296417c2570d1a673fdce5d6513f252b316d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 14 Mar 2025 23:29:09 +0100 Subject: [PATCH 063/392] Improvement: Device#terminal_ref to get a NetTerminalRef from a Device. --- src/db/db/dbDevice.cc | 11 +++++++ src/db/db/dbDevice.h | 16 +++++++++++ src/db/db/gsiDeclDbNetlist.cc | 54 +++++++++++++++++++++++++++++++++-- testdata/ruby/dbNetlist.rb | 5 ++++ 4 files changed, 84 insertions(+), 2 deletions(-) diff --git a/src/db/db/dbDevice.cc b/src/db/db/dbDevice.cc index 816a2c77c..199464c73 100644 --- a/src/db/db/dbDevice.cc +++ b/src/db/db/dbDevice.cc @@ -133,6 +133,17 @@ const Net *Device::net_for_terminal (size_t terminal_id) const return 0; } +const NetTerminalRef *Device::terminal_ref_for_terminal (size_t terminal_id) const +{ + if (terminal_id < m_terminal_refs.size ()) { + Net::terminal_iterator p = m_terminal_refs [terminal_id]; + if (! tl::is_null_iterator (p)) { + return p.operator-> (); + } + } + return 0; +} + void Device::connect_terminal (size_t terminal_id, Net *net) { if (net_for_terminal (terminal_id) == net) { diff --git a/src/db/db/dbDevice.h b/src/db/db/dbDevice.h index b0671200f..708e48d04 100644 --- a/src/db/db/dbDevice.h +++ b/src/db/db/dbDevice.h @@ -254,6 +254,22 @@ public: return const_cast (((const Device *) this)->net_for_terminal (terminal_id)); } + /** + * @brief Gets the terminal reference for the given terminal on a device + * Returns 0 if no net is attached or the device is not embedded into a netlist. + * A terminal ref is the connector between a net and a device. It is useful for example + * to get the shapes of a terminal. + */ + const NetTerminalRef *terminal_ref_for_terminal (size_t terminal_id) const; + + /** + * @brief Gets the terminal reference for the given terminal on a device (non-const version) + */ + NetTerminalRef *terminal_ref_for_terminal (size_t terminal_id) + { + return const_cast (((const Device *) this)->terminal_ref_for_terminal (terminal_id)); + } + /** * @brief Connects the given terminal to the given net * If the net is 0 the terminal is disconnected. diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index f27df1f2e..05e2c85bc 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -268,6 +268,24 @@ static db::Net *net_for_terminal_by_name (db::Device *device, const std::string } } +static const db::NetTerminalRef *terminal_ref_by_name_const (const db::Device *device, const std::string &name) +{ + if (! device->device_class () || ! device->device_class ()->has_terminal_with_name (name)) { + return 0; + } else { + return device->terminal_ref_for_terminal (device->device_class ()->terminal_id_for_name (name)); + } +} + +static db::NetTerminalRef *terminal_ref_by_name (db::Device *device, const std::string &name) +{ + if (! device->device_class () || ! device->device_class ()->has_terminal_with_name (name)) { + return 0; + } else { + return device->terminal_ref_for_terminal (device->device_class ()->terminal_id_for_name (name)); + } +} + Class decl_dbDevice (decl_dbNetlistObject, "db", "Device", gsi::method ("device_class", &db::Device::device_class, "@brief Gets the device class the device belongs to.\n" @@ -354,18 +372,50 @@ Class decl_dbDevice (decl_dbNetlistObject, "db", "Device", "\n\n" "This constness variant has been introduced in version 0.26.8" ) + - gsi::method_ext ("net_for_terminal", net_for_terminal_by_name_const, gsi::arg ("terminal_name"), + gsi::method_ext ("net_for_terminal", &net_for_terminal_by_name_const, gsi::arg ("terminal_name"), "@brief Gets the net connected to the specified terminal.\n" "If the terminal is not connected, nil is returned for the net." "\n\n" "This convenience method has been introduced in version 0.27.3.\n" ) + - gsi::method_ext ("net_for_terminal", net_for_terminal_by_name, gsi::arg ("terminal_name"), + gsi::method_ext ("net_for_terminal", &net_for_terminal_by_name, gsi::arg ("terminal_name"), "@brief Gets the net connected to the specified terminal (non-const version).\n" "If the terminal is not connected, nil is returned for the net." "\n\n" "This convenience method has been introduced in version 0.27.3.\n" ) + + gsi::method ("terminal_ref", (const db::NetTerminalRef *(db::Device::*) (size_t) const) &db::Device::terminal_ref_for_terminal, gsi::arg ("terminal_id"), + "@brief Gets the terminal refeference for a specific terminal.\n" + "The terminal ref is the connector between a net and a device terminal. " + "It knows the net the terminal is connected to and is useful to obtain the shapes making the terminal of the device. " + "If the terminal is not connected, nil is returned for the net.\n" + "\n" + "This method has been introduced in version 0.30." + ) + + gsi::method ("terminal_ref", (db::NetTerminalRef *(db::Device::*) (size_t)) &db::Device::terminal_ref_for_terminal, gsi::arg ("terminal_id"), + "@brief Gets the terminal refeference for a specific terminal (non-const version).\n" + "The terminal ref is the connector between a net and a device terminal. " + "It knows the net the terminal is connected to and is useful to obtain the shapes making the terminal of the device. " + "If the terminal is not connected, nil is returned for the net.\n" + "\n" + "This method has been introduced in version 0.30." + ) + + gsi::method_ext ("terminal_ref", &terminal_ref_by_name_const, gsi::arg ("terminal_name"), + "@brief Gets the terminal refeference for a specific terminal where the terminal is given by name.\n" + "The terminal ref is the connector between a net and a device terminal. " + "It knows the net the terminal is connected to and is useful to obtain the shapes making the terminal of the device. " + "If the terminal is not connected, nil is returned for the net.\n" + "\n" + "This method has been introduced in version 0.30." + ) + + gsi::method_ext ("terminal_ref", &terminal_ref_by_name, gsi::arg ("terminal_name"), + "@brief Gets the terminal refeference for a specific terminal where the terminal is given by name (non-const version).\n" + "The terminal ref is the connector between a net and a device terminal. " + "It knows the net the terminal is connected to and is useful to obtain the shapes making the terminal of the device. " + "If the terminal is not connected, nil is returned for the net.\n" + "\n" + "This method has been introduced in version 0.30." + ) + 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" ) + diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 3e8d919c1..aaf5e9cad 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -347,6 +347,11 @@ class DBNetlist_TestClass < TestBase assert_equal(d1.net_for_terminal("X").inspect, "nil") assert_equal(d1.net_for_terminal(0).inspect, "nil") + assert_equal(d1.terminal_ref(1).net.name, "NET") + assert_equal(d1.terminal_ref("B").net.name, "NET") + assert_equal(d1.terminal_ref("X").inspect, "nil") + assert_equal(d1.terminal_ref(0).inspect, "nil") + d1.disconnect_terminal("B") assert_equal(net.terminal_count, 0) assert_equal(d1.net_for_terminal(1).inspect, "nil") From f136fdcde6d799d8328c3d2b5558437bc250cae9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Mar 2025 00:52:57 +0100 Subject: [PATCH 064/392] WIP --- src/db/db/db.pro | 2 + src/db/db/dbQuadTree.cc | 24 ++ src/db/db/dbQuadTree.h | 494 ++++++++++++++++++++++++++ src/db/db/dbTriangle.cc | 20 +- src/db/db/dbTriangles.cc | 26 +- src/db/db/dbTriangles.h | 1 + src/db/unit_tests/dbQuadTreeTests.cc | 77 ++++ src/db/unit_tests/dbTriangleTests.cc | 36 ++ src/db/unit_tests/dbTrianglesTests.cc | 37 ++ src/db/unit_tests/unit_tests.pro | 1 + 10 files changed, 710 insertions(+), 8 deletions(-) create mode 100644 src/db/db/dbQuadTree.cc create mode 100644 src/db/db/dbQuadTree.h create mode 100644 src/db/unit_tests/dbQuadTreeTests.cc diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 499046cc7..bbae9671a 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -81,6 +81,7 @@ SOURCES = \ dbPolygonGenerators.cc \ dbPropertiesFilter.cc \ dbPropertiesRepository.cc \ + dbQuadTree.cc \ dbReader.cc \ dbRecursiveInstanceIterator.cc \ dbRecursiveShapeIterator.cc \ @@ -319,6 +320,7 @@ HEADERS = \ dbPropertiesFilter.h \ dbPropertiesRepository.h \ dbPropertyConstraint.h \ + dbQuadTree.h \ dbReader.h \ dbRecursiveInstanceIterator.h \ dbRecursiveShapeIterator.h \ diff --git a/src/db/db/dbQuadTree.cc b/src/db/db/dbQuadTree.cc new file mode 100644 index 000000000..7e9992fbc --- /dev/null +++ b/src/db/db/dbQuadTree.cc @@ -0,0 +1,24 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbQuadTree.h" + diff --git a/src/db/db/dbQuadTree.h b/src/db/db/dbQuadTree.h new file mode 100644 index 000000000..e3ab61ef2 --- /dev/null +++ b/src/db/db/dbQuadTree.h @@ -0,0 +1,494 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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_dbQuadTree +#define HDR_dbQuadTree + +#include "dbBox.h" +#include + +namespace db +{ + +template +class quad_tree_node +{ +public: + typedef typename T::coord_type coord_type; + typedef db::box box_type; + typedef db::point point_type; + typedef db::vector vector_type; + typedef std::vector objects_vector; + + quad_tree_node (const point_type ¢er) + : m_split (false), m_center (center) + { + for (unsigned int i = 0; i < 4; ++i) { + m_q [i] = 0; + } + } + + ~quad_tree_node () + { + for (unsigned int i = 0; i < 4; ++i) { + delete m_q [i]; + m_q [i] = 0; + } + } + + const point_type ¢er () const + { + return m_center; + } + + void insert_top (const T &value, const box_type &total_box) + { + insert (value, propose_ucenter (total_box)); + } + + bool remove (const T &value) + { + box_type b = BC () (value); + + if (! m_split || b.contains (m_center)) { + for (auto i = m_objects.begin (); i != m_objects.end (); ++i) { + if (*i == value) { + m_objects.erase (i); + return true; + } + } + } + + for (unsigned int i = 0; i < 4; ++i) { + if (m_q[i] && b.inside (m_q[i]->box (m_center))) { + return b.remove (value); + } + } + + return false; + } + + const objects_vector &objects () const + { + return m_objects; + } + + box_type q_box (unsigned int n) const + { + if (m_q[n]) { + return m_q[n]->box (m_center); + } else { + return box_type (); + } + } + + quad_tree_node *node (unsigned int n) const + { + return m_q [n]; + } + + bool empty () const + { + if (m_objects.empty ()) { + for (unsigned int n = 0; n < 4; ++n) { + if (m_q[n] && ! m_q[n]->empty ()) { + return false; + } + } + return true; + } else { + return false; + } + } + + size_t size () const + { + size_t count = m_objects.size (); + for (unsigned int n = 0; n < 4; ++n) { + if (m_q[n]) { + count += m_q[n]->size (); + } + } + return count; + } + +private: + bool m_split; + point_type m_center; + quad_tree_node *m_q [4]; + objects_vector m_objects; + + box_type box (const point_type &ucenter) const + { + return box_type (ucenter, ucenter - (ucenter - m_center) * 2.0); + } + + box_type q (unsigned int n, const point_type &ucenter) const + { + // NOTE: with this definition the opposite quad index is 3 - n + + vector_type vx (std::abs (ucenter.x () - m_center.x ()), 0); + vector_type vy (0, std::abs (ucenter.y () - m_center.y ())); + switch (n) { + case 0: + return box_type (m_center - vx - vy, m_center); + case 1: + return box_type (m_center - vy, m_center + vx); + case 2: + return box_type (m_center - vx, m_center + vy); + default: + return box_type (m_center, m_center + vx + vy); + } + } + + void split (const point_type &ucenter) + { + m_split = true; + + objects_vector ov; + ov.swap (m_objects); + + for (auto o = ov.begin (); o != ov.end (); ++o) { + insert (*o, ucenter); + } + } + + void insert (const T &value, const point_type &ucenter) + { + if (! m_split && m_objects.size () + 1 < thr) { + + m_objects.push_back (value); + + } else { + + if (! m_split) { + split (ucenter); + } + + box_type b = BC () (value); + // @@@ should exclude m_center on box + if (b.contains (m_center)) { + m_objects.push_back (value); + return; + } + + for (unsigned int i = 0; i < 4; ++i) { + box_type bq = q (i, ucenter); + if (b.inside (bq)) { + if (! m_q[i]) { + m_q[i] = new quad_tree_node (bq.center ()); + } + m_q[i]->insert (value, m_center); + return; + } + } + + for (unsigned int i = 0; i < 4; ++i) { + if (m_q[i]) { + grow (m_center - (m_center - m_q[i]->center ()) * 2.0); + insert (value, ucenter); + return; + } + } + + tl_assert (false); + + } + } + + void grow (const point_type &ucenter) + { + for (unsigned int i = 0; i < 4; ++i) { + if (m_q[i]) { + quad_tree_node *n = m_q[i]; + m_q[i] = new quad_tree_node (q (i, ucenter).center ()); + m_q[i]->m_q[3 - i] = n; + } + } + } + + point_type propose_ucenter (const box_type &total_box) const + { + for (unsigned int i = 0; i < 4; ++i) { + if (m_q[i]) { + return m_center - (m_center - m_q[i]->center ()) * 2.0; + } + } + + coord_type dx = std::max (std::abs (total_box.left () - m_center.x ()), std::abs (total_box.right () - m_center.y ())); + coord_type dy = std::max (std::abs (total_box.bottom () - m_center.y ()), std::abs (total_box.top () - m_center.y ())); + return m_center - vector_type (dx, dy); + } +}; + +template +class quad_tree_iterator +{ +public: + typedef quad_tree_node quad_tree_node_type; + typedef typename T::coord_type coord_type; + typedef db::box box_type; + + quad_tree_iterator () + : m_s (), m_i (0) + { + // .. nothing yet .. + } + + quad_tree_iterator (const quad_tree_node_type *root, const S &s) + : m_s (s), m_i (0) + { + m_stack.push_back (std::make_pair (root, -1)); + validate (); + } + + bool at_end () const + { + return m_stack.empty (); + } + + quad_tree_iterator &operator++ () + { + ++m_i; + validate (); + return *this; + } + + const T &operator* () const + { + return m_stack.back ().first->objects () [m_i]; + } + + const T *operator-> () const + { + return (m_stack.back ().first->objects ().begin () + m_i).operator-> (); + } + +public: + S m_s; + std::vector > m_stack; + size_t m_i; + + void validate () + { + auto s = m_stack.end (); + tl_assert (s != m_stack.begin ()); + --s; + + const quad_tree_node_type *qn = s->first; + while (m_i < s->first->objects ().size () && ! m_s.select (s->first->objects () [m_i])) { + ++m_i; + } + + if (m_i < qn->objects ().size ()) { + return; + } + + m_i = 0; + + for (unsigned int n = 0; n < 4; ++n) { + box_type bq = qn->q_box (n); + if (! bq.empty () && m_s.select_quad (bq)) { + m_stack.back ().second = n; + m_stack.push_back (std::make_pair (qn->node (n), -1)); + validate (); + return; + } + } + + while (! m_stack.empty ()) { + + m_stack.pop_back (); + + int &n = m_stack.back ().second; + while (++n < 4) { + box_type bq = qn->q_box (n); + if (! bq.empty () && m_s.select_quad (bq)) { + m_stack.push_back (std::make_pair (qn->node (n), -1)); + validate (); + return; + } + } + + } + } +}; + +template +class quad_tree_always_sel +{ +public: + typedef typename T::coord_type coord_type; + typedef db::box box_type; + + bool select (const T &) const + { + return true; + } + + bool select_quad (const box_type &) const + { + return true; + } +}; + +template +class quad_tree_touching_sel +{ +public: + typedef typename T::coord_type coord_type; + typedef db::box box_type; + + quad_tree_touching_sel () + { + // .. nothing yet .. + } + + quad_tree_touching_sel (const box_type &box) + : m_box (box) + { + // .. nothing yet .. + } + + bool select (const T &value) const + { + return select_quad (BC () (value)); + } + + bool select_quad (const box_type &box) const + { + return m_box.touches (box); + } + +private: + box_type m_box; +}; + +template +class quad_tree_overlapping_sel +{ +public: + typedef typename T::coord_type coord_type; + typedef db::box box_type; + + quad_tree_overlapping_sel () + { + // .. nothing yet .. + } + + quad_tree_overlapping_sel (const box_type &box) + : m_box (box) + { + // .. nothing yet .. + } + + bool select (const T &value) const + { + return select_quad (BC () (value)); + } + + bool select_quad (const box_type &box) const + { + return m_box.overlaps (box); + } + +private: + box_type m_box; +}; + +template +class quad_tree +{ +public: + typedef quad_tree_node quad_tree_node_type; + typedef quad_tree_iterator > quad_tree_flat_iterator; + typedef quad_tree_iterator > quad_tree_touching_iterator; + typedef quad_tree_iterator > quad_tree_overlapping_iterator; + typedef typename T::coord_type coord_type; + typedef db::box box_type; + typedef db::point point_type; + typedef db::vector vector_type; + typedef std::vector objects_vector; + + quad_tree () + : m_root (point_type ()) + { + // .. nothing yet .. + } + + bool empty () const + { + return m_root.empty (); + } + + size_t size () const + { + return m_root.size (); + } + + void insert (const T &value) + { + box_type b = BC () (value); + if (b.empty ()) { + return; + } + + m_total_box += b; + m_root.insert_top (value, m_total_box); + } + + void erase (const T &value) + { + m_root.remove (value); + } + + quad_tree_flat_iterator begin () const + { + return quad_tree_flat_iterator (&m_root, quad_tree_always_sel ()); + } + + quad_tree_overlapping_iterator begin_overlapping (const box_type &box) const + { + if (m_total_box.overlaps (box)) { + return quad_tree_overlapping_iterator (&m_root, quad_tree_overlapping_sel (box)); + } else { + return quad_tree_overlapping_iterator (); + } + } + + quad_tree_touching_iterator begin_touching (const box_type &box) const + { + if (m_total_box.touches (box)) { + return quad_tree_touching_iterator (&m_root, quad_tree_touching_sel (box)); + } else { + return quad_tree_touching_iterator (); + } + } + +private: + box_type m_total_box; + quad_tree_node_type m_root; +}; + +} + +#endif diff --git a/src/db/db/dbTriangle.cc b/src/db/db/dbTriangle.cc index 1016ad3ec..0fa3018c9 100644 --- a/src/db/db/dbTriangle.cc +++ b/src/db/db/dbTriangle.cc @@ -507,15 +507,23 @@ Triangle::common_edge (const Triangle *other) const int Triangle::contains (const db::DPoint &point) const { + // the relative distance of the point from the edge + double alpha = db::epsilon; + + double d = db::vprod (*mp_v[2] - *mp_v[0], *mp_v[1] - *mp_v[0]); + if (fabs (d) < db::epsilon * db::epsilon) { + return -1; + } + int res = 1; - const Vertex *vl = mp_v[2];; + const Vertex *vl = mp_v[2]; for (int i = 0; i < 3; ++i) { - const Vertex *v = mp_v[i];; - int s = db::DEdge (*vl, *v).side_of (point); - if (s == 0) { - res = 0; - } else if (s > 0) { + const Vertex *v = mp_v[i]; + double n = db::vprod (point - *vl, *v - *vl) / d; + if (n < -alpha) { return -1; + } else if (n < alpha) { + res = 0; } vl = v; } diff --git a/src/db/db/dbTriangles.cc b/src/db/db/dbTriangles.cc index e6aff5ee5..5a0475a5d 100644 --- a/src/db/db/dbTriangles.cc +++ b/src/db/db/dbTriangles.cc @@ -355,6 +355,7 @@ Triangles::insert_point (db::DCoord x, db::DCoord y, std::list > *new_triangles) { + // @@@ printf("@@@ insert %d\n", (int)mp_triangles.size ()); fflush(stdout); std::vector tris = find_triangle_for_point (*vertex); // the new vertex is outside the domain @@ -412,7 +413,24 @@ Triangles::find_closest_edge (const db::DPoint &p, db::Vertex *vstart, bool insi { if (!vstart) { - if (! mp_triangles.empty ()) { + if (m_is_constrained && ! m_initial_segments.empty ()) { + + // Use the closest initial segment for the starting point + // TODO: m_initial_segments should be some search tree + + double d = -1; + for (auto i = m_initial_segments.begin (); i != m_initial_segments.end (); ++i) { + if (db::sprod_sign (p - i->first.p1 (), i->first.d ()) >= 0 && + db::sprod_sign (p - i->first.p2 (), i->first.d ()) < 0) { + double ds = std::abs (i->first.distance (p)); + if (d < 0.0 || ds < d) { + vstart = i->second; + d = ds; + } + } + } + + } else if (! mp_triangles.empty ()) { unsigned int ls = 0; size_t n = m_vertex_heap.size (); @@ -1319,6 +1337,7 @@ void Triangles::constrain (const std::vector > &contours) { tl_assert (! m_is_constrained); + m_initial_segments.clear (); std::vector > > resolved_edges; @@ -1329,8 +1348,10 @@ Triangles::constrain (const std::vector > &contours) if (vv == c->end ()) { vv = c->begin (); } - resolved_edges.push_back (std::make_pair (db::DEdge (**v, **vv), std::vector ())); + db::DEdge e (**v, **vv); + resolved_edges.push_back (std::make_pair (e, std::vector ())); resolved_edges.back ().second = ensure_edge (*v, *vv); + m_initial_segments.push_back (std::make_pair (e, *v)); } } @@ -1606,6 +1627,7 @@ Triangles::refine (const TriangulateParameters ¶meters) auto cr = (*t)->circumcircle(); auto center = cr.first; + // printf("@@@ %s -- %s\n", (*t)->to_string().c_str(), center.to_string ().c_str()); fflush(stdout); if ((*t)->contains (center) >= 0) { if (tl::verbosity () >= parameters.base_verbosity + 20) { diff --git a/src/db/db/dbTriangles.h b/src/db/db/dbTriangles.h index f86f77c1c..f15b2dc7a 100644 --- a/src/db/db/dbTriangles.h +++ b/src/db/db/dbTriangles.h @@ -311,6 +311,7 @@ private: std::vector m_returned_edges; tl::stable_vector m_vertex_heap; bool m_is_constrained; + std::vector > m_initial_segments; size_t m_level; size_t m_id; size_t m_flips, m_hops; diff --git a/src/db/unit_tests/dbQuadTreeTests.cc b/src/db/unit_tests/dbQuadTreeTests.cc new file mode 100644 index 000000000..fd74e164b --- /dev/null +++ b/src/db/unit_tests/dbQuadTreeTests.cc @@ -0,0 +1,77 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbQuadTree.h" +#include "dbBoxConvert.h" +#include "tlUnitTest.h" +#include "tlString.h" + +typedef db::quad_tree, size_t (1)> my_quad_tree; + +std::string find_touching (const my_quad_tree &qt, const db::DBox &box) +{ + std::vector v; + auto i = qt.begin_touching (box); + while (! i.at_end ()) { + v.push_back (i->to_string ()); + ++i; + } + std::sort (v.begin (), v.end ()); + return tl::join (v, "/"); +} + +std::string find_overlapping (const my_quad_tree &qt, const db::DBox &box) +{ + std::vector v; + auto i = qt.begin_overlapping (box); + while (! i.at_end ()) { + v.push_back (i->to_string ()); + ++i; + } + std::sort (v.begin (), v.end ()); + return tl::join (v, "/"); +} + +TEST(basic) +{ + my_quad_tree tree; + EXPECT_EQ (tree.empty (), true); + EXPECT_EQ (tree.size (), size_t (0)); + + tree.insert (db::DBox ()); + EXPECT_EQ (tree.empty (), true); + EXPECT_EQ (tree.size (), size_t (0)); + + tree.insert (db::DBox (-1, -2, 3, 4)); + EXPECT_EQ (tree.empty (), false); + EXPECT_EQ (tree.size (), size_t (1)); + + EXPECT_EQ (find_touching (tree, db::DBox (-2, 0, -1, 0)), "..."); + + tree.insert (db::DBox (-1, -3, 3, 0)); + EXPECT_EQ (tree.empty (), false); + EXPECT_EQ (tree.size (), size_t (1)); + + EXPECT_EQ (find_touching (tree, db::DBox (-2, 0, -1, 0)), "..."); +} + diff --git a/src/db/unit_tests/dbTriangleTests.cc b/src/db/unit_tests/dbTriangleTests.cc index c93c93b8d..439eb1c65 100644 --- a/src/db/unit_tests/dbTriangleTests.cc +++ b/src/db/unit_tests/dbTriangleTests.cc @@ -302,6 +302,42 @@ TEST(Triangle_contains) } } +TEST(Triangle_contains_small) +{ + db::Vertex v1; + db::Vertex v2 (0.001, 0.002); + db::Vertex v3 (0.002, 0.001); + + TestableTriangleEdge s1 (&v1, &v2); + TestableTriangleEdge s2 (&v2, &v3); + TestableTriangleEdge s3 (&v3, &v1); + + { + db::Triangle tri (&s1, &s2, &s3); + EXPECT_EQ (tri.contains (db::DPoint (0, 0)), 0); + EXPECT_EQ (tri.contains (db::DPoint (-0.001, -0.002)), -1); + EXPECT_EQ (tri.contains (db::DPoint (0.0005, 0.001)), 0); + EXPECT_EQ (tri.contains (db::DPoint (0.0005, 0.002)), -1); + EXPECT_EQ (tri.contains (db::DPoint (0.0025, 0.001)), -1); + EXPECT_EQ (tri.contains (db::DPoint (0.001, -0.001)), -1); + EXPECT_EQ (tri.contains (db::DPoint (0.001, 0.001)), 1); + } + + s1.reverse (); + s2.reverse (); + s3.reverse (); + + { + db::Triangle tri2 (&s3, &s2, &s1); + EXPECT_EQ (tri2.contains(db::DPoint(0, 0)), 0); + EXPECT_EQ (tri2.contains(db::DPoint(0.0005, 0.001)), 0); + EXPECT_EQ (tri2.contains(db::DPoint(0.0005, 0.002)), -1); + EXPECT_EQ (tri2.contains(db::DPoint(0.0025, 0.001)), -1); + EXPECT_EQ (tri2.contains(db::DPoint(0.001, -0.001)), -1); + EXPECT_EQ (tri2.contains(db::DPoint(0.001, 0.001)), 1); + } +} + TEST(Triangle_circumcircle) { db::Vertex v1; diff --git a/src/db/unit_tests/dbTrianglesTests.cc b/src/db/unit_tests/dbTrianglesTests.cc index 0bc00893e..9b7dd021c 100644 --- a/src/db/unit_tests/dbTrianglesTests.cc +++ b/src/db/unit_tests/dbTrianglesTests.cc @@ -23,6 +23,7 @@ #include "dbTriangles.h" #include "dbWriter.h" +#include "dbRegionProcessors.h" #include "tlUnitTest.h" #include "tlStream.h" #include "tlFileUtils.h" @@ -917,3 +918,39 @@ TEST(triangulate_problematic) EXPECT_LT (tri.num_triangles (), size_t (490)); } +TEST(triangulate_thin) +{ + db::DPoint contour[] = { + db::DPoint (18790, 58090), + db::DPoint (18790, 58940), + db::DPoint (29290, 58940), + db::DPoint (29290, 58090) + }; + + db::DPoint hole[] = { + db::DPoint (18791, 58091), + db::DPoint (29289, 58091), + db::DPoint (29289, 58939), + db::DPoint (18791, 58939) + }; + + db::DPolygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + poly.insert_hole (hole + 0, hole + sizeof (hole) / sizeof (hole[0])); + + double dbu = 0.001; + + db::Triangles::TriangulateParameters param; + param.min_b = 0.5; + param.max_area = 0.0; + param.min_length = 2 * dbu; + + TestableTriangles tri; + db::DCplxTrans trans = db::DCplxTrans (dbu) * db::DCplxTrans (db::DTrans (db::DPoint () - poly.box ().center ())); + tri.triangulate (trans * poly, param); + + EXPECT_EQ (tri.check (false), true); + + // for debugging: + tri.dump ("debug.gds"); +} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index a24390237..ecb3c7ccb 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -14,6 +14,7 @@ SOURCES = \ dbObjectWithPropertiesTests.cc \ dbPolygonNeighborhoodTests.cc \ dbPropertiesFilterTests.cc \ + dbQuadTreeTests.cc \ dbRecursiveInstanceIteratorTests.cc \ dbRegionCheckUtilsTests.cc \ dbTriangleTests.cc \ From 54b5d9f5d682c0831473e2687fe056c38a2ab820 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Mar 2025 13:35:51 +0100 Subject: [PATCH 065/392] WIP (quad tree) --- src/db/db/dbQuadTree.h | 156 +++++++++++++++++----- src/db/unit_tests/dbQuadTreeTests.cc | 187 ++++++++++++++++++++++++++- 2 files changed, 310 insertions(+), 33 deletions(-) diff --git a/src/db/db/dbQuadTree.h b/src/db/db/dbQuadTree.h index e3ab61ef2..353f7ddcd 100644 --- a/src/db/db/dbQuadTree.h +++ b/src/db/db/dbQuadTree.h @@ -24,6 +24,7 @@ #define HDR_dbQuadTree #include "dbBox.h" +#include "tlLog.h" #include namespace db @@ -38,6 +39,7 @@ public: typedef db::point point_type; typedef db::vector vector_type; typedef std::vector objects_vector; + typedef db::coord_traits coord_traits; quad_tree_node (const point_type ¢er) : m_split (false), m_center (center) @@ -65,23 +67,31 @@ public: insert (value, propose_ucenter (total_box)); } - bool remove (const T &value) + bool erase (const T &value) { box_type b = BC () (value); - if (! m_split || b.contains (m_center)) { + int n = quad_for (b); + + if (! m_split || n < 0) { + for (auto i = m_objects.begin (); i != m_objects.end (); ++i) { if (*i == value) { m_objects.erase (i); return true; } } - } - for (unsigned int i = 0; i < 4; ++i) { - if (m_q[i] && b.inside (m_q[i]->box (m_center))) { - return b.remove (value); + } else if (m_q[n]) { + + if (m_q[n]->erase (value)) { + if (m_q[n]->empty ()) { + delete m_q[n]; + m_q[n] = 0; + } + return true; } + } return false; @@ -131,12 +141,39 @@ public: return count; } + size_t levels () const + { + size_t l = 1; + for (unsigned int n = 0; n < 4; ++n) { + if (m_q[n]) { + l = std::max (l, m_q[n]->levels () + 1); + } + } + return l; + } + + bool check_top (const box_type &total_box) const + { + return check (propose_ucenter (total_box)); + } + private: bool m_split; point_type m_center; quad_tree_node *m_q [4]; objects_vector m_objects; + int quad_for (const box_type &box) const + { + int sx = coord_traits::less (box.right (), m_center.x ()) ? 0 : (coord_traits::less (m_center.x (), box.left ()) ? 1 : -1); + int sy = coord_traits::less (box.top (), m_center.y ()) ? 0 : (coord_traits::less (m_center.y (), box.bottom ()) ? 2 : -1); + if (sx < 0 || sy < 0) { + return -1; + } else { + return sx + sy; + } + } + box_type box (const point_type &ucenter) const { return box_type (ucenter, ucenter - (ucenter - m_center) * 2.0); @@ -185,33 +222,24 @@ private: } box_type b = BC () (value); - // @@@ should exclude m_center on box - if (b.contains (m_center)) { + int n = quad_for (b); + + if (n < 0) { m_objects.push_back (value); return; } - for (unsigned int i = 0; i < 4; ++i) { - box_type bq = q (i, ucenter); - if (b.inside (bq)) { - if (! m_q[i]) { - m_q[i] = new quad_tree_node (bq.center ()); - } - m_q[i]->insert (value, m_center); - return; + if (b.inside (box (ucenter))) { + if (! m_q[n]) { + box_type bq = q (n, ucenter); + m_q[n] = new quad_tree_node (bq.center ()); } + m_q[n]->insert (value, m_center); + } else { + grow (m_center - (m_center - ucenter) * 2.0); + insert (value, ucenter); } - for (unsigned int i = 0; i < 4; ++i) { - if (m_q[i]) { - grow (m_center - (m_center - m_q[i]->center ()) * 2.0); - insert (value, ucenter); - return; - } - } - - tl_assert (false); - } } @@ -238,6 +266,61 @@ private: coord_type dy = std::max (std::abs (total_box.bottom () - m_center.y ()), std::abs (total_box.top () - m_center.y ())); return m_center - vector_type (dx, dy); } + + bool check (const point_type &ucenter) const + { + bool result = true; + + box_type bq = box (ucenter); + + for (auto i = m_objects.begin (); i != m_objects.end (); ++i) { + box_type b = BC () (*i); + if (! b.inside (bq)) { + tl::error << "Box " << b.to_string () << " not inside quad box " << bq.to_string (); + result = false; + } + } + + if (m_split) { + + for (auto i = m_objects.begin (); i != m_objects.end (); ++i) { + box_type b = BC () (*i); + int n = quad_for (b); + if (n >= 0) { + tl::error << "Box " << b.to_string () << " on quad level not overlapping multiple quads"; + result = false; + } + } + + for (unsigned int n = 0; n < 4; ++n) { + if (m_q[n]) { + m_q[n]->check (m_center); + box_type bbq = m_q[n]->box (m_center); + if (bbq != q (n, ucenter)) { + tl::error << "Quad not centered (quad box is " << bbq.to_string () << ", should be " << q (n, ucenter).to_string (); + result = false; + } + } + } + + } else { + + if (m_objects.size () > thr) { + tl::error << "Non-split object count exceeds threshold " << m_objects.size () << " > " << thr; + result = false; + } + + for (unsigned int n = 0; n < 4; ++n) { + if (m_q[n]) { + tl::error << "Non-split node has child nodes"; + result = false; + } + } + + } + + return result; + } }; template @@ -315,9 +398,9 @@ public: } } - while (! m_stack.empty ()) { + m_stack.pop_back (); - m_stack.pop_back (); + while (! m_stack.empty ()) { int &n = m_stack.back ().second; while (++n < 4) { @@ -329,6 +412,8 @@ public: } } + m_stack.pop_back (); + } } }; @@ -415,6 +500,7 @@ private: box_type m_box; }; +// @@@ TODO: copy, assignment, move, swap template class quad_tree { @@ -445,6 +531,16 @@ public: return m_root.size (); } + size_t levels () const + { + return m_root.levels (); + } + + bool check () const + { + return m_root.check_top (m_total_box); + } + void insert (const T &value) { box_type b = BC () (value); @@ -456,9 +552,9 @@ public: m_root.insert_top (value, m_total_box); } - void erase (const T &value) + bool erase (const T &value) { - m_root.remove (value); + return m_root.erase (value); } quad_tree_flat_iterator begin () const diff --git a/src/db/unit_tests/dbQuadTreeTests.cc b/src/db/unit_tests/dbQuadTreeTests.cc index fd74e164b..62eec169e 100644 --- a/src/db/unit_tests/dbQuadTreeTests.cc +++ b/src/db/unit_tests/dbQuadTreeTests.cc @@ -28,6 +28,18 @@ typedef db::quad_tree, size_t (1)> my_quad_tree; +std::string find_all (const my_quad_tree &qt) +{ + std::vector v; + auto i = qt.begin (); + while (! i.at_end ()) { + v.push_back (i->to_string ()); + ++i; + } + std::sort (v.begin (), v.end ()); + return tl::join (v, "/"); +} + std::string find_touching (const my_quad_tree &qt, const db::DBox &box) { std::vector v; @@ -40,6 +52,20 @@ std::string find_touching (const my_quad_tree &qt, const db::DBox &box) return tl::join (v, "/"); } +std::string find_touching_from_all (const my_quad_tree &qt, const db::DBox &box) +{ + std::vector v; + auto i = qt.begin (); + while (! i.at_end ()) { + if (i->touches (box)) { + v.push_back (i->to_string ()); + } + ++i; + } + std::sort (v.begin (), v.end ()); + return tl::join (v, "/"); +} + std::string find_overlapping (const my_quad_tree &qt, const db::DBox &box) { std::vector v; @@ -52,26 +78,181 @@ std::string find_overlapping (const my_quad_tree &qt, const db::DBox &box) return tl::join (v, "/"); } +std::string find_overlapping_from_all (const my_quad_tree &qt, const db::DBox &box) +{ + std::vector v; + auto i = qt.begin (); + while (! i.at_end ()) { + if (i->overlaps (box)) { + v.push_back (i->to_string ()); + } + ++i; + } + std::sort (v.begin (), v.end ()); + return tl::join (v, "/"); +} + TEST(basic) { my_quad_tree tree; EXPECT_EQ (tree.empty (), true); EXPECT_EQ (tree.size (), size_t (0)); + EXPECT_EQ (tree.check (), true); + EXPECT_EQ (tree.levels (), size_t (1)); tree.insert (db::DBox ()); EXPECT_EQ (tree.empty (), true); EXPECT_EQ (tree.size (), size_t (0)); + EXPECT_EQ (tree.check (), true); + EXPECT_EQ (tree.levels (), size_t (1)); tree.insert (db::DBox (-1, -2, 3, 4)); EXPECT_EQ (tree.empty (), false); EXPECT_EQ (tree.size (), size_t (1)); + EXPECT_EQ (tree.check (), true); + EXPECT_EQ (tree.levels (), size_t (1)); - EXPECT_EQ (find_touching (tree, db::DBox (-2, 0, -1, 0)), "..."); + EXPECT_EQ (find_all (tree), "(-1,-2;3,4)"); + + db::DBox bx; + + bx = db::DBox (-2, 0, -1, 0); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, -3, -1, -2); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, -3, -1, -2.5); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, 4, -1, 5); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, 4.5, -1, 5); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, 3, -1, 5); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, 3, -1.5, 5); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); tree.insert (db::DBox (-1, -3, 3, 0)); EXPECT_EQ (tree.empty (), false); - EXPECT_EQ (tree.size (), size_t (1)); + EXPECT_EQ (tree.size (), size_t (2)); + EXPECT_EQ (tree.check (), true); + EXPECT_EQ (tree.levels (), size_t (1)); - EXPECT_EQ (find_touching (tree, db::DBox (-2, 0, -1, 0)), "..."); + EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;3,0)"); + + bx = db::DBox (-2, 0, -1, 0); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, -3, -1, -2); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, -3, -1, -2.5); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, 4, -1, 5); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, 4.5, -1, 5); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, 3, -1, 5); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, 3, -1.5, 5); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + + tree.insert (db::DBox (-1, -3, -0.5, -2)); + EXPECT_EQ (tree.empty (), false); + EXPECT_EQ (tree.size (), size_t (3)); + EXPECT_EQ (tree.check (), true); + EXPECT_EQ (tree.levels (), size_t (2)); + + EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;3,0)"); + + bx = db::DBox (-2, 0, -1, 0); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, -3, -1, -2); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, -3, -1, -2.5); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, 4, -1, 5); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, 4.5, -1, 5); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, 3, -1, 5); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, 3, -1.5, 5); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + + tree.insert (db::DBox (-1, -3, -0.5, 2)); + EXPECT_EQ (tree.empty (), false); + EXPECT_EQ (tree.size (), size_t (4)); + EXPECT_EQ (tree.check (), true); + EXPECT_EQ (tree.levels (), size_t (2)); + + EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); + + bx = db::DBox (-2, 0, -1, 0); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, -3, -1, -2); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, -3, -1, -2.5); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, 4, -1, 5); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, 4.5, -1, 5); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, 3, -1, 5); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); + bx = db::DBox (-2, 3, -1.5, 5); + EXPECT_EQ (find_touching (tree, bx), find_touching_from_all (tree, bx)); + EXPECT_EQ (find_overlapping (tree, bx), find_overlapping_from_all (tree, bx)); +} + +TEST(remove) +{ + my_quad_tree tree; + tree.insert (db::DBox (-1, -2, 3, 4)); + tree.insert (db::DBox (-1, -3, 3, 0)); + tree.insert (db::DBox (-1, -3, -0.5, -2)); + tree.insert (db::DBox (-1, -3, -0.5, 2)); + + EXPECT_EQ (tree.check (), true); + + EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); + + EXPECT_EQ (tree.erase (db::DBox (-1, -3, -0.5, -1)), false); + EXPECT_EQ (tree.erase (db::DBox (-1, -3, -0.5, -2)), true); + EXPECT_EQ (tree.check (), true); + + EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); + + while (! tree.empty ()) { + EXPECT_EQ (tree.erase (*tree.begin ()), true); + EXPECT_EQ (tree.check (), true); + } + + EXPECT_EQ (tree.size (), size_t (0)); + EXPECT_EQ (tree.levels (), size_t (1)); } From 5c8e0539eeecad7987a2678e7db89a389ca205e9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Mar 2025 13:56:16 +0100 Subject: [PATCH 066/392] WIP (quad tree) --- src/db/db/dbQuadTree.h | 119 ++++++++++++++++++++++- src/db/unit_tests/dbQuadTreeTests.cc | 135 +++++++++++++++++++++++++++ 2 files changed, 249 insertions(+), 5 deletions(-) diff --git a/src/db/db/dbQuadTree.h b/src/db/db/dbQuadTree.h index 353f7ddcd..93674ea69 100644 --- a/src/db/db/dbQuadTree.h +++ b/src/db/db/dbQuadTree.h @@ -44,16 +44,77 @@ public: quad_tree_node (const point_type ¢er) : m_split (false), m_center (center) { - for (unsigned int i = 0; i < 4; ++i) { - m_q [i] = 0; - } + init (); } ~quad_tree_node () { + clear (); + } + + quad_tree_node (const quad_tree_node &other) + : m_split (false), m_center (center) + { + init (); + operator= (other); + } + + quad_tree_node &operator= (const quad_tree_node &other) + { + if (this != &other) { + clear (); + m_split = other.m_split; + m_center = other.m_center; + m_objects = other.m_objects; + for (unsigned int i = 0; i < 4; ++i) { + if (other.m_q[i]) { + m_q[i] = other.m_q[i]->clone (); + } + } + } + return *this; + } + + quad_tree_node (quad_tree_node &&other) + { + init (); + swap (other); + } + + quad_tree_node &operator= (quad_tree_node &&other) + { + swap (other); + return *this; + } + + void swap (quad_tree_node &other) + { + if (this != &other) { + std::swap (m_center, other.m_center); + std::swap (m_split, other.m_split); + m_objects.swap (other.m_objects); + for (unsigned int i = 0; i < 4; ++i) { + std::swap (m_q[i], other.m_q[i]); + } + } + } + + quad_tree_node *clone () const + { + quad_tree_node *node = new quad_tree_node (m_center); + *node = *this; + return node; + } + + void clear () + { + m_objects.clear (); + m_split = false; for (unsigned int i = 0; i < 4; ++i) { - delete m_q [i]; - m_q [i] = 0; + if (m_q[i]) { + delete m_q[i]; + } + m_q[i] = 0; } } @@ -163,6 +224,13 @@ private: quad_tree_node *m_q [4]; objects_vector m_objects; + void init () + { + for (unsigned int i = 0; i < 4; ++i) { + m_q[i] = 0; + } + } + int quad_for (const box_type &box) const { int sx = coord_traits::less (box.right (), m_center.x ()) ? 0 : (coord_traits::less (m_center.x (), box.left ()) ? 1 : -1); @@ -521,6 +589,47 @@ public: // .. nothing yet .. } + quad_tree (const quad_tree &other) + : m_root (point_type ()) + { + operator= (other); + } + + quad_tree &operator= (const quad_tree &other) + { + if (this != &other) { + m_root = other.m_root; + m_total_box = other.m_total_box; + } + return *this; + } + + quad_tree (quad_tree &&other) + : m_root (point_type ()) + { + swap (other); + } + + quad_tree &operator= (quad_tree &&other) + { + swap (other); + return *this; + } + + void clear () + { + m_root.clear (); + m_total_box = box_type (); + } + + void swap (quad_tree &other) + { + if (this != &other) { + m_root.swap (other.m_root); + std::swap (m_total_box, m_total_box); + } + } + bool empty () const { return m_root.empty (); diff --git a/src/db/unit_tests/dbQuadTreeTests.cc b/src/db/unit_tests/dbQuadTreeTests.cc index 62eec169e..6d39ea49b 100644 --- a/src/db/unit_tests/dbQuadTreeTests.cc +++ b/src/db/unit_tests/dbQuadTreeTests.cc @@ -256,3 +256,138 @@ TEST(remove) EXPECT_EQ (tree.levels (), size_t (1)); } +TEST(clear) +{ + my_quad_tree tree; + tree.insert (db::DBox (-1, -2, 3, 4)); + tree.insert (db::DBox (-1, -3, 3, 0)); + tree.insert (db::DBox (-1, -3, -0.5, -2)); + tree.insert (db::DBox (-1, -3, -0.5, 2)); + + EXPECT_EQ (tree.check (), true); + EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); + + tree.clear (); + + EXPECT_EQ (tree.check (), true); + EXPECT_EQ (tree.empty (), true); + EXPECT_EQ (tree.size (), size_t (0)); + EXPECT_EQ (tree.levels (), size_t (1)); + EXPECT_EQ (find_all (tree), ""); +} + +TEST(copy) +{ + my_quad_tree tree; + tree.insert (db::DBox (-1, -2, 3, 4)); + tree.insert (db::DBox (-1, -3, 3, 0)); + tree.insert (db::DBox (-1, -3, -0.5, -2)); + tree.insert (db::DBox (-1, -3, -0.5, 2)); + + EXPECT_EQ (tree.check (), true); + EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); + EXPECT_EQ (tree.levels (), size_t (2)); + + my_quad_tree tree2 (tree); + tree.clear (); + + EXPECT_EQ (tree2.check (), true); + EXPECT_EQ (find_all (tree2), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); + EXPECT_EQ (tree2.levels (), size_t (2)); +} + +TEST(assign) +{ + my_quad_tree tree; + tree.insert (db::DBox (-1, -2, 3, 4)); + tree.insert (db::DBox (-1, -3, 3, 0)); + tree.insert (db::DBox (-1, -3, -0.5, -2)); + tree.insert (db::DBox (-1, -3, -0.5, 2)); + + EXPECT_EQ (tree.check (), true); + EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); + EXPECT_EQ (tree.levels (), size_t (2)); + + my_quad_tree tree2; + tree2 = tree; + tree.clear (); + + EXPECT_EQ (tree2.check (), true); + EXPECT_EQ (find_all (tree2), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); + EXPECT_EQ (tree2.levels (), size_t (2)); +} + +TEST(swap) +{ + my_quad_tree tree; + tree.insert (db::DBox (-1, -2, 3, 4)); + tree.insert (db::DBox (-1, -3, 3, 0)); + tree.insert (db::DBox (-1, -3, -0.5, -2)); + tree.insert (db::DBox (-1, -3, -0.5, 2)); + + EXPECT_EQ (tree.check (), true); + EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); + EXPECT_EQ (tree.levels (), size_t (2)); + + my_quad_tree tree2; + tree2.swap (tree); + + EXPECT_EQ (tree.check (), true); + EXPECT_EQ (tree.empty (), true); + EXPECT_EQ (find_all (tree), ""); + EXPECT_EQ (tree.levels (), size_t (1)); + + EXPECT_EQ (tree2.check (), true); + EXPECT_EQ (find_all (tree2), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); + EXPECT_EQ (tree2.levels (), size_t (2)); +} + +TEST(move) +{ + my_quad_tree tree; + tree.insert (db::DBox (-1, -2, 3, 4)); + tree.insert (db::DBox (-1, -3, 3, 0)); + tree.insert (db::DBox (-1, -3, -0.5, -2)); + tree.insert (db::DBox (-1, -3, -0.5, 2)); + + EXPECT_EQ (tree.check (), true); + EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); + EXPECT_EQ (tree.levels (), size_t (2)); + + my_quad_tree tree2; + tree2 = std::move (tree); + + EXPECT_EQ (tree.check (), true); + EXPECT_EQ (tree.empty (), true); + EXPECT_EQ (find_all (tree), ""); + EXPECT_EQ (tree.levels (), size_t (1)); + + EXPECT_EQ (tree2.check (), true); + EXPECT_EQ (find_all (tree2), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); + EXPECT_EQ (tree2.levels (), size_t (2)); +} + +TEST(move_ctor) +{ + my_quad_tree tree; + tree.insert (db::DBox (-1, -2, 3, 4)); + tree.insert (db::DBox (-1, -3, 3, 0)); + tree.insert (db::DBox (-1, -3, -0.5, -2)); + tree.insert (db::DBox (-1, -3, -0.5, 2)); + + EXPECT_EQ (tree.check (), true); + EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); + EXPECT_EQ (tree.levels (), size_t (2)); + + my_quad_tree tree2 (std::move (tree)); + + EXPECT_EQ (tree.check (), true); + EXPECT_EQ (tree.empty (), true); + EXPECT_EQ (find_all (tree), ""); + EXPECT_EQ (tree.levels (), size_t (1)); + + EXPECT_EQ (tree2.check (), true); + EXPECT_EQ (find_all (tree2), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); + EXPECT_EQ (tree2.levels (), size_t (2)); +} + From f1f35ae2a4afc5dabb41bd40e06f14bbf5116717 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Mar 2025 14:46:03 +0100 Subject: [PATCH 067/392] WIP (quad tree) --- src/db/db/dbQuadTree.h | 33 ++++++----- src/db/unit_tests/dbQuadTreeTests.cc | 84 ++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 13 deletions(-) diff --git a/src/db/db/dbQuadTree.h b/src/db/db/dbQuadTree.h index 93674ea69..546c007ff 100644 --- a/src/db/db/dbQuadTree.h +++ b/src/db/db/dbQuadTree.h @@ -290,22 +290,26 @@ private: } box_type b = BC () (value); - int n = quad_for (b); - - if (n < 0) { - m_objects.push_back (value); - return; - } - if (b.inside (box (ucenter))) { - if (! m_q[n]) { - box_type bq = q (n, ucenter); - m_q[n] = new quad_tree_node (bq.center ()); + + int n = quad_for (b); + if (n < 0) { + m_objects.push_back (value); + } else { + if (! m_q[n]) { + box_type bq = q (n, ucenter); + m_q[n] = new quad_tree_node (bq.center ()); + } + m_q[n]->insert (value, m_center); } - m_q[n]->insert (value, m_center); + } else { - grow (m_center - (m_center - ucenter) * 2.0); - insert (value, ucenter); + + tl_assert (m_q[0] || m_q[1] || m_q[2] || m_q[3]); + point_type new_ucenter = m_center - (m_center - ucenter) * 2.0; + grow (new_ucenter); + insert (value, new_ucenter); + } } @@ -317,6 +321,7 @@ private: if (m_q[i]) { quad_tree_node *n = m_q[i]; m_q[i] = new quad_tree_node (q (i, ucenter).center ()); + m_q[i]->m_split = true; m_q[i]->m_q[3 - i] = n; } } @@ -470,6 +475,8 @@ public: while (! m_stack.empty ()) { + qn = m_stack.back ().first; + int &n = m_stack.back ().second; while (++n < 4) { box_type bq = qn->q_box (n); diff --git a/src/db/unit_tests/dbQuadTreeTests.cc b/src/db/unit_tests/dbQuadTreeTests.cc index 6d39ea49b..1866907d1 100644 --- a/src/db/unit_tests/dbQuadTreeTests.cc +++ b/src/db/unit_tests/dbQuadTreeTests.cc @@ -26,6 +26,8 @@ #include "tlUnitTest.h" #include "tlString.h" +#include + typedef db::quad_tree, size_t (1)> my_quad_tree; std::string find_all (const my_quad_tree &qt) @@ -256,6 +258,58 @@ TEST(remove) EXPECT_EQ (tree.levels (), size_t (1)); } +TEST(grow) +{ + my_quad_tree tree; + tree.insert (db::DBox (-1, -2, 3, 4)); + tree.insert (db::DBox (-1, -3, 3, 0)); + tree.insert (db::DBox (-1, -3, -0.5, -2)); + tree.insert (db::DBox (-1, -3, -0.5, 2)); + EXPECT_EQ (tree.levels (), size_t (2)); + tree.insert (db::DBox (-100, -3, -99, 2)); + EXPECT_EQ (tree.levels (), size_t (8)); + + EXPECT_EQ (tree.check (), true); + EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)/(-100,-3;-99,2)"); + EXPECT_EQ (find_overlapping (tree, db::DBox (-100, -100, -90, 100)), "(-100,-3;-99,2)"); + + bool r = true; + while (r && ! tree.empty ()) { + r = tree.erase (*tree.begin ()); + EXPECT_EQ (r, true); + EXPECT_EQ (tree.check (), true); + } + + EXPECT_EQ (tree.size (), size_t (0)); + EXPECT_EQ (tree.levels (), size_t (1)); +} + +TEST(grow2) +{ + my_quad_tree tree; + tree.insert (db::DBox (-1, -2, 3, 4)); + tree.insert (db::DBox (-1, -3, 3, 0)); + tree.insert (db::DBox (-1, -3, -0.5, -2)); + tree.insert (db::DBox (-1, -3, -0.5, 2)); + EXPECT_EQ (tree.levels (), size_t (2)); + tree.insert (db::DBox (-100, -3, -99, -1)); + EXPECT_EQ (tree.levels (), size_t (8)); + + EXPECT_EQ (tree.check (), true); + EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)/(-100,-3;-99,-1)"); + EXPECT_EQ (find_overlapping (tree, db::DBox (-100, -100, -90, 100)), "(-100,-3;-99,-1)"); + + bool r = true; + while (r && ! tree.empty ()) { + r = tree.erase (*tree.begin ()); + EXPECT_EQ (r, true); + EXPECT_EQ (tree.check (), true); + } + + EXPECT_EQ (tree.size (), size_t (0)); + EXPECT_EQ (tree.levels (), size_t (1)); +} + TEST(clear) { my_quad_tree tree; @@ -391,3 +445,33 @@ TEST(move_ctor) EXPECT_EQ (tree2.levels (), size_t (2)); } +static double rvalue () +{ + return ((rand () % 1000000) - 5000) * 0.001; +} + +static db::DBox rbox () +{ + return db::DBox (db::DPoint (rvalue (), rvalue ()), db::DPoint (rvalue (), rvalue ())); +} + +TEST(many) +{ + return; // @@@ + my_quad_tree tree; + + unsigned int n = 1000; + + for (unsigned int i = 0; i < n; ++i) { + tree.insert (rbox ()); + } + + EXPECT_EQ (tree.check (), true); + + bool r = true; + while (r && ! tree.empty ()) { + r = tree.erase (*tree.begin ()); + EXPECT_EQ (r, true); + EXPECT_EQ (tree.check (), true); + } +} From 4369835e8b641f82d426fda584efc667b157a643 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Mar 2025 16:36:49 +0100 Subject: [PATCH 068/392] WIP (quad tree) --- src/db/db/dbQuadTree.h | 55 ++++++++++++----- src/db/unit_tests/dbQuadTreeTests.cc | 88 ++++++++++++++++------------ 2 files changed, 90 insertions(+), 53 deletions(-) diff --git a/src/db/db/dbQuadTree.h b/src/db/db/dbQuadTree.h index 546c007ff..31034827f 100644 --- a/src/db/db/dbQuadTree.h +++ b/src/db/db/dbQuadTree.h @@ -30,7 +30,7 @@ namespace db { -template +template class quad_tree_node { public: @@ -137,7 +137,7 @@ public: if (! m_split || n < 0) { for (auto i = m_objects.begin (); i != m_objects.end (); ++i) { - if (*i == value) { + if (CMP () (*i, value)) { m_objects.erase (i); return true; } @@ -290,7 +290,7 @@ private: } box_type b = BC () (value); - if (b.inside (box (ucenter))) { + if (inside (b, box (ucenter))) { int n = quad_for (b); if (n < 0) { @@ -337,7 +337,7 @@ private: coord_type dx = std::max (std::abs (total_box.left () - m_center.x ()), std::abs (total_box.right () - m_center.y ())); coord_type dy = std::max (std::abs (total_box.bottom () - m_center.y ()), std::abs (total_box.top () - m_center.y ())); - return m_center - vector_type (dx, dy); + return m_center - vector_type (dx * 2, dy * 2); } bool check (const point_type &ucenter) const @@ -348,10 +348,15 @@ private: for (auto i = m_objects.begin (); i != m_objects.end (); ++i) { box_type b = BC () (*i); - if (! b.inside (bq)) { + if (! inside (b, bq)) { tl::error << "Box " << b.to_string () << " not inside quad box " << bq.to_string (); result = false; } + if (coord_traits::equal (b.left (), ucenter.x ()) || coord_traits::equal (b.right (), ucenter.x ()) || + coord_traits::equal (b.bottom (), ucenter.y ()) || coord_traits::equal (b.top (), ucenter.y ())) { + tl::error << "Box " << b.to_string () << " touches center of upper-level quad " << ucenter.to_string (); + result = false; + } } if (m_split) { @@ -367,9 +372,11 @@ private: for (unsigned int n = 0; n < 4; ++n) { if (m_q[n]) { - m_q[n]->check (m_center); + if (! m_q[n]->check (m_center)) { + result = false; + } box_type bbq = m_q[n]->box (m_center); - if (bbq != q (n, ucenter)) { + if (! bbq.equal (q (n, ucenter))) { tl::error << "Quad not centered (quad box is " << bbq.to_string () << ", should be " << q (n, ucenter).to_string (); result = false; } @@ -394,13 +401,23 @@ private: return result; } + + static bool inside (const box_type &box, const box_type &in) + { + if (box.empty () || in.empty ()) { + return false; + } else { + return ! coord_traits::less (box.left (), in.left ()) && ! coord_traits::less (in.right (), box.right ()) && + ! coord_traits::less (box.bottom (), in.bottom ()) && ! coord_traits::less (in.top (), box.top ()); + } + } }; -template +template class quad_tree_iterator { public: - typedef quad_tree_node quad_tree_node_type; + typedef quad_tree_node quad_tree_node_type; typedef typename T::coord_type coord_type; typedef db::box box_type; @@ -575,15 +592,23 @@ private: box_type m_box; }; -// @@@ TODO: copy, assignment, move, swap -template +template +struct quad_tree_default_cmp +{ + bool operator() (const T &a, const T &b) const + { + return a == b; + } +}; + +template > class quad_tree { public: - typedef quad_tree_node quad_tree_node_type; - typedef quad_tree_iterator > quad_tree_flat_iterator; - typedef quad_tree_iterator > quad_tree_touching_iterator; - typedef quad_tree_iterator > quad_tree_overlapping_iterator; + typedef quad_tree_node quad_tree_node_type; + typedef quad_tree_iterator > quad_tree_flat_iterator; + typedef quad_tree_iterator > quad_tree_touching_iterator; + typedef quad_tree_iterator > quad_tree_overlapping_iterator; typedef typename T::coord_type coord_type; typedef db::box box_type; typedef db::point point_type; diff --git a/src/db/unit_tests/dbQuadTreeTests.cc b/src/db/unit_tests/dbQuadTreeTests.cc index 1866907d1..c0df73a44 100644 --- a/src/db/unit_tests/dbQuadTreeTests.cc +++ b/src/db/unit_tests/dbQuadTreeTests.cc @@ -28,9 +28,17 @@ #include -typedef db::quad_tree, size_t (1)> my_quad_tree; +struct MyQuadTreeCMP +{ + bool operator() (const db::DBox &a, const db::DBox &b) const + { + return a.equal (b); + } +}; -std::string find_all (const my_quad_tree &qt) +typedef db::quad_tree, size_t (1), MyQuadTreeCMP> MyQuadTree; + +std::string find_all (const MyQuadTree &qt) { std::vector v; auto i = qt.begin (); @@ -42,7 +50,7 @@ std::string find_all (const my_quad_tree &qt) return tl::join (v, "/"); } -std::string find_touching (const my_quad_tree &qt, const db::DBox &box) +std::string find_touching (const MyQuadTree &qt, const db::DBox &box) { std::vector v; auto i = qt.begin_touching (box); @@ -54,7 +62,7 @@ std::string find_touching (const my_quad_tree &qt, const db::DBox &box) return tl::join (v, "/"); } -std::string find_touching_from_all (const my_quad_tree &qt, const db::DBox &box) +std::string find_touching_from_all (const MyQuadTree &qt, const db::DBox &box) { std::vector v; auto i = qt.begin (); @@ -68,7 +76,7 @@ std::string find_touching_from_all (const my_quad_tree &qt, const db::DBox &box) return tl::join (v, "/"); } -std::string find_overlapping (const my_quad_tree &qt, const db::DBox &box) +std::string find_overlapping (const MyQuadTree &qt, const db::DBox &box) { std::vector v; auto i = qt.begin_overlapping (box); @@ -80,7 +88,7 @@ std::string find_overlapping (const my_quad_tree &qt, const db::DBox &box) return tl::join (v, "/"); } -std::string find_overlapping_from_all (const my_quad_tree &qt, const db::DBox &box) +std::string find_overlapping_from_all (const MyQuadTree &qt, const db::DBox &box) { std::vector v; auto i = qt.begin (); @@ -96,7 +104,7 @@ std::string find_overlapping_from_all (const my_quad_tree &qt, const db::DBox &b TEST(basic) { - my_quad_tree tree; + MyQuadTree tree; EXPECT_EQ (tree.empty (), true); EXPECT_EQ (tree.size (), size_t (0)); EXPECT_EQ (tree.check (), true); @@ -174,7 +182,7 @@ TEST(basic) EXPECT_EQ (tree.empty (), false); EXPECT_EQ (tree.size (), size_t (3)); EXPECT_EQ (tree.check (), true); - EXPECT_EQ (tree.levels (), size_t (2)); + EXPECT_EQ (tree.levels (), size_t (3)); EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;3,0)"); @@ -204,7 +212,7 @@ TEST(basic) EXPECT_EQ (tree.empty (), false); EXPECT_EQ (tree.size (), size_t (4)); EXPECT_EQ (tree.check (), true); - EXPECT_EQ (tree.levels (), size_t (2)); + EXPECT_EQ (tree.levels (), size_t (3)); EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); @@ -233,7 +241,7 @@ TEST(basic) TEST(remove) { - my_quad_tree tree; + MyQuadTree tree; tree.insert (db::DBox (-1, -2, 3, 4)); tree.insert (db::DBox (-1, -3, 3, 0)); tree.insert (db::DBox (-1, -3, -0.5, -2)); @@ -260,12 +268,12 @@ TEST(remove) TEST(grow) { - my_quad_tree tree; + MyQuadTree tree; tree.insert (db::DBox (-1, -2, 3, 4)); tree.insert (db::DBox (-1, -3, 3, 0)); tree.insert (db::DBox (-1, -3, -0.5, -2)); tree.insert (db::DBox (-1, -3, -0.5, 2)); - EXPECT_EQ (tree.levels (), size_t (2)); + EXPECT_EQ (tree.levels (), size_t (3)); tree.insert (db::DBox (-100, -3, -99, 2)); EXPECT_EQ (tree.levels (), size_t (8)); @@ -286,12 +294,12 @@ TEST(grow) TEST(grow2) { - my_quad_tree tree; + MyQuadTree tree; tree.insert (db::DBox (-1, -2, 3, 4)); tree.insert (db::DBox (-1, -3, 3, 0)); tree.insert (db::DBox (-1, -3, -0.5, -2)); tree.insert (db::DBox (-1, -3, -0.5, 2)); - EXPECT_EQ (tree.levels (), size_t (2)); + EXPECT_EQ (tree.levels (), size_t (3)); tree.insert (db::DBox (-100, -3, -99, -1)); EXPECT_EQ (tree.levels (), size_t (8)); @@ -312,7 +320,7 @@ TEST(grow2) TEST(clear) { - my_quad_tree tree; + MyQuadTree tree; tree.insert (db::DBox (-1, -2, 3, 4)); tree.insert (db::DBox (-1, -3, 3, 0)); tree.insert (db::DBox (-1, -3, -0.5, -2)); @@ -332,7 +340,7 @@ TEST(clear) TEST(copy) { - my_quad_tree tree; + MyQuadTree tree; tree.insert (db::DBox (-1, -2, 3, 4)); tree.insert (db::DBox (-1, -3, 3, 0)); tree.insert (db::DBox (-1, -3, -0.5, -2)); @@ -340,19 +348,19 @@ TEST(copy) EXPECT_EQ (tree.check (), true); EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); - EXPECT_EQ (tree.levels (), size_t (2)); + EXPECT_EQ (tree.levels (), size_t (3)); - my_quad_tree tree2 (tree); + MyQuadTree tree2 (tree); tree.clear (); EXPECT_EQ (tree2.check (), true); EXPECT_EQ (find_all (tree2), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); - EXPECT_EQ (tree2.levels (), size_t (2)); + EXPECT_EQ (tree2.levels (), size_t (3)); } TEST(assign) { - my_quad_tree tree; + MyQuadTree tree; tree.insert (db::DBox (-1, -2, 3, 4)); tree.insert (db::DBox (-1, -3, 3, 0)); tree.insert (db::DBox (-1, -3, -0.5, -2)); @@ -360,20 +368,20 @@ TEST(assign) EXPECT_EQ (tree.check (), true); EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); - EXPECT_EQ (tree.levels (), size_t (2)); + EXPECT_EQ (tree.levels (), size_t (3)); - my_quad_tree tree2; + MyQuadTree tree2; tree2 = tree; tree.clear (); EXPECT_EQ (tree2.check (), true); EXPECT_EQ (find_all (tree2), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); - EXPECT_EQ (tree2.levels (), size_t (2)); + EXPECT_EQ (tree2.levels (), size_t (3)); } TEST(swap) { - my_quad_tree tree; + MyQuadTree tree; tree.insert (db::DBox (-1, -2, 3, 4)); tree.insert (db::DBox (-1, -3, 3, 0)); tree.insert (db::DBox (-1, -3, -0.5, -2)); @@ -381,9 +389,9 @@ TEST(swap) EXPECT_EQ (tree.check (), true); EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); - EXPECT_EQ (tree.levels (), size_t (2)); + EXPECT_EQ (tree.levels (), size_t (3)); - my_quad_tree tree2; + MyQuadTree tree2; tree2.swap (tree); EXPECT_EQ (tree.check (), true); @@ -393,12 +401,12 @@ TEST(swap) EXPECT_EQ (tree2.check (), true); EXPECT_EQ (find_all (tree2), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); - EXPECT_EQ (tree2.levels (), size_t (2)); + EXPECT_EQ (tree2.levels (), size_t (3)); } TEST(move) { - my_quad_tree tree; + MyQuadTree tree; tree.insert (db::DBox (-1, -2, 3, 4)); tree.insert (db::DBox (-1, -3, 3, 0)); tree.insert (db::DBox (-1, -3, -0.5, -2)); @@ -406,9 +414,9 @@ TEST(move) EXPECT_EQ (tree.check (), true); EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); - EXPECT_EQ (tree.levels (), size_t (2)); + EXPECT_EQ (tree.levels (), size_t (3)); - my_quad_tree tree2; + MyQuadTree tree2; tree2 = std::move (tree); EXPECT_EQ (tree.check (), true); @@ -418,12 +426,12 @@ TEST(move) EXPECT_EQ (tree2.check (), true); EXPECT_EQ (find_all (tree2), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); - EXPECT_EQ (tree2.levels (), size_t (2)); + EXPECT_EQ (tree2.levels (), size_t (3)); } TEST(move_ctor) { - my_quad_tree tree; + MyQuadTree tree; tree.insert (db::DBox (-1, -2, 3, 4)); tree.insert (db::DBox (-1, -3, 3, 0)); tree.insert (db::DBox (-1, -3, -0.5, -2)); @@ -431,9 +439,9 @@ TEST(move_ctor) EXPECT_EQ (tree.check (), true); EXPECT_EQ (find_all (tree), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); - EXPECT_EQ (tree.levels (), size_t (2)); + EXPECT_EQ (tree.levels (), size_t (3)); - my_quad_tree tree2 (std::move (tree)); + MyQuadTree tree2 (std::move (tree)); EXPECT_EQ (tree.check (), true); EXPECT_EQ (tree.empty (), true); @@ -442,7 +450,7 @@ TEST(move_ctor) EXPECT_EQ (tree2.check (), true); EXPECT_EQ (find_all (tree2), "(-1,-2;3,4)/(-1,-3;-0.5,-2)/(-1,-3;-0.5,2)/(-1,-3;3,0)"); - EXPECT_EQ (tree2.levels (), size_t (2)); + EXPECT_EQ (tree2.levels (), size_t (3)); } static double rvalue () @@ -452,13 +460,16 @@ static double rvalue () static db::DBox rbox () { - return db::DBox (db::DPoint (rvalue (), rvalue ()), db::DPoint (rvalue (), rvalue ())); + db::DBox box; + while ((box = db::DBox (db::DPoint (rvalue (), rvalue ()), db::DPoint (rvalue (), rvalue ()))).empty ()) { + ; + } + return box; } TEST(many) { - return; // @@@ - my_quad_tree tree; + MyQuadTree tree; unsigned int n = 1000; @@ -467,6 +478,7 @@ TEST(many) } EXPECT_EQ (tree.check (), true); + EXPECT_EQ (tree.size (), size_t (n)); bool r = true; while (r && ! tree.empty ()) { From 477e2b5a31fd93b62426d1eca01de6576b5a1ae5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Mar 2025 16:43:56 +0100 Subject: [PATCH 069/392] WIP (quad tree) --- src/db/unit_tests/dbQuadTreeTests.cc | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/db/unit_tests/dbQuadTreeTests.cc b/src/db/unit_tests/dbQuadTreeTests.cc index c0df73a44..26c06c196 100644 --- a/src/db/unit_tests/dbQuadTreeTests.cc +++ b/src/db/unit_tests/dbQuadTreeTests.cc @@ -50,7 +50,7 @@ std::string find_all (const MyQuadTree &qt) return tl::join (v, "/"); } -std::string find_touching (const MyQuadTree &qt, const db::DBox &box) +std::string find_touching (const MyQuadTree &qt, const db::DBox &box, bool report = false) { std::vector v; auto i = qt.begin_touching (box); @@ -58,6 +58,9 @@ std::string find_touching (const MyQuadTree &qt, const db::DBox &box) v.push_back (i->to_string ()); ++i; } + if (report) { + tl::info << v.size () << " items found."; + } std::sort (v.begin (), v.end ()); return tl::join (v, "/"); } @@ -76,7 +79,7 @@ std::string find_touching_from_all (const MyQuadTree &qt, const db::DBox &box) return tl::join (v, "/"); } -std::string find_overlapping (const MyQuadTree &qt, const db::DBox &box) +std::string find_overlapping (const MyQuadTree &qt, const db::DBox &box, bool report = false) { std::vector v; auto i = qt.begin_overlapping (box); @@ -84,6 +87,9 @@ std::string find_overlapping (const MyQuadTree &qt, const db::DBox &box) v.push_back (i->to_string ()); ++i; } + if (report) { + tl::info << v.size () << " items found."; + } std::sort (v.begin (), v.end ()); return tl::join (v, "/"); } @@ -472,6 +478,7 @@ TEST(many) MyQuadTree tree; unsigned int n = 1000; + unsigned int ntests = 100; for (unsigned int i = 0; i < n; ++i) { tree.insert (rbox ()); @@ -480,10 +487,27 @@ TEST(many) EXPECT_EQ (tree.check (), true); EXPECT_EQ (tree.size (), size_t (n)); + bool report = false; + for (unsigned int i = 0; i < ntests; ++i) { + if (report) { + tl::info << "Test iteration " << i << " ..."; + } + auto bx = rbox (); + EXPECT_EQ (find_overlapping (tree, bx, report), find_overlapping_from_all (tree, bx)); + EXPECT_EQ (find_touching (tree, bx, report), find_touching_from_all (tree, bx)); + bx = db::DBox (bx.center (), bx.center ()); + EXPECT_EQ (find_touching (tree, bx, report), find_touching_from_all (tree, bx)); + } + bool r = true; while (r && ! tree.empty ()) { r = tree.erase (*tree.begin ()); EXPECT_EQ (r, true); EXPECT_EQ (tree.check (), true); } + + EXPECT_EQ (tree.empty (), true); + EXPECT_EQ (tree.check (), true); + EXPECT_EQ (tree.levels (), size_t (1)); + EXPECT_EQ (tree.size (), size_t (0)); } From 4aeb94d42e70717bc7269da592ebf03e12ce798a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Mar 2025 17:13:06 +0100 Subject: [PATCH 070/392] WIP (quad tree) --- src/db/unit_tests/dbQuadTreeTests.cc | 78 ++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/db/unit_tests/dbQuadTreeTests.cc b/src/db/unit_tests/dbQuadTreeTests.cc index 26c06c196..219f8dc20 100644 --- a/src/db/unit_tests/dbQuadTreeTests.cc +++ b/src/db/unit_tests/dbQuadTreeTests.cc @@ -25,6 +25,7 @@ #include "dbBoxConvert.h" #include "tlUnitTest.h" #include "tlString.h" +#include "tlTimer.h" #include @@ -473,6 +474,13 @@ static db::DBox rbox () return box; } +static db::DBox rbox (double dim) +{ + db::DBox box; + db::DPoint c (rvalue (), rvalue ()); + return box = db::DBox (c, c).enlarged (db::DVector (dim * 0.5, dim * 0.5)); +} + TEST(many) { MyQuadTree tree; @@ -511,3 +519,73 @@ TEST(many) EXPECT_EQ (tree.levels (), size_t (1)); EXPECT_EQ (tree.size (), size_t (0)); } + +TEST(timing_insert) +{ + MyQuadTree tree; + + { + unsigned int n = 1000000; + tl::SelfTimer timer (tl::sprintf ("%d inserts ..", int (n))); + for (unsigned int i = 0; i < n; ++i) { + tree.insert (rbox ()); + } + } + + tree.clear (); + + { + unsigned int n = 2000000; + tl::SelfTimer timer (tl::sprintf ("%d inserts ..", int (n))); + for (unsigned int i = 0; i < n; ++i) { + tree.insert (rbox ()); + } + } +} + +TEST(timing_lookup) +{ + MyQuadTree tree; + + unsigned int n = 250000; + for (unsigned int i = 0; i < n; ++i) { + tree.insert (rbox (10.0)); + } + + unsigned int ntests = 100; + + std::vector > > tests; + for (unsigned int i = 0; i < ntests; ++i) { + db::DBox bx = rbox (10.0); + tests.push_back (std::make_pair (bx, std::make_pair (size_t (0), size_t (0)))); + } + + { + tl::SelfTimer timer (tl::sprintf ("%d tests (lookup) ..", int (ntests))); + for (auto t = tests.begin (); t != tests.end (); ++t) { + size_t n = 0; + for (auto i = tree.begin_touching (t->first); ! i.at_end (); ++i) { + ++n; + } + t->second.first = n; + } + } + + { + tl::SelfTimer timer (tl::sprintf ("%d tests (brute force) ..", int (ntests))); + for (auto t = tests.begin (); t != tests.end (); ++t) { + size_t n = 0; + for (auto i = tree.begin (); ! i.at_end (); ++i) { + if (i->touches (t->first)) { + ++n; + } + } + t->second.second = n; + } + } + + for (auto t = tests.begin (); t != tests.end (); ++t) { + EXPECT_EQ (t->second.first, t->second.second); + } +} + From f3037d11f3a771e16f4e65bfd45630194b7ec424 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Mar 2025 18:16:15 +0100 Subject: [PATCH 071/392] WIP (quad tree) --- src/db/db/dbQuadTree.h | 190 ++++++++++++++++++++------- src/db/unit_tests/dbQuadTreeTests.cc | 10 +- 2 files changed, 151 insertions(+), 49 deletions(-) diff --git a/src/db/db/dbQuadTree.h b/src/db/db/dbQuadTree.h index 31034827f..453ea78b8 100644 --- a/src/db/db/dbQuadTree.h +++ b/src/db/db/dbQuadTree.h @@ -30,6 +30,9 @@ namespace db { +/** + * @brief The quad tree node implementation class + */ template class quad_tree_node { @@ -42,9 +45,9 @@ public: typedef db::coord_traits coord_traits; quad_tree_node (const point_type ¢er) - : m_split (false), m_center (center) + : m_center (center) { - init (); + init (true); } ~quad_tree_node () @@ -53,9 +56,9 @@ public: } quad_tree_node (const quad_tree_node &other) - : m_split (false), m_center (center) + : m_center (center) { - init (); + init (true); operator= (other); } @@ -63,12 +66,14 @@ public: { if (this != &other) { clear (); - m_split = other.m_split; m_center = other.m_center; m_objects = other.m_objects; - for (unsigned int i = 0; i < 4; ++i) { - if (other.m_q[i]) { - m_q[i] = other.m_q[i]->clone (); + if (! other.is_leaf ()) { + init (false); + for (unsigned int i = 0; i < 4; ++i) { + if (other.m_q[i]) { + m_q[i] = other.m_q[i]->clone (); + } } } } @@ -77,7 +82,7 @@ public: quad_tree_node (quad_tree_node &&other) { - init (); + init (true); swap (other); } @@ -91,7 +96,6 @@ public: { if (this != &other) { std::swap (m_center, other.m_center); - std::swap (m_split, other.m_split); m_objects.swap (other.m_objects); for (unsigned int i = 0; i < 4; ++i) { std::swap (m_q[i], other.m_q[i]); @@ -109,12 +113,14 @@ public: void clear () { m_objects.clear (); - m_split = false; - for (unsigned int i = 0; i < 4; ++i) { - if (m_q[i]) { - delete m_q[i]; + if (! is_leaf ()) { + for (unsigned int i = 0; i < 4; ++i) { + if (m_q[i]) { + delete m_q[i]; + } + m_q[i] = 0; } - m_q[i] = 0; + init (true); } } @@ -134,7 +140,7 @@ public: int n = quad_for (b); - if (! m_split || n < 0) { + if (is_leaf () || n < 0) { for (auto i = m_objects.begin (); i != m_objects.end (); ++i) { if (CMP () (*i, value)) { @@ -165,7 +171,7 @@ public: box_type q_box (unsigned int n) const { - if (m_q[n]) { + if (! is_leaf () && m_q[n]) { return m_q[n]->box (m_center); } else { return box_type (); @@ -174,15 +180,18 @@ public: quad_tree_node *node (unsigned int n) const { + tl_assert (! is_leaf ()); return m_q [n]; } bool empty () const { if (m_objects.empty ()) { - for (unsigned int n = 0; n < 4; ++n) { - if (m_q[n] && ! m_q[n]->empty ()) { - return false; + if (! is_leaf ()) { + for (unsigned int n = 0; n < 4; ++n) { + if (m_q[n] && ! m_q[n]->empty ()) { + return false; + } } } return true; @@ -194,9 +203,11 @@ public: size_t size () const { size_t count = m_objects.size (); - for (unsigned int n = 0; n < 4; ++n) { - if (m_q[n]) { - count += m_q[n]->size (); + if (! is_leaf ()) { + for (unsigned int n = 0; n < 4; ++n) { + if (m_q[n]) { + count += m_q[n]->size (); + } } } return count; @@ -205,9 +216,11 @@ public: size_t levels () const { size_t l = 1; - for (unsigned int n = 0; n < 4; ++n) { - if (m_q[n]) { - l = std::max (l, m_q[n]->levels () + 1); + if (! is_leaf ()) { + for (unsigned int n = 0; n < 4; ++n) { + if (m_q[n]) { + l = std::max (l, m_q[n]->levels () + 1); + } } } return l; @@ -219,16 +232,23 @@ public: } private: - bool m_split; point_type m_center; quad_tree_node *m_q [4]; objects_vector m_objects; - void init () + void init (bool is_leaf) { for (unsigned int i = 0; i < 4; ++i) { m_q[i] = 0; } + if (is_leaf) { + m_q[0] = reinterpret_cast (1); + } + } + + bool is_leaf () const + { + return m_q[0] == reinterpret_cast (1); } int quad_for (const box_type &box) const @@ -267,7 +287,7 @@ private: void split (const point_type &ucenter) { - m_split = true; + init (false); objects_vector ov; ov.swap (m_objects); @@ -279,13 +299,13 @@ private: void insert (const T &value, const point_type &ucenter) { - if (! m_split && m_objects.size () + 1 < thr) { + if (is_leaf () && m_objects.size () + 1 < thr) { m_objects.push_back (value); } else { - if (! m_split) { + if (is_leaf ()) { split (ucenter); } @@ -321,7 +341,7 @@ private: if (m_q[i]) { quad_tree_node *n = m_q[i]; m_q[i] = new quad_tree_node (q (i, ucenter).center ()); - m_q[i]->m_split = true; + m_q[i]->init (false); m_q[i]->m_q[3 - i] = n; } } @@ -329,9 +349,11 @@ private: point_type propose_ucenter (const box_type &total_box) const { - for (unsigned int i = 0; i < 4; ++i) { - if (m_q[i]) { - return m_center - (m_center - m_q[i]->center ()) * 2.0; + if (! is_leaf ()) { + for (unsigned int i = 0; i < 4; ++i) { + if (m_q[i]) { + return m_center - (m_center - m_q[i]->center ()) * 2.0; + } } } @@ -359,7 +381,7 @@ private: } } - if (m_split) { + if (! is_leaf ()) { for (auto i = m_objects.begin (); i != m_objects.end (); ++i) { box_type b = BC () (*i); @@ -390,13 +412,6 @@ private: result = false; } - for (unsigned int n = 0; n < 4; ++n) { - if (m_q[n]) { - tl::error << "Non-split node has child nodes"; - result = false; - } - } - } return result; @@ -413,6 +428,9 @@ private: } }; +/** + * @brief The iterator implementation class + */ template class quad_tree_iterator { @@ -510,6 +528,9 @@ public: } }; +/** + * @brief The selector for implementing the all-iterator + */ template class quad_tree_always_sel { @@ -528,6 +549,9 @@ public: } }; +/** + * @brief The selector for implementing the touching iterator + */ template class quad_tree_touching_sel { @@ -560,6 +584,9 @@ private: box_type m_box; }; +/** + * @brief The selector for implementing the overlapping iterator + */ template class quad_tree_overlapping_sel { @@ -592,6 +619,9 @@ private: box_type m_box; }; +/** + * @brief The default compare function + */ template struct quad_tree_default_cmp { @@ -601,7 +631,22 @@ struct quad_tree_default_cmp } }; -template > +/** + * @brief A generic quad tree implementation + * + * In contrast to the box_tree implementation, this is a self-sorting implementation + * which is more generic. + * + * @param T The value to be stored + * @param BC The box converter + * @param thr The number of items per leaf node before splitting + * @param CMP The compare function (equality) + * + * T needs to have a type member named "coord_type". + * BC is a function of T and delivers a db::box for T + * CMP is a function that delivers a bool value for a pair of T (equality) + */ +template > class quad_tree { public: @@ -615,18 +660,29 @@ public: typedef db::vector vector_type; typedef std::vector objects_vector; + /** + * @brief Default constructor + * + * This creates an empty tree. + */ quad_tree () : m_root (point_type ()) { // .. nothing yet .. } + /** + * @brief Copy constructor + */ quad_tree (const quad_tree &other) : m_root (point_type ()) { operator= (other); } + /** + * @brief Assignment + */ quad_tree &operator= (const quad_tree &other) { if (this != &other) { @@ -636,52 +692,79 @@ public: return *this; } + /** + * @brief Move constructor + */ quad_tree (quad_tree &&other) : m_root (point_type ()) { swap (other); } + /** + * @brief Move assignment + */ quad_tree &operator= (quad_tree &&other) { swap (other); return *this; } + /** + * @brief Empties the tree + */ void clear () { m_root.clear (); m_total_box = box_type (); } + /** + * @brief Swaps the tree with another + */ void swap (quad_tree &other) { if (this != &other) { m_root.swap (other.m_root); - std::swap (m_total_box, m_total_box); + std::swap (m_total_box, other.m_total_box); } } + /** + * @brief Returns a value indicating whether the tree is empty + */ bool empty () const { return m_root.empty (); } + /** + * @brief Returns the number of items stored in the tree + */ size_t size () const { return m_root.size (); } + /** + * @brief Returns the number of quad levels (for testing) + */ size_t levels () const { return m_root.levels (); } + /** + * @brief Checks the tree for consistency (for testing) + */ bool check () const { return m_root.check_top (m_total_box); } + /** + * @brief Inserts an object into the tree + */ void insert (const T &value) { box_type b = BC () (value); @@ -693,16 +776,30 @@ public: m_root.insert_top (value, m_total_box); } + /** + * @brief Erases the given element from the tree + * + * @return true, if the element was found and erased. + * + * If multiple elements of the same kind are stored, the + * first one is erased. + */ bool erase (const T &value) { return m_root.erase (value); } + /** + * @brief begin iterator for all elements + */ quad_tree_flat_iterator begin () const { return quad_tree_flat_iterator (&m_root, quad_tree_always_sel ()); } + /** + * @brief begin iterator for all elements overlapping the given box + */ quad_tree_overlapping_iterator begin_overlapping (const box_type &box) const { if (m_total_box.overlaps (box)) { @@ -712,6 +809,9 @@ public: } } + /** + * @brief begin iterator for all elements touching the given box + */ quad_tree_touching_iterator begin_touching (const box_type &box) const { if (m_total_box.touches (box)) { diff --git a/src/db/unit_tests/dbQuadTreeTests.cc b/src/db/unit_tests/dbQuadTreeTests.cc index 219f8dc20..8ffe12327 100644 --- a/src/db/unit_tests/dbQuadTreeTests.cc +++ b/src/db/unit_tests/dbQuadTreeTests.cc @@ -530,6 +530,7 @@ TEST(timing_insert) for (unsigned int i = 0; i < n; ++i) { tree.insert (rbox ()); } + tl::info << "Quad levels: " << tree.levels (); } tree.clear (); @@ -540,6 +541,7 @@ TEST(timing_insert) for (unsigned int i = 0; i < n; ++i) { tree.insert (rbox ()); } + tl::info << "Quad levels: " << tree.levels (); } } @@ -547,16 +549,16 @@ TEST(timing_lookup) { MyQuadTree tree; - unsigned int n = 250000; + unsigned int n = 1000000; for (unsigned int i = 0; i < n; ++i) { - tree.insert (rbox (10.0)); + tree.insert (rbox (5.0)); } - unsigned int ntests = 100; + unsigned int ntests = 1000; std::vector > > tests; for (unsigned int i = 0; i < ntests; ++i) { - db::DBox bx = rbox (10.0); + db::DBox bx = rbox (5.0); tests.push_back (std::make_pair (bx, std::make_pair (size_t (0), size_t (0)))); } From 4e65b96cb769156e81dd12f90a893a029cd38eb9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Mar 2025 21:10:15 +0100 Subject: [PATCH 072/392] WIP (quad tree) --- src/db/db/dbQuadTree.h | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/db/db/dbQuadTree.h b/src/db/db/dbQuadTree.h index 453ea78b8..d17ee1af7 100644 --- a/src/db/db/dbQuadTree.h +++ b/src/db/db/dbQuadTree.h @@ -37,12 +37,11 @@ template class quad_tree_node { public: - typedef typename T::coord_type coord_type; - typedef db::box box_type; - typedef db::point point_type; - typedef db::vector vector_type; + typedef typename BC::box_type box_type; + typedef typename box_type::point_type point_type; + typedef typename box_type::vector_type vector_type; typedef std::vector objects_vector; - typedef db::coord_traits coord_traits; + typedef db::coord_traits coord_traits; quad_tree_node (const point_type ¢er) : m_center (center) @@ -357,8 +356,8 @@ private: } } - coord_type dx = std::max (std::abs (total_box.left () - m_center.x ()), std::abs (total_box.right () - m_center.y ())); - coord_type dy = std::max (std::abs (total_box.bottom () - m_center.y ()), std::abs (total_box.top () - m_center.y ())); + typename box_type::coord_type dx = std::max (std::abs (total_box.left () - m_center.x ()), std::abs (total_box.right () - m_center.y ())); + typename box_type::coord_type dy = std::max (std::abs (total_box.bottom () - m_center.y ()), std::abs (total_box.top () - m_center.y ())); return m_center - vector_type (dx * 2, dy * 2); } @@ -436,8 +435,7 @@ class quad_tree_iterator { public: typedef quad_tree_node quad_tree_node_type; - typedef typename T::coord_type coord_type; - typedef db::box box_type; + typedef typename BC::box_type box_type; quad_tree_iterator () : m_s (), m_i (0) @@ -535,8 +533,7 @@ template class quad_tree_always_sel { public: - typedef typename T::coord_type coord_type; - typedef db::box box_type; + typedef typename BC::box_type box_type; bool select (const T &) const { @@ -556,8 +553,7 @@ template class quad_tree_touching_sel { public: - typedef typename T::coord_type coord_type; - typedef db::box box_type; + typedef typename BC::box_type box_type; quad_tree_touching_sel () { @@ -591,8 +587,7 @@ template class quad_tree_overlapping_sel { public: - typedef typename T::coord_type coord_type; - typedef db::box box_type; + typedef typename BC::box_type box_type; quad_tree_overlapping_sel () { @@ -654,10 +649,9 @@ public: typedef quad_tree_iterator > quad_tree_flat_iterator; typedef quad_tree_iterator > quad_tree_touching_iterator; typedef quad_tree_iterator > quad_tree_overlapping_iterator; - typedef typename T::coord_type coord_type; - typedef db::box box_type; - typedef db::point point_type; - typedef db::vector vector_type; + typedef typename BC::box_type box_type; + typedef typename box_type::point_type point_type; + typedef typename box_type::vector_type vector_type; typedef std::vector objects_vector; /** From cd62f62140c1dfeeb0b7c4bcac52163164302a6d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Mar 2025 23:24:06 +0100 Subject: [PATCH 073/392] WIP (quad tree) --- src/db/db/dbQuadTree.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/db/db/dbQuadTree.h b/src/db/db/dbQuadTree.h index d17ee1af7..7cc9a13dc 100644 --- a/src/db/db/dbQuadTree.h +++ b/src/db/db/dbQuadTree.h @@ -373,9 +373,12 @@ private: tl::error << "Box " << b.to_string () << " not inside quad box " << bq.to_string (); result = false; } + point_type ucenter_opp = m_center + (m_center - ucenter); if (coord_traits::equal (b.left (), ucenter.x ()) || coord_traits::equal (b.right (), ucenter.x ()) || - coord_traits::equal (b.bottom (), ucenter.y ()) || coord_traits::equal (b.top (), ucenter.y ())) { - tl::error << "Box " << b.to_string () << " touches center of upper-level quad " << ucenter.to_string (); + coord_traits::equal (b.bottom (), ucenter.y ()) || coord_traits::equal (b.top (), ucenter.y ()) || + coord_traits::equal (b.left (), ucenter_opp.x ()) || coord_traits::equal (b.right (), ucenter_opp.x ()) || + coord_traits::equal (b.bottom (), ucenter_opp.y ()) || coord_traits::equal (b.top (), ucenter_opp.y ())) { + tl::error << "Box " << b.to_string () << " touches quad boundary " << ucenter.to_string () << " .. " << ucenter_opp.to_string (); result = false; } } @@ -421,8 +424,8 @@ private: if (box.empty () || in.empty ()) { return false; } else { - return ! coord_traits::less (box.left (), in.left ()) && ! coord_traits::less (in.right (), box.right ()) && - ! coord_traits::less (box.bottom (), in.bottom ()) && ! coord_traits::less (in.top (), box.top ()); + return coord_traits::less (in.left (), box.left ()) && coord_traits::less (box.right (), in.right ()) && + coord_traits::less (in.bottom (), box.bottom ()) && coord_traits::less (box.top (), in.top ()); } } }; From 6596008826580c8013adb9cacd2146a4f3393b36 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Mar 2025 00:10:00 +0100 Subject: [PATCH 074/392] instrumenting triangles implementation with Quad Tree, but without effect --- src/db/db/dbTriangles.cc | 120 ++++++++++++++++++++------ src/db/db/dbTriangles.h | 14 ++- src/db/unit_tests/dbTrianglesTests.cc | 1 + 3 files changed, 106 insertions(+), 29 deletions(-) diff --git a/src/db/db/dbTriangles.cc b/src/db/db/dbTriangles.cc index 5a0475a5d..b325579c9 100644 --- a/src/db/db/dbTriangles.cc +++ b/src/db/db/dbTriangles.cc @@ -44,9 +44,7 @@ Triangles::Triangles () Triangles::~Triangles () { - while (! mp_triangles.empty ()) { - remove_triangle (mp_triangles.begin ().operator-> ()); - } + clear (); } db::Vertex * @@ -88,6 +86,19 @@ Triangles::create_triangle (TriangleEdge *e1, TriangleEdge *e2, TriangleEdge *e3 db::Triangle *res = new db::Triangle (e1, e2, e3); res->set_id (++m_id); mp_triangles.push_back (res); + + m_triangle_qt.insert (res); + + // @@@ +#if 0 + if (mp_triangles.size () != m_triangle_qt.size ()) { + size_t a = mp_triangles.size (); + size_t b = m_triangle_qt.size (); + printf("@@@ %ld -- %ld\n", a, b); fflush(stdout); + } + tl_assert (mp_triangles.size () == m_triangle_qt.size ()); // @@@ +#endif + // @@@ return res; } @@ -99,7 +110,20 @@ Triangles::remove_triangle (db::Triangle *tri) edges [i] = tri->edge (i); } + bool removed = m_triangle_qt.erase (tri); + tl_assert (removed); + delete tri; + // @@@ +#if 0 + if (mp_triangles.size () != m_triangle_qt.size ()) { + size_t a = mp_triangles.size (); + size_t b = m_triangle_qt.size (); + printf("@@@ %ld -- %ld\n", a, b); fflush(stdout); + } + tl_assert (mp_triangles.size () == m_triangle_qt.size ()); // @@@ +#endif + // @@@ // clean up edges we do no longer need for (int i = 0; i < 3; ++i) { @@ -395,6 +419,8 @@ Triangles::insert (db::Vertex *vertex, std::list > *n std::vector Triangles::find_triangle_for_point (const db::DPoint &point) { +// @@@ +#if 1 // minimize distance search db::TriangleEdge *edge = find_closest_edge (point); std::vector res; @@ -405,7 +431,29 @@ Triangles::find_triangle_for_point (const db::DPoint &point) } } } + + // @@@ + std::set setb; + for (auto i = m_triangle_qt.begin_touching (db::DBox (point, point)); ! i.at_end (); ++i) { + if ((*i)->contains (point) >= 0) { + setb.insert (*i); + } + } + std::set seta (res.begin (), res.end ()); + if (seta != setb) { + tl_assert (false); + } + // @@@ return res; +#else + std::vector res; + for (auto i = m_triangle_qt.begin_touching (db::DBox (point, point)); ! i.at_end (); ++i) { + if ((*i)->contains (point) >= 0) { + res.push_back (*i); + } + } + return res; +#endif } db::TriangleEdge * @@ -413,24 +461,7 @@ Triangles::find_closest_edge (const db::DPoint &p, db::Vertex *vstart, bool insi { if (!vstart) { - if (m_is_constrained && ! m_initial_segments.empty ()) { - - // Use the closest initial segment for the starting point - // TODO: m_initial_segments should be some search tree - - double d = -1; - for (auto i = m_initial_segments.begin (); i != m_initial_segments.end (); ++i) { - if (db::sprod_sign (p - i->first.p1 (), i->first.d ()) >= 0 && - db::sprod_sign (p - i->first.p2 (), i->first.d ()) < 0) { - double ds = std::abs (i->first.distance (p)); - if (d < 0.0 || ds < d) { - vstart = i->second; - d = ds; - } - } - } - - } else if (! mp_triangles.empty ()) { + if (! mp_triangles.empty ()) { unsigned int ls = 0; size_t n = m_vertex_heap.size (); @@ -660,7 +691,7 @@ Triangles::split_triangle (db::Triangle *t, db::Vertex *vertex, std::list &tris, db::Vertex *vertex, db::TriangleEdge *split_edge, std::list > *new_triangles_out) +Triangles::split_triangles_on_edge (const std::vector &_tris /*@@@*/, db::Vertex *vertex, db::TriangleEdge *split_edge, std::list > *new_triangles_out) { TriangleEdge *s1 = create_edge (split_edge->v1 (), vertex); TriangleEdge *s2 = create_edge (split_edge->v2 (), vertex); @@ -669,6 +700,12 @@ Triangles::split_triangles_on_edge (const std::vector &tris, db: std::vector new_triangles; + std::vector tris; + tris.reserve (2); + for (auto t = split_edge->begin_triangles (); t != split_edge->end_triangles (); ++t) { + tris.push_back (t.operator-> ()); + } + for (auto t = tris.begin (); t != tris.end (); ++t) { (*t)->unlink (); @@ -1181,19 +1218,47 @@ Triangles::search_edges_crossing (Vertex *from, Vertex *to) } db::Vertex * -Triangles::find_vertex_for_point (const db::DPoint &pt) +Triangles::find_vertex_for_point (const db::DPoint &point) { - db::TriangleEdge *edge = find_closest_edge (pt); +// @@@ +#if 1 // minimize distance search + db::TriangleEdge *edge = find_closest_edge (point); if (!edge) { return 0; } db::Vertex *v = 0; - if (edge->v1 ()->equal (pt)) { + if (edge->v1 ()->equal (point)) { v = edge->v1 (); - } else if (edge->v2 ()->equal (pt)) { + } else if (edge->v2 ()->equal (point)) { v = edge->v2 (); } + // @@@ + db::Vertex *vv = 0; + for (auto i = m_triangle_qt.begin_touching (db::DBox (point, point)); ! i.at_end () && ! vv; ++i) { + db::Triangle *t = *i; + for (unsigned int i = 0; i < 3 && ! vv; ++i) { + if (t->vertex (i)->equal (point)) { + vv = t->vertex (i); + } + } + } + if (vv != v) { + tl_assert (false); // @@@ + } + // @@@ return v; +#else + for (auto i = m_triangle_qt.begin_touching (db::DBox (point, point)); ! i.at_end (); ++i) { + db::Triangle *t = *i; + for (unsigned int i = 0; i < 3; ++i) { + if (t->vertex (i)->equal (point)) { + return t->vertex (i); + } + } + } + return 0; +#endif +// @@@ } db::TriangleEdge * @@ -1337,7 +1402,6 @@ void Triangles::constrain (const std::vector > &contours) { tl_assert (! m_is_constrained); - m_initial_segments.clear (); std::vector > > resolved_edges; @@ -1351,7 +1415,6 @@ Triangles::constrain (const std::vector > &contours) db::DEdge e (**v, **vv); resolved_edges.push_back (std::make_pair (e, std::vector ())); resolved_edges.back ().second = ensure_edge (*v, *vv); - m_initial_segments.push_back (std::make_pair (e, *v)); } } @@ -1436,6 +1499,7 @@ void Triangles::clear () { mp_triangles.clear (); + m_triangle_qt.clear (); m_edges_heap.clear (); m_vertex_heap.clear (); m_returned_edges.clear (); diff --git a/src/db/db/dbTriangles.h b/src/db/db/dbTriangles.h index f15b2dc7a..96f342a1e 100644 --- a/src/db/db/dbTriangles.h +++ b/src/db/db/dbTriangles.h @@ -29,6 +29,7 @@ #include "dbTriangle.h" #include "dbBox.h" #include "dbRegion.h" +#include "dbQuadTree.h" #include "tlObjectCollection.h" #include "tlStableVector.h" @@ -306,12 +307,23 @@ protected: std::vector find_inside_circle (const db::DPoint ¢er, double radius) const; private: + struct TriangleBoxConvert + { + typedef db::DBox box_type; + box_type operator() (db::Triangle *t) const + { + return t ? t->bbox () : box_type (); + } + }; + + typedef db::quad_tree triangle_qt_type; + tl::list mp_triangles; + triangle_qt_type m_triangle_qt; tl::stable_vector m_edges_heap; std::vector m_returned_edges; tl::stable_vector m_vertex_heap; bool m_is_constrained; - std::vector > m_initial_segments; size_t m_level; size_t m_id; size_t m_flips, m_hops; diff --git a/src/db/unit_tests/dbTrianglesTests.cc b/src/db/unit_tests/dbTrianglesTests.cc index 9b7dd021c..c2209c3da 100644 --- a/src/db/unit_tests/dbTrianglesTests.cc +++ b/src/db/unit_tests/dbTrianglesTests.cc @@ -277,6 +277,7 @@ namespace { TEST(insert_many) { + return; // @@@ srand (0); TestableTriangles tris; From df631aa97013fe698287e2aaedbf2a18916fe60c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Mar 2025 00:19:15 +0100 Subject: [PATCH 075/392] Some minor refactoring --- src/db/db/dbQuadTree.h | 23 ++++---- src/db/db/dbTriangles.cc | 83 +-------------------------- src/db/db/dbTriangles.h | 8 +-- src/db/unit_tests/dbTrianglesTests.cc | 1 - 4 files changed, 13 insertions(+), 102 deletions(-) diff --git a/src/db/db/dbQuadTree.h b/src/db/db/dbQuadTree.h index 7cc9a13dc..45d1c0a53 100644 --- a/src/db/db/dbQuadTree.h +++ b/src/db/db/dbQuadTree.h @@ -128,15 +128,13 @@ public: return m_center; } - void insert_top (const T &value, const box_type &total_box) + void insert_top (const T &value, const box_type &total_box, const box_type &b) { - insert (value, propose_ucenter (total_box)); + insert (value, propose_ucenter (total_box), b); } - bool erase (const T &value) + bool erase (const T &value, const box_type &b) { - box_type b = BC () (value); - int n = quad_for (b); if (is_leaf () || n < 0) { @@ -150,7 +148,7 @@ public: } else if (m_q[n]) { - if (m_q[n]->erase (value)) { + if (m_q[n]->erase (value, b)) { if (m_q[n]->empty ()) { delete m_q[n]; m_q[n] = 0; @@ -292,11 +290,11 @@ private: ov.swap (m_objects); for (auto o = ov.begin (); o != ov.end (); ++o) { - insert (*o, ucenter); + insert (*o, ucenter, BC () (*o)); } } - void insert (const T &value, const point_type &ucenter) + void insert (const T &value, const point_type &ucenter, const box_type &b) { if (is_leaf () && m_objects.size () + 1 < thr) { @@ -308,7 +306,6 @@ private: split (ucenter); } - box_type b = BC () (value); if (inside (b, box (ucenter))) { int n = quad_for (b); @@ -319,7 +316,7 @@ private: box_type bq = q (n, ucenter); m_q[n] = new quad_tree_node (bq.center ()); } - m_q[n]->insert (value, m_center); + m_q[n]->insert (value, m_center, b); } } else { @@ -327,7 +324,7 @@ private: tl_assert (m_q[0] || m_q[1] || m_q[2] || m_q[3]); point_type new_ucenter = m_center - (m_center - ucenter) * 2.0; grow (new_ucenter); - insert (value, new_ucenter); + insert (value, new_ucenter, b); } @@ -770,7 +767,7 @@ public: } m_total_box += b; - m_root.insert_top (value, m_total_box); + m_root.insert_top (value, m_total_box, b); } /** @@ -783,7 +780,7 @@ public: */ bool erase (const T &value) { - return m_root.erase (value); + return m_root.erase (value, BC () (value)); } /** diff --git a/src/db/db/dbTriangles.cc b/src/db/db/dbTriangles.cc index b325579c9..246afa2e6 100644 --- a/src/db/db/dbTriangles.cc +++ b/src/db/db/dbTriangles.cc @@ -87,18 +87,6 @@ Triangles::create_triangle (TriangleEdge *e1, TriangleEdge *e2, TriangleEdge *e3 res->set_id (++m_id); mp_triangles.push_back (res); - m_triangle_qt.insert (res); - - // @@@ -#if 0 - if (mp_triangles.size () != m_triangle_qt.size ()) { - size_t a = mp_triangles.size (); - size_t b = m_triangle_qt.size (); - printf("@@@ %ld -- %ld\n", a, b); fflush(stdout); - } - tl_assert (mp_triangles.size () == m_triangle_qt.size ()); // @@@ -#endif - // @@@ return res; } @@ -110,20 +98,7 @@ Triangles::remove_triangle (db::Triangle *tri) edges [i] = tri->edge (i); } - bool removed = m_triangle_qt.erase (tri); - tl_assert (removed); - delete tri; - // @@@ -#if 0 - if (mp_triangles.size () != m_triangle_qt.size ()) { - size_t a = mp_triangles.size (); - size_t b = m_triangle_qt.size (); - printf("@@@ %ld -- %ld\n", a, b); fflush(stdout); - } - tl_assert (mp_triangles.size () == m_triangle_qt.size ()); // @@@ -#endif - // @@@ // clean up edges we do no longer need for (int i = 0; i < 3; ++i) { @@ -379,7 +354,6 @@ Triangles::insert_point (db::DCoord x, db::DCoord y, std::list > *new_triangles) { - // @@@ printf("@@@ insert %d\n", (int)mp_triangles.size ()); fflush(stdout); std::vector tris = find_triangle_for_point (*vertex); // the new vertex is outside the domain @@ -400,7 +374,7 @@ Triangles::insert (db::Vertex *vertex, std::list > *n if (! on_edges.empty ()) { if (on_edges.size () == size_t (1)) { - split_triangles_on_edge (tris, vertex, on_edges.front (), new_triangles); + split_triangles_on_edge (vertex, on_edges.front (), new_triangles); return vertex; } else { // the vertex is already present @@ -419,8 +393,6 @@ Triangles::insert (db::Vertex *vertex, std::list > *n std::vector Triangles::find_triangle_for_point (const db::DPoint &point) { -// @@@ -#if 1 // minimize distance search db::TriangleEdge *edge = find_closest_edge (point); std::vector res; @@ -432,28 +404,7 @@ Triangles::find_triangle_for_point (const db::DPoint &point) } } - // @@@ - std::set setb; - for (auto i = m_triangle_qt.begin_touching (db::DBox (point, point)); ! i.at_end (); ++i) { - if ((*i)->contains (point) >= 0) { - setb.insert (*i); - } - } - std::set seta (res.begin (), res.end ()); - if (seta != setb) { - tl_assert (false); - } - // @@@ return res; -#else - std::vector res; - for (auto i = m_triangle_qt.begin_touching (db::DBox (point, point)); ! i.at_end (); ++i) { - if ((*i)->contains (point) >= 0) { - res.push_back (*i); - } - } - return res; -#endif } db::TriangleEdge * @@ -691,7 +642,7 @@ Triangles::split_triangle (db::Triangle *t, db::Vertex *vertex, std::list &_tris /*@@@*/, db::Vertex *vertex, db::TriangleEdge *split_edge, std::list > *new_triangles_out) +Triangles::split_triangles_on_edge (db::Vertex *vertex, db::TriangleEdge *split_edge, std::list > *new_triangles_out) { TriangleEdge *s1 = create_edge (split_edge->v1 (), vertex); TriangleEdge *s2 = create_edge (split_edge->v2 (), vertex); @@ -1220,8 +1171,6 @@ Triangles::search_edges_crossing (Vertex *from, Vertex *to) db::Vertex * Triangles::find_vertex_for_point (const db::DPoint &point) { -// @@@ -#if 1 // minimize distance search db::TriangleEdge *edge = find_closest_edge (point); if (!edge) { return 0; @@ -1232,33 +1181,7 @@ Triangles::find_vertex_for_point (const db::DPoint &point) } else if (edge->v2 ()->equal (point)) { v = edge->v2 (); } - // @@@ - db::Vertex *vv = 0; - for (auto i = m_triangle_qt.begin_touching (db::DBox (point, point)); ! i.at_end () && ! vv; ++i) { - db::Triangle *t = *i; - for (unsigned int i = 0; i < 3 && ! vv; ++i) { - if (t->vertex (i)->equal (point)) { - vv = t->vertex (i); - } - } - } - if (vv != v) { - tl_assert (false); // @@@ - } - // @@@ return v; -#else - for (auto i = m_triangle_qt.begin_touching (db::DBox (point, point)); ! i.at_end (); ++i) { - db::Triangle *t = *i; - for (unsigned int i = 0; i < 3; ++i) { - if (t->vertex (i)->equal (point)) { - return t->vertex (i); - } - } - } - return 0; -#endif -// @@@ } db::TriangleEdge * @@ -1499,7 +1422,6 @@ void Triangles::clear () { mp_triangles.clear (); - m_triangle_qt.clear (); m_edges_heap.clear (); m_vertex_heap.clear (); m_returned_edges.clear (); @@ -1691,7 +1613,6 @@ Triangles::refine (const TriangulateParameters ¶meters) auto cr = (*t)->circumcircle(); auto center = cr.first; - // printf("@@@ %s -- %s\n", (*t)->to_string().c_str(), center.to_string ().c_str()); fflush(stdout); if ((*t)->contains (center) >= 0) { if (tl::verbosity () >= parameters.base_verbosity + 20) { diff --git a/src/db/db/dbTriangles.h b/src/db/db/dbTriangles.h index 96f342a1e..67c48b4c6 100644 --- a/src/db/db/dbTriangles.h +++ b/src/db/db/dbTriangles.h @@ -20,8 +20,6 @@ */ - - #ifndef HDR_dbTriangles #define HDR_dbTriangles @@ -29,7 +27,6 @@ #include "dbTriangle.h" #include "dbBox.h" #include "dbRegion.h" -#include "dbQuadTree.h" #include "tlObjectCollection.h" #include "tlStableVector.h" @@ -316,10 +313,7 @@ private: } }; - typedef db::quad_tree triangle_qt_type; - tl::list mp_triangles; - triangle_qt_type m_triangle_qt; tl::stable_vector m_edges_heap; std::vector m_returned_edges; tl::stable_vector m_vertex_heap; @@ -342,7 +336,7 @@ private: db::TriangleEdge *find_closest_edge (const db::DPoint &p, db::Vertex *vstart = 0, bool inside_only = false); db::Vertex *insert (db::Vertex *vertex, std::list > *new_triangles = 0); void split_triangle (db::Triangle *t, db::Vertex *vertex, std::list > *new_triangles_out); - void split_triangles_on_edge (const std::vector &tris, db::Vertex *vertex, db::TriangleEdge *split_edge, std::list > *new_triangles_out); + void split_triangles_on_edge (db::Vertex *vertex, db::TriangleEdge *split_edge, std::list > *new_triangles_out); void add_more_triangles (std::vector &new_triangles, db::TriangleEdge *incoming_edge, db::Vertex *from_vertex, db::Vertex *to_vertex, diff --git a/src/db/unit_tests/dbTrianglesTests.cc b/src/db/unit_tests/dbTrianglesTests.cc index c2209c3da..9b7dd021c 100644 --- a/src/db/unit_tests/dbTrianglesTests.cc +++ b/src/db/unit_tests/dbTrianglesTests.cc @@ -277,7 +277,6 @@ namespace { TEST(insert_many) { - return; // @@@ srand (0); TestableTriangles tris; From 1f5c2b5132ea14ffaff7ff712d1ccfde67062d46 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Mar 2025 14:44:27 +0100 Subject: [PATCH 076/392] Marked one test as long runner --- src/db/unit_tests/dbQuadTreeTests.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/db/unit_tests/dbQuadTreeTests.cc b/src/db/unit_tests/dbQuadTreeTests.cc index 8ffe12327..d92e07237 100644 --- a/src/db/unit_tests/dbQuadTreeTests.cc +++ b/src/db/unit_tests/dbQuadTreeTests.cc @@ -547,6 +547,8 @@ TEST(timing_insert) TEST(timing_lookup) { + test_is_long_runner (); + MyQuadTree tree; unsigned int n = 1000000; From d9343ee530ee228c978b6d974918a8cd190ac3f2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Mar 2025 17:28:12 +0100 Subject: [PATCH 077/392] WIP --- src/db/db/dbTriangle.cc | 63 +++++++++++++++++---------- src/db/db/dbTriangle.h | 4 +- src/db/db/dbTriangles.cc | 10 +++-- src/db/unit_tests/dbTrianglesTests.cc | 46 ++++++++++++++++++- 4 files changed, 92 insertions(+), 31 deletions(-) diff --git a/src/db/db/dbTriangle.cc b/src/db/db/dbTriangle.cc index 0fa3018c9..afe396181 100644 --- a/src/db/db/dbTriangle.cc +++ b/src/db/db/dbTriangle.cc @@ -436,22 +436,40 @@ Triangle::bbox () const std::pair -Triangle::circumcircle () const +Triangle::circumcircle (bool *ok) const { - db::DVector v1 = *mp_v[0] - *mp_v[1]; - db::DVector v2 = *mp_v[0] - *mp_v[2]; - db::DVector n1 = db::DVector (v1.y (), -v1.x ()); - db::DVector n2 = db::DVector (v2.y (), -v2.x ()); + // see https://en.wikipedia.org/wiki/Circumcircle + // we set A=(0,0), so the formulas simplify - double p1s = v1.sq_length (); - double p2s = v2.sq_length (); + if (ok) { + *ok = true; + } - double s = db::vprod (v1, v2); - tl_assert (fabs (s) > db::epsilon); + db::DVector b = *mp_v[1] - *mp_v[0]; + db::DVector c = *mp_v[2] - *mp_v[0]; - db::DVector r = (n1 * p2s - n2 * p1s) * (0.5 / s); - db::DPoint center = *mp_v[0] + r; - double radius = r.length (); + double b2 = b.sq_length (); + double c2 = c.sq_length (); + + double sx = 0.5 * (b2 * c.y () - c2 * b.y ()); + double sy = 0.5 * (b.x () * c2 - c.x() * b2); + + double a1 = b.x() * c.y(); + double a2 = c.x() * b.y(); + double a = a1 - a2; + double a_abs = std::abs (a); + + if (a_abs < (std::abs (a1) + std::abs (a2)) * db::epsilon) { + if (ok) { + *ok = false; + return std::make_pair (db::DPoint (), 0.0); + } else { + tl_assert (false); + } + } + + double radius = sqrt (sx * sx + sy * sy) / a_abs; + db::DPoint center = *mp_v[0] + db::DVector (sx / a, sy / a); return std::make_pair (center, radius); } @@ -507,26 +525,22 @@ Triangle::common_edge (const Triangle *other) const int Triangle::contains (const db::DPoint &point) const { - // the relative distance of the point from the edge - double alpha = db::epsilon; - - double d = db::vprod (*mp_v[2] - *mp_v[0], *mp_v[1] - *mp_v[0]); - if (fabs (d) < db::epsilon * db::epsilon) { - return -1; - } + int vps = db::vprod_sign (*mp_v[2] - *mp_v[0], *mp_v[1] - *mp_v[0]); int res = 1; + const Vertex *vl = mp_v[2]; for (int i = 0; i < 3; ++i) { const Vertex *v = mp_v[i]; - double n = db::vprod (point - *vl, *v - *vl) / d; - if (n < -alpha) { + int n = db::vprod_sign (point - *vl, *v - *vl) * vps; + if (n < 0) { return -1; - } else if (n < alpha) { + } else if (n == 0) { res = 0; } vl = v; } + return res; } @@ -544,8 +558,9 @@ double Triangle::b () const { double lmin = min_edge_length (); - auto cr = circumcircle (); - return lmin / cr.second; + bool ok = false; + auto cr = circumcircle (&ok); + return ok ? lmin / cr.second : 0.0; } bool diff --git a/src/db/db/dbTriangle.h b/src/db/db/dbTriangle.h index dadcabc4a..29ff09abb 100644 --- a/src/db/db/dbTriangle.h +++ b/src/db/db/dbTriangle.h @@ -483,8 +483,10 @@ public: /** * @brief Gets the center point and radius of the circumcircle + * If ok is non-null, it will receive a boolean value indicating whether the circumcircle is valid. + * An invalid circumcircle is an indicator for a degenerated triangle with area 0 (or close to). */ - std::pair circumcircle () const; + std::pair circumcircle (bool *ok = 0) const; /** * @brief Gets the vertex opposite of the given edge diff --git a/src/db/db/dbTriangles.cc b/src/db/db/dbTriangles.cc index 246afa2e6..ae25fc574 100644 --- a/src/db/db/dbTriangles.cc +++ b/src/db/db/dbTriangles.cc @@ -937,13 +937,15 @@ Triangles::is_illegal_edge (db::TriangleEdge *edge) return false; } - auto lr = left->circumcircle (); - if (right->opposite (edge)->in_circle (lr.first, lr.second) > 0) { + bool ok = false; + + auto lr = left->circumcircle (&ok); + if (! ok || right->opposite (edge)->in_circle (lr.first, lr.second) > 0) { return true; } - auto rr = right->circumcircle(); - if (left->opposite (edge)->in_circle (rr.first, rr.second) > 0) { + auto rr = right->circumcircle(&ok); + if (! ok || left->opposite (edge)->in_circle (rr.first, rr.second) > 0) { return true; } diff --git a/src/db/unit_tests/dbTrianglesTests.cc b/src/db/unit_tests/dbTrianglesTests.cc index 9b7dd021c..a76f1e292 100644 --- a/src/db/unit_tests/dbTrianglesTests.cc +++ b/src/db/unit_tests/dbTrianglesTests.cc @@ -951,6 +951,48 @@ TEST(triangulate_thin) EXPECT_EQ (tri.check (false), true); - // for debugging: - tri.dump ("debug.gds"); + // for debugging: + // tri.dump ("debug.gds"); + + for (auto t = tri.begin (); t != tri.end (); ++t) { + EXPECT_GE (t->b (), param.min_b); + } + + EXPECT_GT (tri.num_triangles (), size_t (13000)); + EXPECT_LT (tri.num_triangles (), size_t (13200)); +} + +TEST(triangulate_issue1996) +{ + db::DPoint contour[] = { + db::DPoint (-8000, -8075), + db::DPoint (-8000, 8075), + db::DPoint (18000, 8075), + db::DPoint (18000, -8075) + }; + + db::DPolygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + + double dbu = 0.001; + + db::Triangles::TriangulateParameters param; + param.min_b = 0.5; + param.max_area = 500.0 * dbu * dbu; + + TestableTriangles tri; + db::DCplxTrans trans = db::DCplxTrans (dbu) * db::DCplxTrans (db::DTrans (db::DPoint () - poly.box ().center ())); + tri.triangulate (trans * poly, param); + + EXPECT_EQ (tri.check (false), true); + + // for debugging: + // tri.dump ("debug.gds"); + + for (auto t = tri.begin (); t != tri.end (); ++t) { + EXPECT_GE (t->b (), param.min_b); + } + + EXPECT_GT (tri.num_triangles (), size_t (13000)); + EXPECT_LT (tri.num_triangles (), size_t (13200)); } From 6f69efd4279488c8c0c829df92a664e2da9c85ec Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 20 Mar 2025 23:30:30 +0100 Subject: [PATCH 078/392] WIP --- src/db/db/dbTriangle.cc | 8 ++++- src/db/db/dbTriangles.cc | 78 +++++++++++++++++++++++++++++++++------- 2 files changed, 72 insertions(+), 14 deletions(-) diff --git a/src/db/db/dbTriangle.cc b/src/db/db/dbTriangle.cc index afe396181..13201bffb 100644 --- a/src/db/db/dbTriangle.cc +++ b/src/db/db/dbTriangle.cc @@ -525,7 +525,13 @@ Triangle::common_edge (const Triangle *other) const int Triangle::contains (const db::DPoint &point) const { - int vps = db::vprod_sign (*mp_v[2] - *mp_v[0], *mp_v[1] - *mp_v[0]); + auto c = *mp_v[2] - *mp_v[0]; + auto b = *mp_v[1] - *mp_v[0]; + + int vps = db::vprod_sign (c, b); + if (vps == 0) { + return db::vprod_sign (point - *mp_v[0], b) == 0 && db::vprod_sign (point - *mp_v[0], c) == 0 ? 0 : -1; + } int res = 1; diff --git a/src/db/db/dbTriangles.cc b/src/db/db/dbTriangles.cc index ae25fc574..0d49c53a8 100644 --- a/src/db/db/dbTriangles.cc +++ b/src/db/db/dbTriangles.cc @@ -36,6 +36,12 @@ namespace db { +static inline bool is_equal (const db::DPoint &a, const db::DPoint &b) +{ + return std::abs (a.x () - b.x ()) < std::max (1.0, (std::abs (a.x ()) + std::abs (b.x ()))) * db::epsilon && + std::abs (a.y () - b.y ()) < std::max (1.0, (std::abs (a.y ()) + std::abs (b.y ()))) * db::epsilon; +} + Triangles::Triangles () : m_is_constrained (false), m_level (0), m_id (0), m_flips (0), m_hops (0) { @@ -358,6 +364,12 @@ Triangles::insert (db::Vertex *vertex, std::list > *n // the new vertex is outside the domain if (tris.empty ()) { + // @@@ + if (m_is_constrained) { + dump("debug.gds"); // @@@ + find_triangle_for_point (*vertex); + } + // @@@ tl_assert (! m_is_constrained); insert_new_vertex (vertex, new_triangles); return vertex; @@ -365,26 +377,65 @@ Triangles::insert (db::Vertex *vertex, std::list > *n // check, if the new vertex is on an edge (may be edge between triangles or edge on outside) std::vector on_edges; + std::vector on_vertex; for (int i = 0; i < 3; ++i) { db::TriangleEdge *e = tris.front ()->edge (i); if (e->side_of (*vertex) == 0) { - on_edges.push_back (e); + if (is_equal (*vertex, *e->v1 ()) || is_equal (*vertex, *e->v2 ())) { + on_vertex.push_back (e); + } else { + on_edges.push_back (e); + } } } - if (! on_edges.empty ()) { - if (on_edges.size () == size_t (1)) { - split_triangles_on_edge (vertex, on_edges.front (), new_triangles); - return vertex; - } else { - // the vertex is already present - tl_assert (on_edges.size () == size_t (2)); - return on_edges.front ()->common_vertex (on_edges [1]); - } + if (! on_vertex.empty ()) { + + tl_assert (on_vertex.size () == size_t (2)); + return on_vertex.front ()->common_vertex (on_vertex [1]); + + } else if (! on_edges.empty ()) { + + tl_assert (on_edges.size () == size_t (1)); +// @@@ +auto v1 = on_edges.front()->v1(); +auto v2 = on_edges.front()->v2(); +unsigned int ns1 = 0, ns2 = 0; +for (auto e = v1->begin_edges (); e != v1->end_edges (); ++e) { + if ((*e)->is_segment()) { ++ns1; } +} +for (auto e = v2->begin_edges (); e != v2->end_edges (); ++e) { + if ((*e)->is_segment()) { ++ns2; } +} +std::string vs = vertex->to_string(); +std::string es = on_edges.front()->to_string(); +if (vs == "(-12.9999999999, 3.50126953125)" && es == "((-13, 3.5328125), (-12.9999999998, 3.4697265625))") { + printf("@@@ BANG!\n"); fflush(stdout); +} +// @@@ + split_triangles_on_edge (vertex, on_edges.front (), new_triangles); +// @@@ +unsigned int ns1p = 0, ns2p = 0; +for (auto e = v1->begin_edges (); e != v1->end_edges (); ++e) { + if ((*e)->is_segment()) { ++ns1p; } +} +for (auto e = v2->begin_edges (); e != v2->end_edges (); ++e) { + if ((*e)->is_segment()) { ++ns2p; } +} +if (ns1 != ns1p || ns2 != ns2p) { + printf("@@@ on '%s' '%s' - BANG!\n", vs.c_str(), es.c_str()); fflush(stdout); +} +tl_assert(ns1 == ns1p); +tl_assert(ns2 == ns2p); +// @@@ + return vertex; + } else if (tris.size () == size_t (1)) { + // the new vertex is inside one triangle split_triangle (tris.front (), vertex, new_triangles); return vertex; + } tl_assert (false); @@ -1178,9 +1229,9 @@ Triangles::find_vertex_for_point (const db::DPoint &point) return 0; } db::Vertex *v = 0; - if (edge->v1 ()->equal (point)) { + if (is_equal (*edge->v1 (), point)) { v = edge->v1 (); - } else if (edge->v2 ()->equal (point)) { + } else if (is_equal (*edge->v2 (), point)) { v = edge->v2 (); } return v; @@ -1194,7 +1245,7 @@ Triangles::find_edge_for_points (const db::DPoint &p1, const db::DPoint &p2) return 0; } for (auto e = v->begin_edges (); e != v->end_edges (); ++e) { - if ((*e)->other (v)->equal (p2)) { + if (is_equal (*(*e)->other (v), p2)) { return *e; } } @@ -1587,6 +1638,7 @@ Triangles::refine (const TriangulateParameters ¶meters) if (tl::verbosity () >= parameters.base_verbosity + 10) { tl::info << "Iteration " << nloop << " .."; } + printf("@@@ iteration %d ..\n", (int)nloop); fflush(stdout); std::list > to_consider; for (auto t = new_triangles.begin (); t != new_triangles.end (); ++t) { From 69a8f3ac114dea218ec14e35e16dedf83e97164f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 21 Mar 2025 23:52:08 +0100 Subject: [PATCH 079/392] Preparations for 0.30. --- Changelog.Debian | 7 ++++++ src/tl/tl/tlHash.h | 60 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/Changelog.Debian b/Changelog.Debian index 85f58621a..1d2c2b3c3 100644 --- a/Changelog.Debian +++ b/Changelog.Debian @@ -1,3 +1,10 @@ +klayout (0.30.0-1) unstable; urgency=low + + * New features and bugfixes + - See changelog + + -- Matthias Köfferlein Tue, 25 Mar 2025 00:00:00 +0100 + klayout (0.29.12-1) unstable; urgency=low * New features and bugfixes diff --git a/src/tl/tl/tlHash.h b/src/tl/tl/tlHash.h index d00417067..72535c511 100644 --- a/src/tl/tl/tlHash.h +++ b/src/tl/tl/tlHash.h @@ -34,6 +34,12 @@ #include #include +// for std::hash of QString and QByteArray +#if defined(HAVE_QT) +# include +# include +#endif + #include "tlSList.h" /** @@ -342,6 +348,60 @@ namespace std } }; +#if defined(HAVE_QT) && QT_VERSION < 0x050000 + + /** + * @brief Generic hash for QString + */ + + template <> + size_t hfunc (const QString &o, size_t h) + { + return hfunc_iterable (o, h); + } + + template <> + size_t hfunc (const QString &o) + { + return hfunc (o, size_t (0)); + } + + template <> + struct hash + { + size_t operator() (const QString &o) const + { + return hfunc (o); + } + }; + + /** + * @brief Generic hash for QByteArray + */ + + template <> + size_t hfunc (const QByteArray &o, size_t h) + { + return hfunc_iterable (o, h); + } + + template <> + size_t hfunc (const QByteArray &o) + { + return hfunc (o, size_t (0)); + } + + template <> + struct hash + { + size_t operator() (const QString &o) const + { + return hfunc (o); + } + }; + +#endif + } #endif From 283a9d0b2210d9e8e61c1cce4cced8b9359782a9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Mar 2025 00:02:13 +0100 Subject: [PATCH 080/392] Build fixes for Qt4 --- src/tl/tl/tlHash.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/tl/tl/tlHash.h b/src/tl/tl/tlHash.h index 72535c511..1c6a55670 100644 --- a/src/tl/tl/tlHash.h +++ b/src/tl/tl/tlHash.h @@ -354,6 +354,15 @@ namespace std * @brief Generic hash for QString */ + template <> + struct hash + { + size_t operator() (const QChar &o) const + { + return hfunc (o.unicode ()); + } + }; + template <> size_t hfunc (const QString &o, size_t h) { From 2aa88fe29ce823a22600e8449f6b7bd1509ffeda Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Mar 2025 14:09:00 +0100 Subject: [PATCH 081/392] Fixed a linker issue on Qt4 --- src/tl/tl/tlHash.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tl/tl/tlHash.h b/src/tl/tl/tlHash.h index 1c6a55670..f9c135eb2 100644 --- a/src/tl/tl/tlHash.h +++ b/src/tl/tl/tlHash.h @@ -364,13 +364,13 @@ namespace std }; template <> - size_t hfunc (const QString &o, size_t h) + inline size_t hfunc (const QString &o, size_t h) { return hfunc_iterable (o, h); } template <> - size_t hfunc (const QString &o) + inline size_t hfunc (const QString &o) { return hfunc (o, size_t (0)); } @@ -389,13 +389,13 @@ namespace std */ template <> - size_t hfunc (const QByteArray &o, size_t h) + inline size_t hfunc (const QByteArray &o, size_t h) { return hfunc_iterable (o, h); } template <> - size_t hfunc (const QByteArray &o) + inline size_t hfunc (const QByteArray &o) { return hfunc (o, size_t (0)); } From ee60ae814610988f4a6472ca15810be6be42e74e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Mar 2025 14:49:16 +0100 Subject: [PATCH 082/392] Changelog for 0.30 --- Changelog | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/Changelog b/Changelog index fdab73e4d..7d6541701 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,36 @@ +0.30.0 (2025-03-25): +* Enhancement: Better support for user properties + The property management has been overhauled. Properties are not globally + scoped. The "Layout" class now provides class methods to convert property IDs + to property hashes and back. It is no longer required to know the origin of + a property ID. + Other changes are: + - New classes "BoxWithProperties", "EdgeWithProperties", "PolygonWithProperties", + "SimplePolygonWithProperties", "PathWithProperties", "EdgePairWithProperties", + "TextWithProperties" and corresponding "D..." classes. + Many functions accept these objects now in addition to the property-less "Box", + "Edge" etc. objects. Many functions now deliver property-annotated objects + instead of the plain ones. + - "EdgePairFilter#property_glob", "EdgePairFilter#property_filter", "EdgePairFilter#property_filter_bounded" + creates filters for edge pair objects, which can be used on "EdgePairs" containers to + filter by properties. + - The same is available for "EdgeFilter" (for filtering edges in "Edges" containers), + "PolygonFilter" (for filtering polygons in "Region" containers), and + "TextFilter" (for filtering texts in "Texts" containers) + - New method "process_with_properties" for "PolygonOperator", "EdgeOperator", + "EdgePairOperator" and "TextOperator" +* Enhancement: New class "PolygonNeighborhood" + This is a visitor to obtain all "neighboring" polygons for each polygon + in a Region. Currently, neighborhood is defined by the bounding box of + the primary polygons. +* Enhancement: New methods: + - "LayoutToNetlist#build_net", "build_nets" and "build_all_nets" do not + - "LayoutToNetlist#layer_index" from a name + - "LayoutToNetlist#texts_by_index" and "LayoutToNetlist#texts_by_name" + - "LayoutToNetlist#original_layout" and "LayoutToNetlist#original_top_cell" + - "LayoutToNetlist#polygons_of_net" and "LayoutToNetlist#texts_of_net" + - "Device#terminal_ref" + 0.29.12 (2025-03-02): * Bug: %GITHUB%/issues/1976 Crash on cross mode, lw > 1 and oversampling * Bug: %GITHUB%/issues/1987 Build failure against Qt6.8 From e6914c78b92a23f2d5e9dfebfb2b5dfcfe553d85 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Mar 2025 17:29:16 +0100 Subject: [PATCH 083/392] Fixing builds for Qt4 --- src/tl/tl/tlHash.h | 45 +++++++++++++++++---------------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/src/tl/tl/tlHash.h b/src/tl/tl/tlHash.h index f9c135eb2..82b379a92 100644 --- a/src/tl/tl/tlHash.h +++ b/src/tl/tl/tlHash.h @@ -348,21 +348,28 @@ namespace std } }; -#if defined(HAVE_QT) && QT_VERSION < 0x050000 +#if defined(HAVE_QT) + + /** + * @brief Generic hash for QChar + */ + + template <> + inline size_t hfunc (const QChar &o, size_t h) + { + return hcombine (h, size_t (o.unicode ())); + } + + template <> + inline size_t hfunc (const QChar &o) + { + return size_t (o.unicode ()); + } /** * @brief Generic hash for QString */ - template <> - struct hash - { - size_t operator() (const QChar &o) const - { - return hfunc (o.unicode ()); - } - }; - template <> inline size_t hfunc (const QString &o, size_t h) { @@ -375,15 +382,6 @@ namespace std return hfunc (o, size_t (0)); } - template <> - struct hash - { - size_t operator() (const QString &o) const - { - return hfunc (o); - } - }; - /** * @brief Generic hash for QByteArray */ @@ -400,15 +398,6 @@ namespace std return hfunc (o, size_t (0)); } - template <> - struct hash - { - size_t operator() (const QString &o) const - { - return hfunc (o); - } - }; - #endif } From 561a76088167b331d4ce5c164544502fb9251d8f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Mar 2025 17:51:07 +0100 Subject: [PATCH 084/392] Updating doc, python stubs, fixed non-Qt builds --- src/doc/doc/about/drc_ref.xml | 2 +- src/doc/doc/about/drc_ref_drc.xml | 2 +- src/doc/doc/about/drc_ref_global.xml | 2 +- src/doc/doc/about/drc_ref_layer.xml | 2 +- src/doc/doc/about/drc_ref_netter.xml | 2 +- src/doc/doc/about/drc_ref_source.xml | 2 +- src/doc/doc/about/lvs_ref.xml | 2 +- src/doc/doc/about/lvs_ref_global.xml | 2 +- src/doc/doc/about/lvs_ref_netter.xml | 2 +- src/edt/edt/edtServiceImpl.cc | 4 +- src/pymod/distutils_src/klayout/dbcore.pyi | 7867 +++++++++++++++++-- src/pymod/distutils_src/klayout/rdbcore.pyi | 42 + 12 files changed, 7420 insertions(+), 511 deletions(-) diff --git a/src/doc/doc/about/drc_ref.xml b/src/doc/doc/about/drc_ref.xml index 203941a1d..3974cf751 100644 --- a/src/doc/doc/about/drc_ref.xml +++ b/src/doc/doc/about/drc_ref.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/drc_ref_drc.xml b/src/doc/doc/about/drc_ref_drc.xml index a4ad2a239..78e0e1744 100644 --- a/src/doc/doc/about/drc_ref_drc.xml +++ b/src/doc/doc/about/drc_ref_drc.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/drc_ref_global.xml b/src/doc/doc/about/drc_ref_global.xml index 5c85b99cb..be5f57530 100644 --- a/src/doc/doc/about/drc_ref_global.xml +++ b/src/doc/doc/about/drc_ref_global.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/drc_ref_layer.xml b/src/doc/doc/about/drc_ref_layer.xml index 90b672b06..db0c26410 100644 --- a/src/doc/doc/about/drc_ref_layer.xml +++ b/src/doc/doc/about/drc_ref_layer.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/drc_ref_netter.xml b/src/doc/doc/about/drc_ref_netter.xml index dac86f2ed..9cfe03f3f 100644 --- a/src/doc/doc/about/drc_ref_netter.xml +++ b/src/doc/doc/about/drc_ref_netter.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/drc_ref_source.xml b/src/doc/doc/about/drc_ref_source.xml index a746917e3..a3fea2b89 100644 --- a/src/doc/doc/about/drc_ref_source.xml +++ b/src/doc/doc/about/drc_ref_source.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/lvs_ref.xml b/src/doc/doc/about/lvs_ref.xml index 58168f012..842f21820 100644 --- a/src/doc/doc/about/lvs_ref.xml +++ b/src/doc/doc/about/lvs_ref.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/lvs_ref_global.xml b/src/doc/doc/about/lvs_ref_global.xml index c1d2ad914..ac633b4b3 100644 --- a/src/doc/doc/about/lvs_ref_global.xml +++ b/src/doc/doc/about/lvs_ref_global.xml @@ -1,7 +1,7 @@ - + diff --git a/src/doc/doc/about/lvs_ref_netter.xml b/src/doc/doc/about/lvs_ref_netter.xml index c983b88f0..463eebd69 100644 --- a/src/doc/doc/about/lvs_ref_netter.xml +++ b/src/doc/doc/about/lvs_ref_netter.xml @@ -1,7 +1,7 @@ - + diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index ba5f77723..bc72ec62e 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -38,12 +38,12 @@ #include "layMarker.h" #include "layLayerProperties.h" #include "layLayoutViewBase.h" -#include "layBusy.h" #if defined(HAVE_QT) # include "layLayoutView.h" # include "layTipDialog.h" # include "layDragDropData.h" +# include "layBusy.h" #endif #if defined(HAVE_QT) @@ -1723,8 +1723,10 @@ InstService::make_cell (const lay::CellView &cv) return std::make_pair (true, m_current_cell); } +#if defined(HAVE_QT) // prevents recursion lay::BusySection busy; +#endif // NOTE: do this at the beginning: creating a transaction might delete transactions behind the // head transaction, hence releasing (thus: deleting) cells. To prevert interference, create diff --git a/src/pymod/distutils_src/klayout/dbcore.pyi b/src/pymod/distutils_src/klayout/dbcore.pyi index 0a92ac479..11f6de2f2 100644 --- a/src/pymod/distutils_src/klayout/dbcore.pyi +++ b/src/pymod/distutils_src/klayout/dbcore.pyi @@ -794,6 +794,265 @@ class Box: ... ... +class BoxWithProperties(Box): + r""" + @brief A Box object with properties attached. + This class represents a combination of a Box object an user properties. User properties are stored in form of a properties ID. Convenience methods are provided to manipulate or retrieve user properties directly. + + This class has been introduced in version 0.30. + """ + prop_id: int + r""" + Getter: + @brief Gets the properties ID associated with the object + + Setter: + @brief Sets the properties ID of the object + """ + @overload + @classmethod + def new(cls, box: Box, properties: Dict[Any, Any]) -> BoxWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + @classmethod + def new(cls, box: Box, properties_id: Optional[int] = ...) -> BoxWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + @overload + def __init__(self, box: Box, properties: Dict[Any, Any]) -> None: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + def __init__(self, box: Box, properties_id: Optional[int] = ...) -> None: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + def __mul__(self, f: float) -> BoxWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __repr__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def __rmul__(self, f: float) -> BoxWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __str__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def _assign(self, other: Box) -> None: + r""" + @brief Assigns another object to self + """ + ... + def _const_cast(self) -> BoxWithProperties: + r""" + @brief Returns a non-const reference to self. + Basically, this method allows turning a const object reference to a non-const one. This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects. + + This method has been introduced in version 0.29.6. + """ + ... + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def _dup(self) -> BoxWithProperties: + r""" + @brief Creates a copy of self + """ + ... + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def _to_const_object(self) -> BoxWithProperties: + r""" + @hide + """ + ... + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def delete_property(self, key: Any) -> None: + r""" + @brief Deletes the user property with the given key + This method is a convenience method that deletes the property with the given key. It does nothing if no property with that key exists. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. + """ + ... + def downcast(self) -> Box: + r""" + @brief Gets the corresponding object without the properties + """ + ... + @overload + def move(self, dx: Optional[int] = ..., dy: Optional[int] = ...) -> BoxWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def move(self, v: Vector) -> BoxWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param v The distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def moved(self, dx: Optional[int] = ..., dy: Optional[int] = ...) -> BoxWithProperties: + r""" + @brief Returns the moved object (does not modify self) + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object. + """ + ... + @overload + def moved(self, v: Vector) -> BoxWithProperties: + r""" + @brief Returns the moved object + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param v The distance to move the object. + + @return The moved object. + """ + ... + def properties(self) -> Any: + r""" + @brief Gets the user properties + This method is a convenience method that gets the properties of the shape as a single hash. + """ + ... + def property(self, key: Any) -> Any: + r""" + @brief Gets the user property with the given key + This method is a convenience method that gets the user property with the given key. If no property with that key does not exist, it will return nil. Using that method is more convenient than using the layout object and the properties ID to retrieve the property value. + """ + ... + def set_property(self, key: Any, value: Any) -> None: + r""" + @brief Sets the user property with the given key to the given value + This method is a convenience method that sets the user property with the given key to the given value. If no property with that key exists, it will create one. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. + """ + ... + def to_s(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + @overload + def transform(self, t: ICplxTrans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transform(self, t: Trans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transformed(self, t: CplxTrans) -> DBoxWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: ICplxTrans) -> BoxWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: Trans) -> BoxWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + ... + class Cell: r""" @brief A cell @@ -5203,7 +5462,7 @@ class CompoundRegionOperationNode: r""" @brief Creates a new edge neighborhood collector - @param children The inputs to use. The first one in the primary input, the others are neighbors. + @param children The inputs to use. The inputs are enumerated by base zero indexes in the visitor callback. @param visitor The visitor object (see \EdgeNeighborhoodVisitor) receiving the edge events. @param bext The search window extension to use at the edge beginning. @param eext The search window extension to use at the edge end. @@ -5477,6 +5736,18 @@ class CompoundRegionOperationNode: """ ... @classmethod + def new_polygon_neighborhood(cls, children: Sequence[CompoundRegionOperationNode], visitor: PolygonNeighborhoodVisitorBase, dist: Optional[int] = ...) -> CompoundRegionOperationNode: + r""" + @brief Creates a new polygon neighborhood collector + + @param children The inputs to use. The inputs are enumerated by base zero indexes in the visitor callback. + @param visitor The visitor object (see \PolygonNeighborhoodVisitor) receiving the polygon events. + @param dist The search distance in which to look up neighbors. + + This constructor has been introduced in version 0.30.0. + """ + ... + @classmethod def new_polygons(cls, input: CompoundRegionOperationNode, e: Optional[int] = ...) -> CompoundRegionOperationNode: r""" @brief Creates a node converting the input to polygons. @@ -6302,6 +6573,19 @@ class CplxTrans: """ ... @overload + def __mul__(self, box: BoxWithProperties) -> DBoxWithProperties: + r""" + @brief Transforms a box with properties + + 't*box' or 't.trans(box)' is equivalent to box.transformed(t). + + @param box The box to transform + @return The transformed box + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, d: int) -> float: r""" @brief Transforms a single distance @@ -6330,6 +6614,32 @@ class CplxTrans: """ ... @overload + def __mul__(self, edge: EdgeWithProperties) -> DEdgeWithProperties: + r""" + @brief Transforms an edge with properties + + 't*edge' or 't.trans(edge)' is equivalent to edge.transformed(t). + + @param edge The edge to transform + @return The transformed edge + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __mul__(self, edge_pair: EdgePairWithProperties) -> DEdgePairWithProperties: + r""" + @brief Transforms an edge pair with properties + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, p: Point) -> DPoint: r""" @brief Transforms a point @@ -6371,6 +6681,19 @@ class CplxTrans: """ ... @overload + def __mul__(self, path: PathWithProperties) -> DPathWithProperties: + r""" + @brief Transforms a path with properties + + 't*path' or 't.trans(path)' is equivalent to path.transformed(t). + + @param path The path to transform + @return The transformed path + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, polygon: Polygon) -> DPolygon: r""" @brief Transforms a polygon @@ -6384,6 +6707,32 @@ class CplxTrans: """ ... @overload + def __mul__(self, polygon: PolygonWithProperties) -> DPolygonWithProperties: + r""" + @brief Transforms a polygon with properties + + 't*polygon' or 't.trans(polygon)' is equivalent to polygon.transformed(t). + + @param polygon The polygon to transform + @return The transformed polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __mul__(self, simple_polygon: SimplePolygonWithProperties) -> DSimplePolygonWithProperties: + r""" + @brief Transforms a simple polygon with properties + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, t: CplxTrans) -> CplxTrans: r""" @brief Returns the concatenated transformation @@ -6429,6 +6778,19 @@ class CplxTrans: This convenience method has been introduced in version 0.25. """ ... + @overload + def __mul__(self, text: TextWithProperties) -> DTextWithProperties: + r""" + @brief Transforms a text with properties + + 't*text' or 't.trans(text)' is equivalent to text.transformed(t). + + @param text The text to transform + @return The transformed text + + This convenience method has been introduced in version 0.30. + """ + ... def __ne__(self, other: object) -> bool: r""" @brief Tests for inequality @@ -6457,6 +6819,19 @@ class CplxTrans: """ ... @overload + def __rmul__(self, box: BoxWithProperties) -> DBoxWithProperties: + r""" + @brief Transforms a box with properties + + 't*box' or 't.trans(box)' is equivalent to box.transformed(t). + + @param box The box to transform + @return The transformed box + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, d: int) -> float: r""" @brief Transforms a single distance @@ -6485,6 +6860,32 @@ class CplxTrans: """ ... @overload + def __rmul__(self, edge: EdgeWithProperties) -> DEdgeWithProperties: + r""" + @brief Transforms an edge with properties + + 't*edge' or 't.trans(edge)' is equivalent to edge.transformed(t). + + @param edge The edge to transform + @return The transformed edge + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __rmul__(self, edge_pair: EdgePairWithProperties) -> DEdgePairWithProperties: + r""" + @brief Transforms an edge pair with properties + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, p: Point) -> DPoint: r""" @brief Transforms a point @@ -6526,6 +6927,19 @@ class CplxTrans: """ ... @overload + def __rmul__(self, path: PathWithProperties) -> DPathWithProperties: + r""" + @brief Transforms a path with properties + + 't*path' or 't.trans(path)' is equivalent to path.transformed(t). + + @param path The path to transform + @return The transformed path + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, polygon: Polygon) -> DPolygon: r""" @brief Transforms a polygon @@ -6539,6 +6953,32 @@ class CplxTrans: """ ... @overload + def __rmul__(self, polygon: PolygonWithProperties) -> DPolygonWithProperties: + r""" + @brief Transforms a polygon with properties + + 't*polygon' or 't.trans(polygon)' is equivalent to polygon.transformed(t). + + @param polygon The polygon to transform + @return The transformed polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __rmul__(self, simple_polygon: SimplePolygonWithProperties) -> DSimplePolygonWithProperties: + r""" + @brief Transforms a simple polygon with properties + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, text: Text) -> DText: r""" @brief Transforms a text @@ -6551,6 +6991,19 @@ class CplxTrans: This convenience method has been introduced in version 0.25. """ ... + @overload + def __rmul__(self, text: TextWithProperties) -> DTextWithProperties: + r""" + @brief Transforms a text with properties + + 't*text' or 't.trans(text)' is equivalent to text.transformed(t). + + @param text The text to transform + @return The transformed text + + This convenience method has been introduced in version 0.30. + """ + ... def __str__(self, lazy: Optional[bool] = ..., dbu: Optional[float] = ...) -> str: r""" @brief String conversion @@ -6812,6 +7265,19 @@ class CplxTrans: """ ... @overload + def trans(self, box: BoxWithProperties) -> DBoxWithProperties: + r""" + @brief Transforms a box with properties + + 't*box' or 't.trans(box)' is equivalent to box.transformed(t). + + @param box The box to transform + @return The transformed box + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, edge: Edge) -> DEdge: r""" @brief Transforms an edge @@ -6825,6 +7291,32 @@ class CplxTrans: """ ... @overload + def trans(self, edge: EdgeWithProperties) -> DEdgeWithProperties: + r""" + @brief Transforms an edge with properties + + 't*edge' or 't.trans(edge)' is equivalent to edge.transformed(t). + + @param edge The edge to transform + @return The transformed edge + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def trans(self, edge_pair: EdgePairWithProperties) -> DEdgePairWithProperties: + r""" + @brief Transforms an edge pair with properties + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, p: Point) -> DPoint: r""" @brief Transforms a point @@ -6866,6 +7358,19 @@ class CplxTrans: """ ... @overload + def trans(self, path: PathWithProperties) -> DPathWithProperties: + r""" + @brief Transforms a path with properties + + 't*path' or 't.trans(path)' is equivalent to path.transformed(t). + + @param path The path to transform + @return The transformed path + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, polygon: Polygon) -> DPolygon: r""" @brief Transforms a polygon @@ -6879,6 +7384,32 @@ class CplxTrans: """ ... @overload + def trans(self, polygon: PolygonWithProperties) -> DPolygonWithProperties: + r""" + @brief Transforms a polygon with properties + + 't*polygon' or 't.trans(polygon)' is equivalent to polygon.transformed(t). + + @param polygon The polygon to transform + @return The transformed polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def trans(self, simple_polygon: SimplePolygonWithProperties) -> DSimplePolygonWithProperties: + r""" + @brief Transforms a simple polygon with properties + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, text: Text) -> DText: r""" @brief Transforms a text @@ -6891,6 +7422,19 @@ class CplxTrans: This convenience method has been introduced in version 0.25. """ ... + @overload + def trans(self, text: TextWithProperties) -> DTextWithProperties: + r""" + @brief Transforms a text with properties + + 't*text' or 't.trans(text)' is equivalent to text.transformed(t). + + @param text The text to transform + @return The transformed text + + This convenience method has been introduced in version 0.30. + """ + ... ... class DBox: @@ -7683,6 +8227,265 @@ class DBox: ... ... +class DBoxWithProperties(DBox): + r""" + @brief A DBox object with properties attached. + This class represents a combination of a DBox object an user properties. User properties are stored in form of a properties ID. Convenience methods are provided to manipulate or retrieve user properties directly. + + This class has been introduced in version 0.30. + """ + prop_id: int + r""" + Getter: + @brief Gets the properties ID associated with the object + + Setter: + @brief Sets the properties ID of the object + """ + @overload + @classmethod + def new(cls, box: DBox, properties: Dict[Any, Any]) -> DBoxWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + @classmethod + def new(cls, box: DBox, properties_id: Optional[int] = ...) -> DBoxWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + @overload + def __init__(self, box: DBox, properties: Dict[Any, Any]) -> None: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + def __init__(self, box: DBox, properties_id: Optional[int] = ...) -> None: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + def __mul__(self, f: float) -> DBoxWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __repr__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def __rmul__(self, f: float) -> DBoxWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __str__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def _assign(self, other: DBox) -> None: + r""" + @brief Assigns another object to self + """ + ... + def _const_cast(self) -> DBoxWithProperties: + r""" + @brief Returns a non-const reference to self. + Basically, this method allows turning a const object reference to a non-const one. This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects. + + This method has been introduced in version 0.29.6. + """ + ... + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def _dup(self) -> DBoxWithProperties: + r""" + @brief Creates a copy of self + """ + ... + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def _to_const_object(self) -> DBoxWithProperties: + r""" + @hide + """ + ... + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def delete_property(self, key: Any) -> None: + r""" + @brief Deletes the user property with the given key + This method is a convenience method that deletes the property with the given key. It does nothing if no property with that key exists. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. + """ + ... + def downcast(self) -> DBox: + r""" + @brief Gets the corresponding object without the properties + """ + ... + @overload + def move(self, dx: Optional[float] = ..., dy: Optional[float] = ...) -> DBoxWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def move(self, v: DVector) -> DBoxWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param v The distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def moved(self, dx: Optional[float] = ..., dy: Optional[float] = ...) -> DBoxWithProperties: + r""" + @brief Returns the moved object (does not modify self) + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object. + """ + ... + @overload + def moved(self, v: DVector) -> DBoxWithProperties: + r""" + @brief Returns the moved object + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param v The distance to move the object. + + @return The moved object. + """ + ... + def properties(self) -> Any: + r""" + @brief Gets the user properties + This method is a convenience method that gets the properties of the shape as a single hash. + """ + ... + def property(self, key: Any) -> Any: + r""" + @brief Gets the user property with the given key + This method is a convenience method that gets the user property with the given key. If no property with that key does not exist, it will return nil. Using that method is more convenient than using the layout object and the properties ID to retrieve the property value. + """ + ... + def set_property(self, key: Any, value: Any) -> None: + r""" + @brief Sets the user property with the given key to the given value + This method is a convenience method that sets the user property with the given key to the given value. If no property with that key exists, it will create one. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. + """ + ... + def to_s(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + @overload + def transform(self, t: DCplxTrans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transform(self, t: DTrans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transformed(self, t: DCplxTrans) -> DBoxWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: DTrans) -> DBoxWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: VCplxTrans) -> BoxWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + ... + class DCellInstArray: r""" @brief A single or array cell instance in micrometer units @@ -8731,6 +9534,19 @@ class DCplxTrans: """ ... @overload + def __mul__(self, box: DBoxWithProperties) -> DBoxWithProperties: + r""" + @brief Transforms a box with properties + + 't*box' or 't.trans(box)' is equivalent to box.transformed(t). + + @param box The box to transform + @return The transformed box + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, d: float) -> float: r""" @brief Transforms a single distance @@ -8759,6 +9575,32 @@ class DCplxTrans: """ ... @overload + def __mul__(self, edge: DEdgeWithProperties) -> DEdgeWithProperties: + r""" + @brief Transforms an edge with properties + + 't*edge' or 't.trans(edge)' is equivalent to edge.transformed(t). + + @param edge The edge to transform + @return The transformed edge + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __mul__(self, edge_pair: DEdgePairWithProperties) -> DEdgePairWithProperties: + r""" + @brief Transforms an edge pair with properties + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, p: DPoint) -> DPoint: r""" @brief Transforms a point @@ -8800,6 +9642,19 @@ class DCplxTrans: """ ... @overload + def __mul__(self, path: DPathWithProperties) -> DPathWithProperties: + r""" + @brief Transforms a path with properties + + 't*path' or 't.trans(path)' is equivalent to path.transformed(t). + + @param path The path to transform + @return The transformed path + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, polygon: DPolygon) -> DPolygon: r""" @brief Transforms a polygon @@ -8813,6 +9668,32 @@ class DCplxTrans: """ ... @overload + def __mul__(self, polygon: DPolygonWithProperties) -> DPolygonWithProperties: + r""" + @brief Transforms a polygon with properties + + 't*polygon' or 't.trans(polygon)' is equivalent to polygon.transformed(t). + + @param polygon The polygon to transform + @return The transformed polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __mul__(self, simple_polygon: DSimplePolygonWithProperties) -> DSimplePolygonWithProperties: + r""" + @brief Transforms a simple polygon with properties + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, t: CplxTrans) -> CplxTrans: r""" @brief Multiplication (concatenation) of transformations @@ -8847,6 +9728,19 @@ class DCplxTrans: This convenience method has been introduced in version 0.25. """ ... + @overload + def __mul__(self, text: DTextWithProperties) -> DTextWithProperties: + r""" + @brief Transforms a text with properties + + 't*text' or 't.trans(text)' is equivalent to text.transformed(t). + + @param text The text to transform + @return The transformed text + + This convenience method has been introduced in version 0.30. + """ + ... def __ne__(self, other: object) -> bool: r""" @brief Tests for inequality @@ -8875,6 +9769,19 @@ class DCplxTrans: """ ... @overload + def __rmul__(self, box: DBoxWithProperties) -> DBoxWithProperties: + r""" + @brief Transforms a box with properties + + 't*box' or 't.trans(box)' is equivalent to box.transformed(t). + + @param box The box to transform + @return The transformed box + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, d: float) -> float: r""" @brief Transforms a single distance @@ -8903,6 +9810,32 @@ class DCplxTrans: """ ... @overload + def __rmul__(self, edge: DEdgeWithProperties) -> DEdgeWithProperties: + r""" + @brief Transforms an edge with properties + + 't*edge' or 't.trans(edge)' is equivalent to edge.transformed(t). + + @param edge The edge to transform + @return The transformed edge + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __rmul__(self, edge_pair: DEdgePairWithProperties) -> DEdgePairWithProperties: + r""" + @brief Transforms an edge pair with properties + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, p: DPoint) -> DPoint: r""" @brief Transforms a point @@ -8944,6 +9877,19 @@ class DCplxTrans: """ ... @overload + def __rmul__(self, path: DPathWithProperties) -> DPathWithProperties: + r""" + @brief Transforms a path with properties + + 't*path' or 't.trans(path)' is equivalent to path.transformed(t). + + @param path The path to transform + @return The transformed path + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, polygon: DPolygon) -> DPolygon: r""" @brief Transforms a polygon @@ -8957,6 +9903,32 @@ class DCplxTrans: """ ... @overload + def __rmul__(self, polygon: DPolygonWithProperties) -> DPolygonWithProperties: + r""" + @brief Transforms a polygon with properties + + 't*polygon' or 't.trans(polygon)' is equivalent to polygon.transformed(t). + + @param polygon The polygon to transform + @return The transformed polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __rmul__(self, simple_polygon: DSimplePolygonWithProperties) -> DSimplePolygonWithProperties: + r""" + @brief Transforms a simple polygon with properties + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, text: DText) -> DText: r""" @brief Transforms a text @@ -8969,6 +9941,19 @@ class DCplxTrans: This convenience method has been introduced in version 0.25. """ ... + @overload + def __rmul__(self, text: DTextWithProperties) -> DTextWithProperties: + r""" + @brief Transforms a text with properties + + 't*text' or 't.trans(text)' is equivalent to text.transformed(t). + + @param text The text to transform + @return The transformed text + + This convenience method has been introduced in version 0.30. + """ + ... def __str__(self, lazy: Optional[bool] = ..., dbu: Optional[float] = ...) -> str: r""" @brief String conversion @@ -9234,6 +10219,19 @@ class DCplxTrans: """ ... @overload + def trans(self, box: DBoxWithProperties) -> DBoxWithProperties: + r""" + @brief Transforms a box with properties + + 't*box' or 't.trans(box)' is equivalent to box.transformed(t). + + @param box The box to transform + @return The transformed box + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, edge: DEdge) -> DEdge: r""" @brief Transforms an edge @@ -9247,6 +10245,32 @@ class DCplxTrans: """ ... @overload + def trans(self, edge: DEdgeWithProperties) -> DEdgeWithProperties: + r""" + @brief Transforms an edge with properties + + 't*edge' or 't.trans(edge)' is equivalent to edge.transformed(t). + + @param edge The edge to transform + @return The transformed edge + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def trans(self, edge_pair: DEdgePairWithProperties) -> DEdgePairWithProperties: + r""" + @brief Transforms an edge pair with properties + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, p: DPoint) -> DPoint: r""" @brief Transforms a point @@ -9288,6 +10312,19 @@ class DCplxTrans: """ ... @overload + def trans(self, path: DPathWithProperties) -> DPathWithProperties: + r""" + @brief Transforms a path with properties + + 't*path' or 't.trans(path)' is equivalent to path.transformed(t). + + @param path The path to transform + @return The transformed path + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, polygon: DPolygon) -> DPolygon: r""" @brief Transforms a polygon @@ -9301,6 +10338,32 @@ class DCplxTrans: """ ... @overload + def trans(self, polygon: DPolygonWithProperties) -> DPolygonWithProperties: + r""" + @brief Transforms a polygon with properties + + 't*polygon' or 't.trans(polygon)' is equivalent to polygon.transformed(t). + + @param polygon The polygon to transform + @return The transformed polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def trans(self, simple_polygon: DSimplePolygonWithProperties) -> DSimplePolygonWithProperties: + r""" + @brief Transforms a simple polygon with properties + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, text: DText) -> DText: r""" @brief Transforms a text @@ -9313,6 +10376,19 @@ class DCplxTrans: This convenience method has been introduced in version 0.25. """ ... + @overload + def trans(self, text: DTextWithProperties) -> DTextWithProperties: + r""" + @brief Transforms a text with properties + + 't*text' or 't.trans(text)' is equivalent to text.transformed(t). + + @param text The text to transform + @return The transformed text + + This convenience method has been introduced in version 0.30. + """ + ... ... class DEdge: @@ -10544,6 +11620,524 @@ class DEdgePair: ... ... +class DEdgePairWithProperties(EdgePair): + r""" + @brief A DEdgePair object with properties attached. + This class represents a combination of a DEdgePair object an user properties. User properties are stored in form of a properties ID. Convenience methods are provided to manipulate or retrieve user properties directly. + + This class has been introduced in version 0.30. + """ + prop_id: int + r""" + Getter: + @brief Gets the properties ID associated with the object + + Setter: + @brief Sets the properties ID of the object + """ + @overload + @classmethod + def new(cls, edge_pair: DEdgePair, properties: Dict[Any, Any]) -> DEdgePairWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + @classmethod + def new(cls, edge_pair: DEdgePair, properties_id: Optional[int] = ...) -> DEdgePairWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + @overload + def __init__(self, edge_pair: DEdgePair, properties: Dict[Any, Any]) -> None: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + def __init__(self, edge_pair: DEdgePair, properties_id: Optional[int] = ...) -> None: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + def __mul__(self, f: float) -> DEdgePairWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __repr__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def __rmul__(self, f: float) -> DEdgePairWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __str__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def _assign(self, other: EdgePair) -> None: + r""" + @brief Assigns another object to self + """ + ... + def _const_cast(self) -> DEdgePairWithProperties: + r""" + @brief Returns a non-const reference to self. + Basically, this method allows turning a const object reference to a non-const one. This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects. + + This method has been introduced in version 0.29.6. + """ + ... + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def _dup(self) -> DEdgePairWithProperties: + r""" + @brief Creates a copy of self + """ + ... + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def _to_const_object(self) -> DEdgePairWithProperties: + r""" + @hide + """ + ... + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def delete_property(self, key: Any) -> None: + r""" + @brief Deletes the user property with the given key + This method is a convenience method that deletes the property with the given key. It does nothing if no property with that key exists. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. + """ + ... + def downcast(self) -> DEdgePair: + r""" + @brief Gets the corresponding object without the properties + """ + ... + @overload + def move(self, dx: Optional[float] = ..., dy: Optional[float] = ...) -> DEdgePairWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def move(self, v: DVector) -> DEdgePairWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param v The distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def moved(self, dx: Optional[float] = ..., dy: Optional[float] = ...) -> DEdgePairWithProperties: + r""" + @brief Returns the moved object (does not modify self) + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object. + """ + ... + @overload + def moved(self, v: DVector) -> DEdgePairWithProperties: + r""" + @brief Returns the moved object + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param v The distance to move the object. + + @return The moved object. + """ + ... + def properties(self) -> Any: + r""" + @brief Gets the user properties + This method is a convenience method that gets the properties of the shape as a single hash. + """ + ... + def property(self, key: Any) -> Any: + r""" + @brief Gets the user property with the given key + This method is a convenience method that gets the user property with the given key. If no property with that key does not exist, it will return nil. Using that method is more convenient than using the layout object and the properties ID to retrieve the property value. + """ + ... + def set_property(self, key: Any, value: Any) -> None: + r""" + @brief Sets the user property with the given key to the given value + This method is a convenience method that sets the user property with the given key to the given value. If no property with that key exists, it will create one. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. + """ + ... + def to_s(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + @overload + def transform(self, t: DCplxTrans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transform(self, t: DTrans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transformed(self, t: DCplxTrans) -> DEdgePairWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: DTrans) -> DEdgePairWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: VCplxTrans) -> EdgePairWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + ... + +class DEdgeWithProperties(DEdge): + r""" + @brief A DEdge object with properties attached. + This class represents a combination of a DEdge object an user properties. User properties are stored in form of a properties ID. Convenience methods are provided to manipulate or retrieve user properties directly. + + This class has been introduced in version 0.30. + """ + prop_id: int + r""" + Getter: + @brief Gets the properties ID associated with the object + + Setter: + @brief Sets the properties ID of the object + """ + @overload + @classmethod + def new(cls, edge: DEdge, properties: Dict[Any, Any]) -> DEdgeWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + @classmethod + def new(cls, edge: DEdge, properties_id: Optional[int] = ...) -> DEdgeWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + @overload + def __init__(self, edge: DEdge, properties: Dict[Any, Any]) -> None: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + def __init__(self, edge: DEdge, properties_id: Optional[int] = ...) -> None: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + def __mul__(self, f: float) -> DEdgeWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __repr__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def __rmul__(self, f: float) -> DEdgeWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __str__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def _assign(self, other: DEdge) -> None: + r""" + @brief Assigns another object to self + """ + ... + def _const_cast(self) -> DEdgeWithProperties: + r""" + @brief Returns a non-const reference to self. + Basically, this method allows turning a const object reference to a non-const one. This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects. + + This method has been introduced in version 0.29.6. + """ + ... + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def _dup(self) -> DEdgeWithProperties: + r""" + @brief Creates a copy of self + """ + ... + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def _to_const_object(self) -> DEdgeWithProperties: + r""" + @hide + """ + ... + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def delete_property(self, key: Any) -> None: + r""" + @brief Deletes the user property with the given key + This method is a convenience method that deletes the property with the given key. It does nothing if no property with that key exists. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. + """ + ... + def downcast(self) -> DEdge: + r""" + @brief Gets the corresponding object without the properties + """ + ... + @overload + def move(self, dx: Optional[float] = ..., dy: Optional[float] = ...) -> DEdgeWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def move(self, v: DVector) -> DEdgeWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param v The distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def moved(self, dx: Optional[float] = ..., dy: Optional[float] = ...) -> DEdgeWithProperties: + r""" + @brief Returns the moved object (does not modify self) + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object. + """ + ... + @overload + def moved(self, v: DVector) -> DEdgeWithProperties: + r""" + @brief Returns the moved object + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param v The distance to move the object. + + @return The moved object. + """ + ... + def properties(self) -> Any: + r""" + @brief Gets the user properties + This method is a convenience method that gets the properties of the shape as a single hash. + """ + ... + def property(self, key: Any) -> Any: + r""" + @brief Gets the user property with the given key + This method is a convenience method that gets the user property with the given key. If no property with that key does not exist, it will return nil. Using that method is more convenient than using the layout object and the properties ID to retrieve the property value. + """ + ... + def set_property(self, key: Any, value: Any) -> None: + r""" + @brief Sets the user property with the given key to the given value + This method is a convenience method that sets the user property with the given key to the given value. If no property with that key exists, it will create one. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. + """ + ... + def to_s(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + @overload + def transform(self, t: DCplxTrans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transform(self, t: DTrans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transformed(self, t: DCplxTrans) -> DEdgeWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: DTrans) -> DEdgeWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: VCplxTrans) -> EdgeWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + ... + class DPath: r""" @brief A path class @@ -11105,6 +12699,265 @@ class DPath: ... ... +class DPathWithProperties(DPath): + r""" + @brief A DPath object with properties attached. + This class represents a combination of a DPath object an user properties. User properties are stored in form of a properties ID. Convenience methods are provided to manipulate or retrieve user properties directly. + + This class has been introduced in version 0.30. + """ + prop_id: int + r""" + Getter: + @brief Gets the properties ID associated with the object + + Setter: + @brief Sets the properties ID of the object + """ + @overload + @classmethod + def new(cls, path: DPath, properties: Dict[Any, Any]) -> DPathWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + @classmethod + def new(cls, path: DPath, properties_id: Optional[int] = ...) -> DPathWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + @overload + def __init__(self, path: DPath, properties: Dict[Any, Any]) -> None: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + def __init__(self, path: DPath, properties_id: Optional[int] = ...) -> None: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + def __mul__(self, f: float) -> DPathWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __repr__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def __rmul__(self, f: float) -> DPathWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __str__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def _assign(self, other: DPath) -> None: + r""" + @brief Assigns another object to self + """ + ... + def _const_cast(self) -> DPathWithProperties: + r""" + @brief Returns a non-const reference to self. + Basically, this method allows turning a const object reference to a non-const one. This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects. + + This method has been introduced in version 0.29.6. + """ + ... + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def _dup(self) -> DPathWithProperties: + r""" + @brief Creates a copy of self + """ + ... + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def _to_const_object(self) -> DPathWithProperties: + r""" + @hide + """ + ... + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def delete_property(self, key: Any) -> None: + r""" + @brief Deletes the user property with the given key + This method is a convenience method that deletes the property with the given key. It does nothing if no property with that key exists. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. + """ + ... + def downcast(self) -> DPath: + r""" + @brief Gets the corresponding object without the properties + """ + ... + @overload + def move(self, dx: Optional[float] = ..., dy: Optional[float] = ...) -> DPathWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def move(self, v: DVector) -> DPathWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param v The distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def moved(self, dx: Optional[float] = ..., dy: Optional[float] = ...) -> DPathWithProperties: + r""" + @brief Returns the moved object (does not modify self) + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object. + """ + ... + @overload + def moved(self, v: DVector) -> DPathWithProperties: + r""" + @brief Returns the moved object + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param v The distance to move the object. + + @return The moved object. + """ + ... + def properties(self) -> Any: + r""" + @brief Gets the user properties + This method is a convenience method that gets the properties of the shape as a single hash. + """ + ... + def property(self, key: Any) -> Any: + r""" + @brief Gets the user property with the given key + This method is a convenience method that gets the user property with the given key. If no property with that key does not exist, it will return nil. Using that method is more convenient than using the layout object and the properties ID to retrieve the property value. + """ + ... + def set_property(self, key: Any, value: Any) -> None: + r""" + @brief Sets the user property with the given key to the given value + This method is a convenience method that sets the user property with the given key to the given value. If no property with that key exists, it will create one. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. + """ + ... + def to_s(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + @overload + def transform(self, t: DCplxTrans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transform(self, t: DTrans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transformed(self, t: DCplxTrans) -> DPathWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: DTrans) -> DPathWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: VCplxTrans) -> PathWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + ... + class DPoint: r""" @brief A point class with double (floating-point) coordinates @@ -12462,6 +14315,265 @@ class DPolygon: ... ... +class DPolygonWithProperties(DPolygon): + r""" + @brief A DPolygon object with properties attached. + This class represents a combination of a DPolygon object an user properties. User properties are stored in form of a properties ID. Convenience methods are provided to manipulate or retrieve user properties directly. + + This class has been introduced in version 0.30. + """ + prop_id: int + r""" + Getter: + @brief Gets the properties ID associated with the object + + Setter: + @brief Sets the properties ID of the object + """ + @overload + @classmethod + def new(cls, polygon: DPolygon, properties: Dict[Any, Any]) -> DPolygonWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + @classmethod + def new(cls, polygon: DPolygon, properties_id: Optional[int] = ...) -> DPolygonWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + @overload + def __init__(self, polygon: DPolygon, properties: Dict[Any, Any]) -> None: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + def __init__(self, polygon: DPolygon, properties_id: Optional[int] = ...) -> None: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + def __mul__(self, f: float) -> DPolygonWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __repr__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def __rmul__(self, f: float) -> DPolygonWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __str__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def _assign(self, other: DPolygon) -> None: + r""" + @brief Assigns another object to self + """ + ... + def _const_cast(self) -> DPolygonWithProperties: + r""" + @brief Returns a non-const reference to self. + Basically, this method allows turning a const object reference to a non-const one. This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects. + + This method has been introduced in version 0.29.6. + """ + ... + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def _dup(self) -> DPolygonWithProperties: + r""" + @brief Creates a copy of self + """ + ... + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def _to_const_object(self) -> DPolygonWithProperties: + r""" + @hide + """ + ... + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def delete_property(self, key: Any) -> None: + r""" + @brief Deletes the user property with the given key + This method is a convenience method that deletes the property with the given key. It does nothing if no property with that key exists. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. + """ + ... + def downcast(self) -> DPolygon: + r""" + @brief Gets the corresponding object without the properties + """ + ... + @overload + def move(self, dx: Optional[float] = ..., dy: Optional[float] = ...) -> DPolygonWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def move(self, v: DVector) -> DPolygonWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param v The distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def moved(self, dx: Optional[float] = ..., dy: Optional[float] = ...) -> DPolygonWithProperties: + r""" + @brief Returns the moved object (does not modify self) + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object. + """ + ... + @overload + def moved(self, v: DVector) -> DPolygonWithProperties: + r""" + @brief Returns the moved object + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param v The distance to move the object. + + @return The moved object. + """ + ... + def properties(self) -> Any: + r""" + @brief Gets the user properties + This method is a convenience method that gets the properties of the shape as a single hash. + """ + ... + def property(self, key: Any) -> Any: + r""" + @brief Gets the user property with the given key + This method is a convenience method that gets the user property with the given key. If no property with that key does not exist, it will return nil. Using that method is more convenient than using the layout object and the properties ID to retrieve the property value. + """ + ... + def set_property(self, key: Any, value: Any) -> None: + r""" + @brief Sets the user property with the given key to the given value + This method is a convenience method that sets the user property with the given key to the given value. If no property with that key exists, it will create one. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. + """ + ... + def to_s(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + @overload + def transform(self, t: DCplxTrans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transform(self, t: DTrans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transformed(self, t: DCplxTrans) -> DPolygonWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: DTrans) -> DPolygonWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: VCplxTrans) -> PolygonWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + ... + class DSimplePolygon: r""" @brief A simple polygon class @@ -13122,6 +15234,265 @@ class DSimplePolygon: ... ... +class DSimplePolygonWithProperties(DSimplePolygon): + r""" + @brief A DSimplePolygon object with properties attached. + This class represents a combination of a DSimplePolygon object an user properties. User properties are stored in form of a properties ID. Convenience methods are provided to manipulate or retrieve user properties directly. + + This class has been introduced in version 0.30. + """ + prop_id: int + r""" + Getter: + @brief Gets the properties ID associated with the object + + Setter: + @brief Sets the properties ID of the object + """ + @overload + @classmethod + def new(cls, polygon: DSimplePolygon, properties: Dict[Any, Any]) -> DSimplePolygonWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + @classmethod + def new(cls, polygon: DSimplePolygon, properties_id: Optional[int] = ...) -> DSimplePolygonWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + @overload + def __init__(self, polygon: DSimplePolygon, properties: Dict[Any, Any]) -> None: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + def __init__(self, polygon: DSimplePolygon, properties_id: Optional[int] = ...) -> None: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + def __mul__(self, f: float) -> DSimplePolygonWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __repr__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def __rmul__(self, f: float) -> DSimplePolygonWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __str__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def _assign(self, other: DSimplePolygon) -> None: + r""" + @brief Assigns another object to self + """ + ... + def _const_cast(self) -> DSimplePolygonWithProperties: + r""" + @brief Returns a non-const reference to self. + Basically, this method allows turning a const object reference to a non-const one. This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects. + + This method has been introduced in version 0.29.6. + """ + ... + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def _dup(self) -> DSimplePolygonWithProperties: + r""" + @brief Creates a copy of self + """ + ... + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def _to_const_object(self) -> DSimplePolygonWithProperties: + r""" + @hide + """ + ... + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def delete_property(self, key: Any) -> None: + r""" + @brief Deletes the user property with the given key + This method is a convenience method that deletes the property with the given key. It does nothing if no property with that key exists. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. + """ + ... + def downcast(self) -> DSimplePolygon: + r""" + @brief Gets the corresponding object without the properties + """ + ... + @overload + def move(self, dx: Optional[float] = ..., dy: Optional[float] = ...) -> DSimplePolygonWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def move(self, v: DVector) -> DSimplePolygonWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param v The distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def moved(self, dx: Optional[float] = ..., dy: Optional[float] = ...) -> DSimplePolygonWithProperties: + r""" + @brief Returns the moved object (does not modify self) + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object. + """ + ... + @overload + def moved(self, v: DVector) -> DSimplePolygonWithProperties: + r""" + @brief Returns the moved object + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param v The distance to move the object. + + @return The moved object. + """ + ... + def properties(self) -> Any: + r""" + @brief Gets the user properties + This method is a convenience method that gets the properties of the shape as a single hash. + """ + ... + def property(self, key: Any) -> Any: + r""" + @brief Gets the user property with the given key + This method is a convenience method that gets the user property with the given key. If no property with that key does not exist, it will return nil. Using that method is more convenient than using the layout object and the properties ID to retrieve the property value. + """ + ... + def set_property(self, key: Any, value: Any) -> None: + r""" + @brief Sets the user property with the given key to the given value + This method is a convenience method that sets the user property with the given key to the given value. If no property with that key exists, it will create one. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. + """ + ... + def to_s(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + @overload + def transform(self, t: DCplxTrans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transform(self, t: DTrans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transformed(self, t: DCplxTrans) -> DSimplePolygonWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: DTrans) -> DSimplePolygonWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: VCplxTrans) -> SimplePolygonWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + ... + class DText: r""" @brief A text object @@ -13642,6 +16013,265 @@ class DText: ... ... +class DTextWithProperties(DText): + r""" + @brief A DText object with properties attached. + This class represents a combination of a DText object an user properties. User properties are stored in form of a properties ID. Convenience methods are provided to manipulate or retrieve user properties directly. + + This class has been introduced in version 0.30. + """ + prop_id: int + r""" + Getter: + @brief Gets the properties ID associated with the object + + Setter: + @brief Sets the properties ID of the object + """ + @overload + @classmethod + def new(cls, text: DText, properties: Dict[Any, Any]) -> DTextWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + @classmethod + def new(cls, text: DText, properties_id: Optional[int] = ...) -> DTextWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + @overload + def __init__(self, text: DText, properties: Dict[Any, Any]) -> None: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + def __init__(self, text: DText, properties_id: Optional[int] = ...) -> None: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + def __mul__(self, f: float) -> DTextWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __repr__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def __rmul__(self, f: float) -> DTextWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __str__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def _assign(self, other: DText) -> None: + r""" + @brief Assigns another object to self + """ + ... + def _const_cast(self) -> DTextWithProperties: + r""" + @brief Returns a non-const reference to self. + Basically, this method allows turning a const object reference to a non-const one. This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects. + + This method has been introduced in version 0.29.6. + """ + ... + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def _dup(self) -> DTextWithProperties: + r""" + @brief Creates a copy of self + """ + ... + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def _to_const_object(self) -> DTextWithProperties: + r""" + @hide + """ + ... + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def delete_property(self, key: Any) -> None: + r""" + @brief Deletes the user property with the given key + This method is a convenience method that deletes the property with the given key. It does nothing if no property with that key exists. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. + """ + ... + def downcast(self) -> DText: + r""" + @brief Gets the corresponding object without the properties + """ + ... + @overload + def move(self, dx: Optional[float] = ..., dy: Optional[float] = ...) -> DTextWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def move(self, v: DVector) -> DTextWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param v The distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def moved(self, dx: Optional[float] = ..., dy: Optional[float] = ...) -> DTextWithProperties: + r""" + @brief Returns the moved object (does not modify self) + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object. + """ + ... + @overload + def moved(self, v: DVector) -> DTextWithProperties: + r""" + @brief Returns the moved object + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param v The distance to move the object. + + @return The moved object. + """ + ... + def properties(self) -> Any: + r""" + @brief Gets the user properties + This method is a convenience method that gets the properties of the shape as a single hash. + """ + ... + def property(self, key: Any) -> Any: + r""" + @brief Gets the user property with the given key + This method is a convenience method that gets the user property with the given key. If no property with that key does not exist, it will return nil. Using that method is more convenient than using the layout object and the properties ID to retrieve the property value. + """ + ... + def set_property(self, key: Any, value: Any) -> None: + r""" + @brief Sets the user property with the given key to the given value + This method is a convenience method that sets the user property with the given key to the given value. If no property with that key exists, it will create one. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. + """ + ... + def to_s(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + @overload + def transform(self, t: DCplxTrans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transform(self, t: DTrans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transformed(self, t: DCplxTrans) -> DTextWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: DTrans) -> DTextWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: VCplxTrans) -> TextWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + ... + class DTrans: r""" @brief A simple transformation @@ -13992,6 +16622,19 @@ class DTrans: """ ... @overload + def __mul__(self, box: DBoxWithProperties) -> DBoxWithProperties: + r""" + @brief Transforms a box with properties + + 't*box' or 't.trans(box)' is equivalent to box.transformed(t). + + @param box The box to transform + @return The transformed box + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, d: float) -> float: r""" @brief Transforms a single distance @@ -14020,6 +16663,45 @@ class DTrans: """ ... @overload + def __mul__(self, edge: DEdgeWithProperties) -> DEdgeWithProperties: + r""" + @brief Transforms an edge with properties + + 't*edge' or 't.trans(edge)' is equivalent to edge.transformed(t). + + @param edge The edge to transform + @return The transformed edge + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __mul__(self, edge_pair: DEdgePair) -> DEdgePair: + r""" + @brief Transforms an edge pair + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __mul__(self, edge_pair: DEdgePairWithProperties) -> DEdgePairWithProperties: + r""" + @brief Transforms an edge pair with properties + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, p: DPoint) -> DPoint: r""" @brief Transforms a point @@ -14047,6 +16729,19 @@ class DTrans: """ ... @overload + def __mul__(self, path: DPathWithProperties) -> DPathWithProperties: + r""" + @brief Transforms a path with properties + + 't*path' or 't.trans(path)' is equivalent to path.transformed(t). + + @param path The path to transform + @return The transformed path + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, polygon: DPolygon) -> DPolygon: r""" @brief Transforms a polygon @@ -14060,6 +16755,45 @@ class DTrans: """ ... @overload + def __mul__(self, polygon: DPolygonWithProperties) -> DPolygonWithProperties: + r""" + @brief Transforms a polygon with properties + + 't*polygon' or 't.trans(polygon)' is equivalent to polygon.transformed(t). + + @param polygon The polygon to transform + @return The transformed polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __mul__(self, simple_polygon: DSimplePolygon) -> DSimplePolygon: + r""" + @brief Transforms a simple polygon + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __mul__(self, simple_polygon: DSimplePolygonWithProperties) -> DSimplePolygonWithProperties: + r""" + @brief Transforms a simple polygon with properties + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, t: DTrans) -> DTrans: r""" @brief Returns the concatenated transformation @@ -14084,6 +16818,19 @@ class DTrans: """ ... @overload + def __mul__(self, text: DTextWithProperties) -> DTextWithProperties: + r""" + @brief Transforms a text with properties + + 't*text' or 't.trans(text)' is equivalent to text.transformed(t). + + @param text The text to transform + @return The transformed text + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, v: DVector) -> DVector: r""" @brief Transforms a vector @@ -14124,6 +16871,19 @@ class DTrans: """ ... @overload + def __rmul__(self, box: DBoxWithProperties) -> DBoxWithProperties: + r""" + @brief Transforms a box with properties + + 't*box' or 't.trans(box)' is equivalent to box.transformed(t). + + @param box The box to transform + @return The transformed box + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, d: float) -> float: r""" @brief Transforms a single distance @@ -14152,6 +16912,45 @@ class DTrans: """ ... @overload + def __rmul__(self, edge: DEdgeWithProperties) -> DEdgeWithProperties: + r""" + @brief Transforms an edge with properties + + 't*edge' or 't.trans(edge)' is equivalent to edge.transformed(t). + + @param edge The edge to transform + @return The transformed edge + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __rmul__(self, edge_pair: DEdgePair) -> DEdgePair: + r""" + @brief Transforms an edge pair + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __rmul__(self, edge_pair: DEdgePairWithProperties) -> DEdgePairWithProperties: + r""" + @brief Transforms an edge pair with properties + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, p: DPoint) -> DPoint: r""" @brief Transforms a point @@ -14179,6 +16978,19 @@ class DTrans: """ ... @overload + def __rmul__(self, path: DPathWithProperties) -> DPathWithProperties: + r""" + @brief Transforms a path with properties + + 't*path' or 't.trans(path)' is equivalent to path.transformed(t). + + @param path The path to transform + @return The transformed path + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, polygon: DPolygon) -> DPolygon: r""" @brief Transforms a polygon @@ -14192,6 +17004,45 @@ class DTrans: """ ... @overload + def __rmul__(self, polygon: DPolygonWithProperties) -> DPolygonWithProperties: + r""" + @brief Transforms a polygon with properties + + 't*polygon' or 't.trans(polygon)' is equivalent to polygon.transformed(t). + + @param polygon The polygon to transform + @return The transformed polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __rmul__(self, simple_polygon: DSimplePolygon) -> DSimplePolygon: + r""" + @brief Transforms a simple polygon + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __rmul__(self, simple_polygon: DSimplePolygonWithProperties) -> DSimplePolygonWithProperties: + r""" + @brief Transforms a simple polygon with properties + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, text: DText) -> DText: r""" @brief Transforms a text @@ -14205,6 +17056,19 @@ class DTrans: """ ... @overload + def __rmul__(self, text: DTextWithProperties) -> DTextWithProperties: + r""" + @brief Transforms a text with properties + + 't*text' or 't.trans(text)' is equivalent to text.transformed(t). + + @param text The text to transform + @return The transformed text + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, v: DVector) -> DVector: r""" @brief Transforms a vector @@ -14397,6 +17261,19 @@ class DTrans: """ ... @overload + def trans(self, box: DBoxWithProperties) -> DBoxWithProperties: + r""" + @brief Transforms a box with properties + + 't*box' or 't.trans(box)' is equivalent to box.transformed(t). + + @param box The box to transform + @return The transformed box + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, edge: DEdge) -> DEdge: r""" @brief Transforms an edge @@ -14410,6 +17287,45 @@ class DTrans: """ ... @overload + def trans(self, edge: DEdgeWithProperties) -> DEdgeWithProperties: + r""" + @brief Transforms an edge with properties + + 't*edge' or 't.trans(edge)' is equivalent to edge.transformed(t). + + @param edge The edge to transform + @return The transformed edge + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def trans(self, edge_pair: DEdgePair) -> DEdgePair: + r""" + @brief Transforms an edge pair + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def trans(self, edge_pair: DEdgePairWithProperties) -> DEdgePairWithProperties: + r""" + @brief Transforms an edge pair with properties + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, p: DPoint) -> DPoint: r""" @brief Transforms a point @@ -14437,6 +17353,19 @@ class DTrans: """ ... @overload + def trans(self, path: DPathWithProperties) -> DPathWithProperties: + r""" + @brief Transforms a path with properties + + 't*path' or 't.trans(path)' is equivalent to path.transformed(t). + + @param path The path to transform + @return The transformed path + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, polygon: DPolygon) -> DPolygon: r""" @brief Transforms a polygon @@ -14450,6 +17379,45 @@ class DTrans: """ ... @overload + def trans(self, polygon: DPolygonWithProperties) -> DPolygonWithProperties: + r""" + @brief Transforms a polygon with properties + + 't*polygon' or 't.trans(polygon)' is equivalent to polygon.transformed(t). + + @param polygon The polygon to transform + @return The transformed polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def trans(self, simple_polygon: DSimplePolygon) -> DSimplePolygon: + r""" + @brief Transforms a simple polygon + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def trans(self, simple_polygon: DSimplePolygonWithProperties) -> DSimplePolygonWithProperties: + r""" + @brief Transforms a simple polygon with properties + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, text: DText) -> DText: r""" @brief Transforms a text @@ -14463,6 +17431,19 @@ class DTrans: """ ... @overload + def trans(self, text: DTextWithProperties) -> DTextWithProperties: + r""" + @brief Transforms a text with properties + + 't*text' or 't.trans(text)' is equivalent to text.transformed(t). + + @param text The text to transform + @return The transformed text + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, v: DVector) -> DVector: r""" @brief Transforms a vector @@ -15453,7 +18434,7 @@ class Device(NetlistObject): @overload def net_for_terminal(self, terminal_name: str) -> Net: r""" - @brief Gets the net connected to the specified terminal (non-const version). + @brief Gets the net connected to the specified terminal. If the terminal is not connected, nil is returned for the net. This convenience method has been introduced in version 0.27.3. @@ -15462,7 +18443,7 @@ class Device(NetlistObject): @overload def net_for_terminal(self, terminal_name: str) -> Net: r""" - @brief Gets the net connected to the specified terminal. + @brief Gets the net connected to the specified terminal (non-const version). If the terminal is not connected, nil is returned for the net. This convenience method has been introduced in version 0.27.3. @@ -15494,6 +18475,42 @@ class Device(NetlistObject): If the parameter name is not valid, an exception is thrown. """ ... + @overload + def terminal_ref(self, terminal_id: int) -> NetTerminalRef: + r""" + @brief Gets the terminal refeference for a specific terminal. + The terminal ref is the connector between a net and a device terminal. It knows the net the terminal is connected to and is useful to obtain the shapes making the terminal of the device. If the terminal is not connected, nil is returned for the net. + + This method has been introduced in version 0.30. + """ + ... + @overload + def terminal_ref(self, terminal_id: int) -> NetTerminalRef: + r""" + @brief Gets the terminal refeference for a specific terminal (non-const version). + The terminal ref is the connector between a net and a device terminal. It knows the net the terminal is connected to and is useful to obtain the shapes making the terminal of the device. If the terminal is not connected, nil is returned for the net. + + This method has been introduced in version 0.30. + """ + ... + @overload + def terminal_ref(self, terminal_name: str) -> NetTerminalRef: + r""" + @brief Gets the terminal refeference for a specific terminal where the terminal is given by name. + The terminal ref is the connector between a net and a device terminal. It knows the net the terminal is connected to and is useful to obtain the shapes making the terminal of the device. If the terminal is not connected, nil is returned for the net. + + This method has been introduced in version 0.30. + """ + ... + @overload + def terminal_ref(self, terminal_name: str) -> NetTerminalRef: + r""" + @brief Gets the terminal refeference for a specific terminal where the terminal is given by name (non-const version). + The terminal ref is the connector between a net and a device terminal. It knows the net the terminal is connected to and is useful to obtain the shapes making the terminal of the device. If the terminal is not connected, nil is returned for the net. + + This method has been introduced in version 0.30. + """ + ... ... class DeviceAbstract: @@ -19005,7 +22022,7 @@ class Edge: @return True, if the edge is 'less' as the other edge with respect to first and second point """ ... - def __mul__(self, scale_factor: float) -> Edge: + def __mul__(self, scale_factor: float) -> DEdge: r""" @brief Scale edge @@ -19032,7 +22049,7 @@ class Edge: The DBU argument has been added in version 0.27.6. """ ... - def __rmul__(self, scale_factor: float) -> Edge: + def __rmul__(self, scale_factor: float) -> DEdge: r""" @brief Scale edge @@ -19654,7 +22671,7 @@ class Edge: ... ... -class EdgeFilter: +class EdgeFilter(EdgeFilterBase): r""" @brief A generic edge filter adaptor @@ -19691,47 +22708,6 @@ class EdgeFilter: This class has been introduced in version 0.29. """ - requires_raw_input: bool - r""" - Getter: - @brief Gets a value indicating whether the filter needs raw (unmerged) input - See \requires_raw_input= for details. - - Setter: - @brief Sets a value indicating whether the filter needs raw (unmerged) input - This flag must be set before using this filter. It tells the filter implementation whether the filter wants to have raw input (unmerged). The default value is 'false', meaning that - the filter will receive merged polygons ('merged semantics'). - - Setting this value to false potentially saves some CPU time needed for merging the polygons. - Also, raw input means that strange shapes such as dot-like edges, self-overlapping polygons, empty or degenerated polygons are preserved. - """ - wants_variants: bool - r""" - Getter: - @brief Gets a value indicating whether the filter prefers cell variants - See \wants_variants= for details. - - Setter: - @brief Sets a value indicating whether the filter prefers cell variants - This flag must be set before using this filter for hierarchical applications (deep mode). It tells the filter implementation whether cell variants should be created (true, the default) or shape propagation will be applied (false). - - This decision needs to be made, if the filter indicates that it will deliver different results - for scaled or rotated versions of the shape (see \is_isotropic and the other hints). If a cell - is present with different qualities - as seen from the top cell - the respective instances - need to be differentiated. Cell variant formation is one way, shape propagation the other way. - Typically, cell variant formation is less expensive, but the hierarchy will be modified. - """ - @classmethod - def new(cls) -> EdgeFilter: - r""" - @brief Creates a new object of this class - """ - ... - def __init__(self) -> None: - r""" - @brief Creates a new object of this class - """ - ... def _const_cast(self) -> EdgeFilter: r""" @brief Returns a non-const reference to self. @@ -19785,6 +22761,157 @@ class EdgeFilter: @brief Marks the object as no longer owned by the script side. Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + ... + +class EdgeFilterBase: + r""" + @hide + """ + requires_raw_input: bool + r""" + Getter: + @brief Gets a value indicating whether the filter needs raw (unmerged) input + See \requires_raw_input= for details. + + Setter: + @brief Sets a value indicating whether the filter needs raw (unmerged) input + This flag must be set before using this filter. It tells the filter implementation whether the filter wants to have raw input (unmerged). The default value is 'false', meaning that + the filter will receive merged polygons ('merged semantics'). + + Setting this value to false potentially saves some CPU time needed for merging the polygons. + Also, raw input means that strange shapes such as dot-like edges, self-overlapping polygons, empty or degenerated polygons are preserved. + """ + wants_variants: bool + r""" + Getter: + @brief Gets a value indicating whether the filter prefers cell variants + See \wants_variants= for details. + + Setter: + @brief Sets a value indicating whether the filter prefers cell variants + This flag must be set before using this filter for hierarchical applications (deep mode). It tells the filter implementation whether cell variants should be created (true, the default) or shape propagation will be applied (false). + + This decision needs to be made, if the filter indicates that it will deliver different results + for scaled or rotated versions of the shape (see \is_isotropic and the other hints). If a cell + is present with different qualities - as seen from the top cell - the respective instances + need to be differentiated. Cell variant formation is one way, shape propagation the other way. + Typically, cell variant formation is less expensive, but the hierarchy will be modified. + """ + @classmethod + def new(cls) -> EdgeFilterBase: + r""" + @brief Creates a new object of this class + """ + ... + @classmethod + def property_filter(cls, name: Any, value: Any, inverse: Optional[bool] = ...) -> EdgeFilterBase: + r""" + @brief Creates a single-valued property filter + @param name The name of the property to use. + @param value The value against which the property is checked (exact match). + @param inverse If true, inverts the selection - i.e. all edges without a property with the given name and value are selected. + + Apply this filter with \Edges#filtered. See \property_glob for an example. + + This feature has been introduced in version 0.30. + """ + ... + @classmethod + def property_filter_bounded(cls, name: Any, from_: Any, to: Any, inverse: Optional[bool] = ...) -> EdgeFilterBase: + r""" + @brief Creates a single-valued property filter + @param name The name of the property to use. + @param from The lower value against which the property is checked or 'nil' if no lower bound shall be used. + @param to The upper value against which the property is checked or 'nil' if no upper bound shall be used. + @param inverse If true, inverts the selection - i.e. all edges without a property with the given name and value range are selected. + + This version does a bounded match. The value of the propery needs to be larger or equal to 'from' and less than 'to'. + Apply this filter with \Edges#filtered. See \property_glob for an example. + + This feature has been introduced in version 0.30. + """ + ... + @classmethod + def property_glob(cls, name: Any, pattern: str, inverse: Optional[bool] = ..., case_sensitive: Optional[bool] = ...) -> EdgeFilterBase: + r""" + @brief Creates a single-valued property filter + @param name The name of the property to use. + @param value The glob pattern to match the property value against. + @param inverse If true, inverts the selection - i.e. all edges without a matching property are selected. + @param case_sensitive If true, the match is case sensitive (the default), if false, the match is not case sensitive. + + Apply this filter with \Edges#filtered: + + @code + # edges is a Edges object + # filtered_edges contains all edges where the 'net' property starts with 'C': + filtered_edges = edges.filtered(RBA::EdgeFilterBase::property_glob('net', 'C*')) + @/code + + This feature has been introduced in version 0.30. + """ + ... + def __init__(self) -> None: + r""" + @brief Creates a new object of this class + """ + ... + def _const_cast(self) -> EdgeFilterBase: + r""" + @brief Returns a non-const reference to self. + Basically, this method allows turning a const object reference to a non-const one. This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects. + + This method has been introduced in version 0.29.6. + """ + ... + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def _to_const_object(self) -> EdgeFilterBase: + r""" + @hide + """ + ... + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + Usually it's not required to call this method. It has been introduced in version 0.24. """ ... @@ -20187,7 +23314,7 @@ class EdgeNeighborhoodVisitor(EdgeNeighborhoodVisitorBase): """ ... @overload - def output(self, edge: Edge) -> None: + def output(self, edge: EdgeWithProperties) -> None: r""" @brief Outputs an edge Use this method from one of the callbacks (\on_edge, \begin_polygon, \end_polygon) to deliver a polygon. Note that you have to configure the result type as 'Edges' on construction of the visitor before being able to do so. @@ -20195,7 +23322,7 @@ class EdgeNeighborhoodVisitor(EdgeNeighborhoodVisitorBase): """ ... @overload - def output(self, edge_pair: EdgePair) -> None: + def output(self, edge_pair: EdgePairWithProperties) -> None: r""" @brief Outputs an edge pair Use this method from one of the callbacks (\on_edge, \begin_polygon, \end_polygon) to deliver a polygon. Note that you have to configure the result type as 'EdgePairs' on construction of the visitor before being able to do so. @@ -20203,7 +23330,7 @@ class EdgeNeighborhoodVisitor(EdgeNeighborhoodVisitorBase): """ ... @overload - def output(self, polygon: Polygon) -> None: + def output(self, polygon: PolygonWithProperties) -> None: r""" @brief Outputs a polygon Use this method from one of the callbacks (\on_edge, \begin_polygon, \end_polygon) to deliver a polygon. Note that you have to configure the result type as 'Region' on construction of the visitor before being able to do so. @@ -20940,7 +24067,7 @@ class EdgePair: ... ... -class EdgePairFilter: +class EdgePairFilter(EdgePairFilterBase): r""" @brief A generic edge pair filter adaptor @@ -20975,33 +24102,6 @@ class EdgePairFilter: This class has been introduced in version 0.29. """ - wants_variants: bool - r""" - Getter: - @brief Gets a value indicating whether the filter prefers cell variants - See \wants_variants= for details. - - Setter: - @brief Sets a value indicating whether the filter prefers cell variants - This flag must be set before using this filter for hierarchical applications (deep mode). It tells the filter implementation whether cell variants should be created (true, the default) or shape propagation will be applied (false). - - This decision needs to be made, if the filter indicates that it will deliver different results - for scaled or rotated versions of the shape (see \is_isotropic and the other hints). If a cell - is present with different qualities - as seen from the top cell - the respective instances - need to be differentiated. Cell variant formation is one way, shape propagation the other way. - Typically, cell variant formation is less expensive, but the hierarchy will be modified. - """ - @classmethod - def new(cls) -> EdgePairFilter: - r""" - @brief Creates a new object of this class - """ - ... - def __init__(self) -> None: - r""" - @brief Creates a new object of this class - """ - ... def _const_cast(self) -> EdgePairFilter: r""" @brief Returns a non-const reference to self. @@ -21055,6 +24155,157 @@ class EdgePairFilter: @brief Marks the object as no longer owned by the script side. Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + ... + +class EdgePairFilterBase: + r""" + @hide + """ + requires_raw_input: bool + r""" + Getter: + @brief Gets a value indicating whether the filter needs raw (unmerged) input + See \requires_raw_input= for details. + + Setter: + @brief Sets a value indicating whether the filter needs raw (unmerged) input + This flag must be set before using this filter. It tells the filter implementation whether the filter wants to have raw input (unmerged). The default value is 'false', meaning that + the filter will receive merged polygons ('merged semantics'). + + Setting this value to false potentially saves some CPU time needed for merging the polygons. + Also, raw input means that strange shapes such as dot-like edges, self-overlapping polygons, empty or degenerated polygons are preserved. + """ + wants_variants: bool + r""" + Getter: + @brief Gets a value indicating whether the filter prefers cell variants + See \wants_variants= for details. + + Setter: + @brief Sets a value indicating whether the filter prefers cell variants + This flag must be set before using this filter for hierarchical applications (deep mode). It tells the filter implementation whether cell variants should be created (true, the default) or shape propagation will be applied (false). + + This decision needs to be made, if the filter indicates that it will deliver different results + for scaled or rotated versions of the shape (see \is_isotropic and the other hints). If a cell + is present with different qualities - as seen from the top cell - the respective instances + need to be differentiated. Cell variant formation is one way, shape propagation the other way. + Typically, cell variant formation is less expensive, but the hierarchy will be modified. + """ + @classmethod + def new(cls) -> EdgePairFilterBase: + r""" + @brief Creates a new object of this class + """ + ... + @classmethod + def property_filter(cls, name: Any, value: Any, inverse: Optional[bool] = ...) -> EdgePairFilterBase: + r""" + @brief Creates a single-valued property filter + @param name The name of the property to use. + @param value The value against which the property is checked (exact match). + @param inverse If true, inverts the selection - i.e. all edge pairs without a property with the given name and value are selected. + + Apply this filter with \EdgePairs#filtered. See \property_glob for an example. + + This feature has been introduced in version 0.30. + """ + ... + @classmethod + def property_filter_bounded(cls, name: Any, from_: Any, to: Any, inverse: Optional[bool] = ...) -> EdgePairFilterBase: + r""" + @brief Creates a single-valued property filter + @param name The name of the property to use. + @param from The lower value against which the property is checked or 'nil' if no lower bound shall be used. + @param to The upper value against which the property is checked or 'nil' if no upper bound shall be used. + @param inverse If true, inverts the selection - i.e. all edge pairs without a property with the given name and value range are selected. + + This version does a bounded match. The value of the propery needs to be larger or equal to 'from' and less than 'to'. + Apply this filter with \EdgePairs#filtered. See \property_glob for an example. + + This feature has been introduced in version 0.30. + """ + ... + @classmethod + def property_glob(cls, name: Any, pattern: str, inverse: Optional[bool] = ..., case_sensitive: Optional[bool] = ...) -> EdgePairFilterBase: + r""" + @brief Creates a single-valued property filter + @param name The name of the property to use. + @param value The glob pattern to match the property value against. + @param inverse If true, inverts the selection - i.e. all edge pairs without a matching property are selected. + @param case_sensitive If true, the match is case sensitive (the default), if false, the match is not case sensitive. + + Apply this filter with \EdgePairs#filtered: + + @code + # edge_pairs is a EdgePairs object + # filtered_edge_pairs contains all edge pairs where the 'net' property starts with 'C': + filtered_edge_pairs = edge_pairs.filtered(RBA::EdgePairFilterBase::property_glob('net', 'C*')) + @/code + + This feature has been introduced in version 0.30. + """ + ... + def __init__(self) -> None: + r""" + @brief Creates a new object of this class + """ + ... + def _const_cast(self) -> EdgePairFilterBase: + r""" + @brief Returns a non-const reference to self. + Basically, this method allows turning a const object reference to a non-const one. This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects. + + This method has been introduced in version 0.29.6. + """ + ... + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def _to_const_object(self) -> EdgePairFilterBase: + r""" + @hide + """ + ... + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + Usually it's not required to call this method. It has been introduced in version 0.24. """ ... @@ -21591,6 +24842,265 @@ class EdgePairToPolygonOperator: ... ... +class EdgePairWithProperties(EdgePair): + r""" + @brief A EdgePair object with properties attached. + This class represents a combination of a EdgePair object an user properties. User properties are stored in form of a properties ID. Convenience methods are provided to manipulate or retrieve user properties directly. + + This class has been introduced in version 0.30. + """ + prop_id: int + r""" + Getter: + @brief Gets the properties ID associated with the object + + Setter: + @brief Sets the properties ID of the object + """ + @overload + @classmethod + def new(cls, edge_pair: EdgePair, properties: Dict[Any, Any]) -> EdgePairWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + @classmethod + def new(cls, edge_pair: EdgePair, properties_id: Optional[int] = ...) -> EdgePairWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + @overload + def __init__(self, edge_pair: EdgePair, properties: Dict[Any, Any]) -> None: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + def __init__(self, edge_pair: EdgePair, properties_id: Optional[int] = ...) -> None: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + def __mul__(self, f: float) -> EdgePairWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __repr__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def __rmul__(self, f: float) -> EdgePairWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __str__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def _assign(self, other: EdgePair) -> None: + r""" + @brief Assigns another object to self + """ + ... + def _const_cast(self) -> EdgePairWithProperties: + r""" + @brief Returns a non-const reference to self. + Basically, this method allows turning a const object reference to a non-const one. This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects. + + This method has been introduced in version 0.29.6. + """ + ... + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def _dup(self) -> EdgePairWithProperties: + r""" + @brief Creates a copy of self + """ + ... + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def _to_const_object(self) -> EdgePairWithProperties: + r""" + @hide + """ + ... + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def delete_property(self, key: Any) -> None: + r""" + @brief Deletes the user property with the given key + This method is a convenience method that deletes the property with the given key. It does nothing if no property with that key exists. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. + """ + ... + def downcast(self) -> EdgePair: + r""" + @brief Gets the corresponding object without the properties + """ + ... + @overload + def move(self, dx: Optional[int] = ..., dy: Optional[int] = ...) -> EdgePairWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def move(self, v: Vector) -> EdgePairWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param v The distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def moved(self, dx: Optional[int] = ..., dy: Optional[int] = ...) -> EdgePairWithProperties: + r""" + @brief Returns the moved object (does not modify self) + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object. + """ + ... + @overload + def moved(self, v: Vector) -> EdgePairWithProperties: + r""" + @brief Returns the moved object + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param v The distance to move the object. + + @return The moved object. + """ + ... + def properties(self) -> Any: + r""" + @brief Gets the user properties + This method is a convenience method that gets the properties of the shape as a single hash. + """ + ... + def property(self, key: Any) -> Any: + r""" + @brief Gets the user property with the given key + This method is a convenience method that gets the user property with the given key. If no property with that key does not exist, it will return nil. Using that method is more convenient than using the layout object and the properties ID to retrieve the property value. + """ + ... + def set_property(self, key: Any, value: Any) -> None: + r""" + @brief Sets the user property with the given key to the given value + This method is a convenience method that sets the user property with the given key to the given value. If no property with that key exists, it will create one. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. + """ + ... + def to_s(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + @overload + def transform(self, t: ICplxTrans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transform(self, t: Trans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transformed(self, t: CplxTrans) -> DEdgePairWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: ICplxTrans) -> EdgePairWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: Trans) -> EdgePairWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + ... + class EdgePairs(ShapeCollection): r""" @brief EdgePairs (a collection of edge pairs) @@ -21610,6 +25120,13 @@ class EdgePairs(ShapeCollection): ... @overload @classmethod + def new(cls, array: Sequence[EdgePairWithProperties], dummy: Optional[bool] = ...) -> EdgePairs: + r""" + @hide + """ + ... + @overload + @classmethod def new(cls, array: Sequence[EdgePair]) -> EdgePairs: r""" @brief Constructor from an edge pair array @@ -21632,6 +25149,17 @@ class EdgePairs(ShapeCollection): ... @overload @classmethod + def new(cls, edge_pair: EdgePairWithProperties) -> EdgePairs: + r""" + @brief Constructor from a single edge pair object with properties + + This constructor creates an edge pair collection with a single edge pair. + + This constructor has been introduced in version 0.30. + """ + ... + @overload + @classmethod def new(cls, shape_iterator: RecursiveShapeIterator) -> EdgePairs: r""" @brief Constructor from a hierarchical shape set @@ -21785,6 +25313,12 @@ class EdgePairs(ShapeCollection): """ ... @overload + def __init__(self, array: Sequence[EdgePairWithProperties], dummy: Optional[bool] = ...) -> None: + r""" + @hide + """ + ... + @overload def __init__(self, array: Sequence[EdgePair]) -> None: r""" @brief Constructor from an edge pair array @@ -21805,6 +25339,16 @@ class EdgePairs(ShapeCollection): """ ... @overload + def __init__(self, edge_pair: EdgePairWithProperties) -> None: + r""" + @brief Constructor from a single edge pair object with properties + + This constructor creates an edge pair collection with a single edge pair. + + This constructor has been introduced in version 0.30. + """ + ... + @overload def __init__(self, shape_iterator: RecursiveShapeIterator) -> None: r""" @brief Constructor from a hierarchical shape set @@ -21899,9 +25443,11 @@ class EdgePairs(ShapeCollection): This constructor has been introduced in version 0.26. """ ... - def __iter__(self) -> Iterator[EdgePair]: + def __iter__(self) -> Iterator[EdgePairWithProperties]: r""" @brief Returns each edge pair of the edge pair collection + + Starting with version 0.30, the iterator delivers EdgePairWithProperties objects. """ ... def __len__(self) -> int: @@ -22044,9 +25590,11 @@ class EdgePairs(ShapeCollection): @brief Creates a copy of self """ ... - def each(self) -> Iterator[EdgePair]: + def each(self) -> Iterator[EdgePairWithProperties]: r""" @brief Returns each edge pair of the edge pair collection + + Starting with version 0.30, the iterator delivers EdgePairWithProperties objects. """ ... def edges(self) -> Edges: @@ -22097,7 +25645,7 @@ class EdgePairs(ShapeCollection): The boxes will not be merged, so it is possible to determine overlaps of these boxes for example. """ ... - def filter(self, filter: EdgePairFilter) -> None: + def filter(self, filter: EdgePairFilterBase) -> None: r""" @brief Applies a generic filter in place (replacing the edge pairs from the EdgePair collection) See \EdgePairFilter for a description of this feature. @@ -22114,7 +25662,7 @@ class EdgePairs(ShapeCollection): This method has been introduced in version 0.28.4. """ ... - def filtered(self, filter: EdgePairFilter) -> EdgePairs: + def filtered(self, filter: EdgePairFilterBase) -> EdgePairs: r""" @brief Applies a generic filter and returns a filtered copy See \EdgePairFilter for a description of this feature. @@ -22160,6 +25708,14 @@ class EdgePairs(ShapeCollection): """ ... @overload + def insert(self, edge_pair: EdgePairWithProperties) -> None: + r""" + @brief Inserts an edge pair with properties into the collection + + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, edge_pairs: EdgePairs) -> None: r""" @brief Inserts all edge pairs from the other edge pair collection into this edge pair collection @@ -24638,6 +28194,265 @@ class EdgeToPolygonOperator: ... ... +class EdgeWithProperties(Edge): + r""" + @brief A Edge object with properties attached. + This class represents a combination of a Edge object an user properties. User properties are stored in form of a properties ID. Convenience methods are provided to manipulate or retrieve user properties directly. + + This class has been introduced in version 0.30. + """ + prop_id: int + r""" + Getter: + @brief Gets the properties ID associated with the object + + Setter: + @brief Sets the properties ID of the object + """ + @overload + @classmethod + def new(cls, edge: Edge, properties: Dict[Any, Any]) -> EdgeWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + @classmethod + def new(cls, edge: Edge, properties_id: Optional[int] = ...) -> EdgeWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + @overload + def __init__(self, edge: Edge, properties: Dict[Any, Any]) -> None: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + def __init__(self, edge: Edge, properties_id: Optional[int] = ...) -> None: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + def __mul__(self, f: float) -> DEdgeWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __repr__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def __rmul__(self, f: float) -> DEdgeWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __str__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def _assign(self, other: Edge) -> None: + r""" + @brief Assigns another object to self + """ + ... + def _const_cast(self) -> EdgeWithProperties: + r""" + @brief Returns a non-const reference to self. + Basically, this method allows turning a const object reference to a non-const one. This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects. + + This method has been introduced in version 0.29.6. + """ + ... + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def _dup(self) -> EdgeWithProperties: + r""" + @brief Creates a copy of self + """ + ... + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def _to_const_object(self) -> EdgeWithProperties: + r""" + @hide + """ + ... + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def delete_property(self, key: Any) -> None: + r""" + @brief Deletes the user property with the given key + This method is a convenience method that deletes the property with the given key. It does nothing if no property with that key exists. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. + """ + ... + def downcast(self) -> Edge: + r""" + @brief Gets the corresponding object without the properties + """ + ... + @overload + def move(self, dx: Optional[int] = ..., dy: Optional[int] = ...) -> EdgeWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def move(self, v: Vector) -> EdgeWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param v The distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def moved(self, dx: Optional[int] = ..., dy: Optional[int] = ...) -> EdgeWithProperties: + r""" + @brief Returns the moved object (does not modify self) + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object. + """ + ... + @overload + def moved(self, v: Vector) -> EdgeWithProperties: + r""" + @brief Returns the moved object + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param v The distance to move the object. + + @return The moved object. + """ + ... + def properties(self) -> Any: + r""" + @brief Gets the user properties + This method is a convenience method that gets the properties of the shape as a single hash. + """ + ... + def property(self, key: Any) -> Any: + r""" + @brief Gets the user property with the given key + This method is a convenience method that gets the user property with the given key. If no property with that key does not exist, it will return nil. Using that method is more convenient than using the layout object and the properties ID to retrieve the property value. + """ + ... + def set_property(self, key: Any, value: Any) -> None: + r""" + @brief Sets the user property with the given key to the given value + This method is a convenience method that sets the user property with the given key to the given value. If no property with that key exists, it will create one. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. + """ + ... + def to_s(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + @overload + def transform(self, t: ICplxTrans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transform(self, t: Trans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transformed(self, t: CplxTrans) -> DEdgeWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: ICplxTrans) -> EdgeWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: Trans) -> EdgeWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + ... + class Edges(ShapeCollection): r""" @brief A collection of edges (Not necessarily describing closed contours) @@ -24895,6 +28710,13 @@ class Edges(ShapeCollection): ... @overload @classmethod + def new(cls, array: Sequence[EdgeWithProperties], dummy: Optional[bool] = ...) -> Edges: + r""" + @hide + """ + ... + @overload + @classmethod def new(cls, array: Sequence[Edge]) -> Edges: r""" @brief Constructor from an edge array @@ -24904,12 +28726,21 @@ class Edges(ShapeCollection): ... @overload @classmethod - def new(cls, array: Sequence[Polygon]) -> Edges: + def new(cls, array: Sequence[PolygonWithProperties], dummy: Optional[bool] = ...) -> Edges: + r""" + @hide + """ + ... + @overload + @classmethod + def new(cls, array: Sequence[Polygon], dummy: Optional[bool] = ...) -> Edges: r""" @brief Constructor from a polygon array This constructor creates an edge collection from an array of polygons. The edges form the contours of the polygons. + + The dummy argument is needed internally to differentiate the constructors taking arrays of polygons and edges in case of empty arrays. Do not specify this argument. """ ... @overload @@ -24924,6 +28755,18 @@ class Edges(ShapeCollection): ... @overload @classmethod + def new(cls, box: BoxWithProperties) -> Edges: + r""" + @brief Box constructor + + This constructor creates an edge collection from a box with properties. + The edges form the contour of the box. + + This variant has been introduced in version 0.30. + """ + ... + @overload + @classmethod def new(cls, edge: Edge) -> Edges: r""" @brief Constructor from a single edge @@ -24933,6 +28776,17 @@ class Edges(ShapeCollection): ... @overload @classmethod + def new(cls, edge: EdgeWithProperties) -> Edges: + r""" + @brief Constructor from a single edge with properties + + This constructor creates an edge collection with a single edge. + + This variant has been introduced in version 0.30. + """ + ... + @overload + @classmethod def new(cls, path: Path) -> Edges: r""" @brief Path constructor @@ -24943,6 +28797,18 @@ class Edges(ShapeCollection): ... @overload @classmethod + def new(cls, path: PathWithProperties) -> Edges: + r""" + @brief Path constructor + + This constructor creates an edge collection from a path with properties. + The edges form the contour of the path. + + This variant has been introduced in version 0.30. + """ + ... + @overload + @classmethod def new(cls, polygon: Polygon) -> Edges: r""" @brief Polygon constructor @@ -24953,6 +28819,18 @@ class Edges(ShapeCollection): ... @overload @classmethod + def new(cls, polygon: PolygonWithProperties) -> Edges: + r""" + @brief Polygon constructor + + This constructor creates an edge collection from a polygon with properties. + The edges form the contour of the polygon. + + This variant has been introduced in version 0.30. + """ + ... + @overload + @classmethod def new(cls, polygon: SimplePolygon) -> Edges: r""" @brief Simple polygon constructor @@ -24963,6 +28841,18 @@ class Edges(ShapeCollection): ... @overload @classmethod + def new(cls, polygon: SimplePolygonWithProperties) -> Edges: + r""" + @brief Simple polygon constructor + + This constructor creates an edge collection from a simple polygon with properties. + The edges form the contour of the polygon. + + This variant has been introduced in version 0.30. + """ + ... + @overload + @classmethod def new(cls, shape_iterator: RecursiveShapeIterator, as_edges: Optional[bool] = ...) -> Edges: r""" @brief Constructor of a flat edge collection from a hierarchical shape set @@ -25217,6 +29107,12 @@ class Edges(ShapeCollection): """ ... @overload + def __init__(self, array: Sequence[EdgeWithProperties], dummy: Optional[bool] = ...) -> None: + r""" + @hide + """ + ... + @overload def __init__(self, array: Sequence[Edge]) -> None: r""" @brief Constructor from an edge array @@ -25225,12 +29121,20 @@ class Edges(ShapeCollection): """ ... @overload - def __init__(self, array: Sequence[Polygon]) -> None: + def __init__(self, array: Sequence[PolygonWithProperties], dummy: Optional[bool] = ...) -> None: + r""" + @hide + """ + ... + @overload + def __init__(self, array: Sequence[Polygon], dummy: Optional[bool] = ...) -> None: r""" @brief Constructor from a polygon array This constructor creates an edge collection from an array of polygons. The edges form the contours of the polygons. + + The dummy argument is needed internally to differentiate the constructors taking arrays of polygons and edges in case of empty arrays. Do not specify this argument. """ ... @overload @@ -25243,6 +29147,17 @@ class Edges(ShapeCollection): """ ... @overload + def __init__(self, box: BoxWithProperties) -> None: + r""" + @brief Box constructor + + This constructor creates an edge collection from a box with properties. + The edges form the contour of the box. + + This variant has been introduced in version 0.30. + """ + ... + @overload def __init__(self, edge: Edge) -> None: r""" @brief Constructor from a single edge @@ -25251,6 +29166,16 @@ class Edges(ShapeCollection): """ ... @overload + def __init__(self, edge: EdgeWithProperties) -> None: + r""" + @brief Constructor from a single edge with properties + + This constructor creates an edge collection with a single edge. + + This variant has been introduced in version 0.30. + """ + ... + @overload def __init__(self, path: Path) -> None: r""" @brief Path constructor @@ -25260,6 +29185,17 @@ class Edges(ShapeCollection): """ ... @overload + def __init__(self, path: PathWithProperties) -> None: + r""" + @brief Path constructor + + This constructor creates an edge collection from a path with properties. + The edges form the contour of the path. + + This variant has been introduced in version 0.30. + """ + ... + @overload def __init__(self, polygon: Polygon) -> None: r""" @brief Polygon constructor @@ -25269,6 +29205,17 @@ class Edges(ShapeCollection): """ ... @overload + def __init__(self, polygon: PolygonWithProperties) -> None: + r""" + @brief Polygon constructor + + This constructor creates an edge collection from a polygon with properties. + The edges form the contour of the polygon. + + This variant has been introduced in version 0.30. + """ + ... + @overload def __init__(self, polygon: SimplePolygon) -> None: r""" @brief Simple polygon constructor @@ -25278,6 +29225,17 @@ class Edges(ShapeCollection): """ ... @overload + def __init__(self, polygon: SimplePolygonWithProperties) -> None: + r""" + @brief Simple polygon constructor + + This constructor creates an edge collection from a simple polygon with properties. + The edges form the contour of the polygon. + + This variant has been introduced in version 0.30. + """ + ... + @overload def __init__(self, shape_iterator: RecursiveShapeIterator, as_edges: Optional[bool] = ...) -> None: r""" @brief Constructor of a flat edge collection from a hierarchical shape set @@ -25457,9 +29415,11 @@ class Edges(ShapeCollection): This method has been introduced in version 0.24.The 'not_with' alias has been introduced in version 0.28.12. """ ... - def __iter__(self) -> Iterator[Edge]: + def __iter__(self) -> Iterator[EdgeWithProperties]: r""" @brief Returns each edge of the region + + Starting with version 0.30, the iterator delivers an EdgeWithProperties object. """ ... def __ixor__(self, other: Edges) -> Edges: @@ -25767,18 +29727,20 @@ class Edges(ShapeCollection): @brief Creates a copy of self """ ... - def each(self) -> Iterator[Edge]: + def each(self) -> Iterator[EdgeWithProperties]: r""" @brief Returns each edge of the region + + Starting with version 0.30, the iterator delivers an EdgeWithProperties object. """ ... - def each_merged(self) -> Iterator[Edge]: + def each_merged(self) -> Iterator[EdgeWithProperties]: r""" @brief Returns each edge of the region In contrast to \each, this method delivers merged edges if merge semantics applies while \each delivers the original edges only. - This method has been introduced in version 0.25. + This method has been introduced in version 0.25.Starting with version 0.30, the iterator delivers an EdgeWithProperties object. """ ... def enable_progress(self, label: str) -> None: @@ -25927,7 +29889,7 @@ class Edges(ShapeCollection): The boxes will not be merged, so it is possible to determine overlaps of these boxes for example. """ ... - def filter(self, filter: EdgeFilter) -> None: + def filter(self, filter: EdgeFilterBase) -> None: r""" @brief Applies a generic filter in place (replacing the edges from the Edges collection) See \EdgeFilter for a description of this feature. @@ -25944,7 +29906,7 @@ class Edges(ShapeCollection): This method has been introduced in version 0.28.4. """ ... - def filtered(self, filter: EdgeFilter) -> Edges: + def filtered(self, filter: EdgeFilterBase) -> Edges: r""" @brief Applies a generic filter and returns a filtered copy See \EdgeFilter for a description of this feature. @@ -26001,6 +29963,16 @@ class Edges(ShapeCollection): """ ... @overload + def insert(self, box: BoxWithProperties) -> None: + r""" + @brief Inserts a box + + Inserts the edges that form the contour of the box into the edge collection with the boxes properties. + + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, edge: Edge) -> None: r""" @brief Inserts an edge @@ -26009,6 +29981,16 @@ class Edges(ShapeCollection): """ ... @overload + def insert(self, edge: EdgeWithProperties) -> None: + r""" + @brief Inserts an edge + + Inserts the edge with properties into the edge collection. + + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, edges: Edges) -> None: r""" @brief Inserts all edges from the other edge collection into this one @@ -26016,6 +29998,14 @@ class Edges(ShapeCollection): """ ... @overload + def insert(self, edges: Sequence[EdgeWithProperties]) -> None: + r""" + @brief Inserts all edges from the array into this edge collection + + This variant accepting edges with properties has been introduced in version 0.30. + """ + ... + @overload def insert(self, edges: Sequence[Edge]) -> None: r""" @brief Inserts all edges from the array into this edge collection @@ -26030,6 +30020,16 @@ class Edges(ShapeCollection): """ ... @overload + def insert(self, path: PathWithProperties) -> None: + r""" + @brief Inserts a path + + Inserts the edges that form the contour of the path into the edge collection with the properties of the path. + + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, polygon: Polygon) -> None: r""" @brief Inserts a polygon @@ -26038,6 +30038,16 @@ class Edges(ShapeCollection): """ ... @overload + def insert(self, polygon: PolygonWithProperties) -> None: + r""" + @brief Inserts a polygon + + Inserts the edges that form the contour of the polygon into the edge collection with the properties of the polygon. + + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, polygon: SimplePolygon) -> None: r""" @brief Inserts a simple polygon @@ -26046,6 +30056,24 @@ class Edges(ShapeCollection): """ ... @overload + def insert(self, polygon: SimplePolygonWithProperties) -> None: + r""" + @brief Inserts a simple polygon + + Inserts the edges that form the contour of the simple polygon into the edge collection with the properties of the polygon. + + This variant has been introduced in version 0.30. + """ + ... + @overload + def insert(self, polygons: Sequence[PolygonWithProperties]) -> None: + r""" + @brief Inserts all polygons from the array into this edge collection + + This variant accepting polygons with properties has been introduced in version 0.30. + """ + ... + @overload def insert(self, polygons: Sequence[Polygon]) -> None: r""" @brief Inserts all polygons from the array into this edge collection @@ -26881,7 +30909,7 @@ class Edges(ShapeCollection): 'zero_distance_mode' has been added in version 0.29. """ ... - def split_filter(self, filter: EdgeFilter) -> List[Edges]: + def split_filter(self, filter: EdgeFilterBase) -> List[Edges]: r""" @brief Applies a generic filter and returns a copy with all matching shapes and one with the non-matching ones See \EdgeFilter for a description of this feature. @@ -27820,11 +31848,25 @@ class GenericDeviceExtractor(DeviceExtractorBase): """ ... @overload + def error(self, message: str, geometry: DPolygonWithProperties) -> None: + r""" + @brief Issues an error with the given message and micrometer-units polygon geometry with properties + This flavor has been introduced in version 0.30. + """ + ... + @overload def error(self, message: str, geometry: Polygon) -> None: r""" @brief Issues an error with the given message and database-unit polygon geometry """ ... + @overload + def error(self, message: str, geometry: PolygonWithProperties) -> None: + r""" + @brief Issues an error with the given message and database-units polygon geometry with properties + This flavor has been introduced in version 0.30. + """ + ... def register_device_class(self, device_class: DeviceClass) -> None: r""" @brief Registers a device class. @@ -27878,12 +31920,26 @@ class GenericDeviceExtractor(DeviceExtractorBase): """ ... @overload + def warn(self, message: str, geometry: DPolygonWithProperties) -> None: + r""" + @brief Issues a warning with the given message and micrometer-units polygon geometry with properties + This flavor has been introduced in version 0.30. + """ + ... + @overload def warn(self, message: str, geometry: Polygon) -> None: r""" @brief Issues a warning with the given message and database-unit polygon geometry Warnings have been introduced in version 0.28.13. """ ... + @overload + def warn(self, message: str, geometry: PolygonWithProperties) -> None: + r""" + @brief Issues a warning with the given message and database-unit polygon geometry + This flavor has been introduced in version 0.30. + """ + ... ... class GenericDeviceParameterCompare(EqualDeviceParameters): @@ -28710,6 +32766,19 @@ class ICplxTrans: """ ... @overload + def __mul__(self, box: BoxWithProperties) -> BoxWithProperties: + r""" + @brief Transforms a box with properties + + 't*box' or 't.trans(box)' is equivalent to box.transformed(t). + + @param box The box to transform + @return The transformed box + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, d: int) -> int: r""" @brief Transforms a single distance @@ -28738,6 +32807,32 @@ class ICplxTrans: """ ... @overload + def __mul__(self, edge: EdgeWithProperties) -> EdgeWithProperties: + r""" + @brief Transforms an edge with properties + + 't*edge' or 't.trans(edge)' is equivalent to edge.transformed(t). + + @param edge The edge to transform + @return The transformed edge + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __mul__(self, edge_pair: EdgePairWithProperties) -> EdgePairWithProperties: + r""" + @brief Transforms an edge pair with properties + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, p: Point) -> Point: r""" @brief Transforms a point @@ -28779,6 +32874,19 @@ class ICplxTrans: """ ... @overload + def __mul__(self, path: PathWithProperties) -> PathWithProperties: + r""" + @brief Transforms a path with properties + + 't*path' or 't.trans(path)' is equivalent to path.transformed(t). + + @param path The path to transform + @return The transformed path + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, polygon: Polygon) -> Polygon: r""" @brief Transforms a polygon @@ -28792,6 +32900,32 @@ class ICplxTrans: """ ... @overload + def __mul__(self, polygon: PolygonWithProperties) -> PolygonWithProperties: + r""" + @brief Transforms a polygon with properties + + 't*polygon' or 't.trans(polygon)' is equivalent to polygon.transformed(t). + + @param polygon The polygon to transform + @return The transformed polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __mul__(self, simple_polygon: SimplePolygonWithProperties) -> SimplePolygonWithProperties: + r""" + @brief Transforms a simple polygon with properties + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, t: ICplxTrans) -> ICplxTrans: r""" @brief Returns the concatenated transformation @@ -28826,6 +32960,19 @@ class ICplxTrans: This convenience method has been introduced in version 0.25. """ ... + @overload + def __mul__(self, text: TextWithProperties) -> TextWithProperties: + r""" + @brief Transforms a text with properties + + 't*text' or 't.trans(text)' is equivalent to text.transformed(t). + + @param text The text to transform + @return The transformed text + + This convenience method has been introduced in version 0.30. + """ + ... def __ne__(self, other: object) -> bool: r""" @brief Tests for inequality @@ -28854,6 +33001,19 @@ class ICplxTrans: """ ... @overload + def __rmul__(self, box: BoxWithProperties) -> BoxWithProperties: + r""" + @brief Transforms a box with properties + + 't*box' or 't.trans(box)' is equivalent to box.transformed(t). + + @param box The box to transform + @return The transformed box + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, d: int) -> int: r""" @brief Transforms a single distance @@ -28882,6 +33042,32 @@ class ICplxTrans: """ ... @overload + def __rmul__(self, edge: EdgeWithProperties) -> EdgeWithProperties: + r""" + @brief Transforms an edge with properties + + 't*edge' or 't.trans(edge)' is equivalent to edge.transformed(t). + + @param edge The edge to transform + @return The transformed edge + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __rmul__(self, edge_pair: EdgePairWithProperties) -> EdgePairWithProperties: + r""" + @brief Transforms an edge pair with properties + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, p: Point) -> Point: r""" @brief Transforms a point @@ -28923,6 +33109,19 @@ class ICplxTrans: """ ... @overload + def __rmul__(self, path: PathWithProperties) -> PathWithProperties: + r""" + @brief Transforms a path with properties + + 't*path' or 't.trans(path)' is equivalent to path.transformed(t). + + @param path The path to transform + @return The transformed path + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, polygon: Polygon) -> Polygon: r""" @brief Transforms a polygon @@ -28936,6 +33135,32 @@ class ICplxTrans: """ ... @overload + def __rmul__(self, polygon: PolygonWithProperties) -> PolygonWithProperties: + r""" + @brief Transforms a polygon with properties + + 't*polygon' or 't.trans(polygon)' is equivalent to polygon.transformed(t). + + @param polygon The polygon to transform + @return The transformed polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __rmul__(self, simple_polygon: SimplePolygonWithProperties) -> SimplePolygonWithProperties: + r""" + @brief Transforms a simple polygon with properties + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, text: Text) -> Text: r""" @brief Transforms a text @@ -28948,6 +33173,19 @@ class ICplxTrans: This convenience method has been introduced in version 0.25. """ ... + @overload + def __rmul__(self, text: TextWithProperties) -> TextWithProperties: + r""" + @brief Transforms a text with properties + + 't*text' or 't.trans(text)' is equivalent to text.transformed(t). + + @param text The text to transform + @return The transformed text + + This convenience method has been introduced in version 0.30. + """ + ... def __str__(self, lazy: Optional[bool] = ..., dbu: Optional[float] = ...) -> str: r""" @brief String conversion @@ -29213,6 +33451,19 @@ class ICplxTrans: """ ... @overload + def trans(self, box: BoxWithProperties) -> BoxWithProperties: + r""" + @brief Transforms a box with properties + + 't*box' or 't.trans(box)' is equivalent to box.transformed(t). + + @param box The box to transform + @return The transformed box + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, edge: Edge) -> Edge: r""" @brief Transforms an edge @@ -29226,6 +33477,32 @@ class ICplxTrans: """ ... @overload + def trans(self, edge: EdgeWithProperties) -> EdgeWithProperties: + r""" + @brief Transforms an edge with properties + + 't*edge' or 't.trans(edge)' is equivalent to edge.transformed(t). + + @param edge The edge to transform + @return The transformed edge + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def trans(self, edge_pair: EdgePairWithProperties) -> EdgePairWithProperties: + r""" + @brief Transforms an edge pair with properties + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, p: Point) -> Point: r""" @brief Transforms a point @@ -29267,6 +33544,19 @@ class ICplxTrans: """ ... @overload + def trans(self, path: PathWithProperties) -> PathWithProperties: + r""" + @brief Transforms a path with properties + + 't*path' or 't.trans(path)' is equivalent to path.transformed(t). + + @param path The path to transform + @return The transformed path + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, polygon: Polygon) -> Polygon: r""" @brief Transforms a polygon @@ -29280,6 +33570,32 @@ class ICplxTrans: """ ... @overload + def trans(self, polygon: PolygonWithProperties) -> PolygonWithProperties: + r""" + @brief Transforms a polygon with properties + + 't*polygon' or 't.trans(polygon)' is equivalent to polygon.transformed(t). + + @param polygon The polygon to transform + @return The transformed polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def trans(self, simple_polygon: SimplePolygonWithProperties) -> SimplePolygonWithProperties: + r""" + @brief Transforms a simple polygon with properties + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, text: Text) -> Text: r""" @brief Transforms a text @@ -29292,6 +33608,19 @@ class ICplxTrans: This convenience method has been introduced in version 0.25. """ ... + @overload + def trans(self, text: TextWithProperties) -> TextWithProperties: + r""" + @brief Transforms a text with properties + + 't*text' or 't.trans(text)' is equivalent to text.transformed(t). + + @param text The text to transform + @return The transformed text + + This convenience method has been introduced in version 0.30. + """ + ... ... class IMatrix2d: @@ -30526,11 +34855,11 @@ class Instance: Starting with version 0.25 the displacement is of vector type. Setter: - @brief Sets the displacement vector for the 'a' axis in micrometer units + @brief Sets the displacement vector for the 'a' axis - Like \a= with an integer displacement, this method will set the displacement vector but it accepts a vector in micrometer units that is of \DVector type. The vector will be translated to database units internally. + If the instance was not an array instance before it is made one. - This method has been introduced in version 0.25. + This method has been introduced in version 0.23. Starting with version 0.25 the displacement is of vector type. """ b: Vector r""" @@ -31074,7 +35403,7 @@ class Instance: r""" @brief Gets the layout this instance is contained in - This method has been introduced in version 0.22. + This const version of the method has been introduced in version 0.25. """ ... @overload @@ -31082,7 +35411,7 @@ class Instance: r""" @brief Gets the layout this instance is contained in - This const version of the method has been introduced in version 0.25. + This method has been introduced in version 0.22. """ ... def pcell_declaration(self) -> PCellDeclaration_Native: @@ -33331,6 +37660,94 @@ class Layout: This method has been introduced in version 0.27. """ + @classmethod + def _class_properties_array(cls, properties_id: int) -> Any: + r""" + @brief Gets the properties set for a given properties ID + + Basically this method performs the backward conversion of the 'properties_id' method. Given a properties ID, it returns the properties set as an array. In this array, each key and the value is stored as a pair (an array with two elements). + If the properties ID is not valid, an empty array is returned. + A version that returns a hash instead of pairs of key/values, is \properties_hash. + + For details about the properties ID concept see \properties_id. + + @param properties_id The properties ID to get the properties for + @return An array of key/value pairs (see \properties_id) + + The 'properties_array' alias was introduced in version 0.29.7 and the plain 'properties' alias was deprecated. In version 0.30, this method was turned into a static (class method), providing universal conversions without need for a Layout object. + """ + ... + @classmethod + def _class_properties_hash(cls, properties_id: int) -> Any: + r""" + @brief Gets the properties set for a given properties ID as a hash + + Returns the properties for a given properties ID as a hash. + It is a convenient alternative to \properties_array, which returns an array of key/value pairs. + + For details about the properties ID concept see \properties_id. + + @param properties_id The properties ID to get the properties for + @return The hash representing the properties for the given ID (values vs. key) + + This method has been introduced in version 0.29.7. In version 0.30, this method was turned into a static (class method), providing universal conversions without need for a Layout object. + """ + ... + @overload + @classmethod + def _class_properties_id(cls, properties: Dict[Any, Any]) -> int: + r""" + @brief Gets the properties ID for a given properties set + + This variant accepts a hash of value vs. key for the properties instead of array of key/value pairs. Apart from this, it behaves like the other \properties_id variant. + + @param properties A hash of property keys/values (both keys and values can be integer, double or string) + @return The unique properties ID for that set + + This variant has been introduced in version 0.29.7 and was turned in a static (class) method in 0.30. + """ + ... + @overload + @classmethod + def _class_properties_id(cls, properties: Sequence[Any]) -> int: + r""" + @brief Gets the properties ID for a given properties set + + In most places within the system, properties are stored as properties IDs. These are numbers representative for a specific set of properties. This method allows deriving a properties ID from a list of key/value pairs. + It delivers a unique number for this set. A variant exists that takes a dict object instead of a list of key/value pairs. + + The \properties_array and \properties_hash methods allow converting the properties ID back into a list or dict object. + Individual values for a given key can be extracted using \property in the static (class) method variant. + + @code + pid = RBA::Layout::properties_id([[1, "one"], ["key", "value"]]) + # same as: + # pid = RBA::Layout::properties_id({ 1 => "one", "key" => "value" }) + + RBA::Layout::properties_hash(pid) # -> { 1 => "one", "key" => "value" } + RBA::Layout::property(pid, 1) # -> "one" + @/code + + In previous versions, these function were methods of the Layout object. Since version 0.30, they are static (class) methods. This means, that they provide a universal way of converting property sets into IDs and back, without need for a Layout object. + + @param properties The array of pairs of variants (both elements can be integer, double or string) + @return The unique properties ID for that set + """ + ... + @classmethod + def _class_property(cls, properties_id: int, key: Any) -> Any: + r""" + @brief Extracts a property value for a given key from the properties ID + + From a given properties ID, retrieves the value for a given key. If no value for this particular key exists, 'nil' is returned. + + For details about the properties ID concept see \properties_id. + + Note, that this is a static (class) method that provides a universal way of extracting property values from IDs without need for a Layout object. + + This method has been introduced in version 0.30. + """ + ... @overload @classmethod def new(cls) -> Layout: @@ -33373,6 +37790,94 @@ class Layout: Starting with version 0.25, layouts created with the default constructor are always editable. Before that version, they inherited the editable flag from the application. """ ... + @classmethod + def properties_array(cls, properties_id: int) -> Any: + r""" + @brief Gets the properties set for a given properties ID + + Basically this method performs the backward conversion of the 'properties_id' method. Given a properties ID, it returns the properties set as an array. In this array, each key and the value is stored as a pair (an array with two elements). + If the properties ID is not valid, an empty array is returned. + A version that returns a hash instead of pairs of key/values, is \properties_hash. + + For details about the properties ID concept see \properties_id. + + @param properties_id The properties ID to get the properties for + @return An array of key/value pairs (see \properties_id) + + The 'properties_array' alias was introduced in version 0.29.7 and the plain 'properties' alias was deprecated. In version 0.30, this method was turned into a static (class method), providing universal conversions without need for a Layout object. + """ + ... + @classmethod + def properties_hash(cls, properties_id: int) -> Any: + r""" + @brief Gets the properties set for a given properties ID as a hash + + Returns the properties for a given properties ID as a hash. + It is a convenient alternative to \properties_array, which returns an array of key/value pairs. + + For details about the properties ID concept see \properties_id. + + @param properties_id The properties ID to get the properties for + @return The hash representing the properties for the given ID (values vs. key) + + This method has been introduced in version 0.29.7. In version 0.30, this method was turned into a static (class method), providing universal conversions without need for a Layout object. + """ + ... + @overload + @classmethod + def properties_id(cls, properties: Dict[Any, Any]) -> int: + r""" + @brief Gets the properties ID for a given properties set + + This variant accepts a hash of value vs. key for the properties instead of array of key/value pairs. Apart from this, it behaves like the other \properties_id variant. + + @param properties A hash of property keys/values (both keys and values can be integer, double or string) + @return The unique properties ID for that set + + This variant has been introduced in version 0.29.7 and was turned in a static (class) method in 0.30. + """ + ... + @overload + @classmethod + def properties_id(cls, properties: Sequence[Any]) -> int: + r""" + @brief Gets the properties ID for a given properties set + + In most places within the system, properties are stored as properties IDs. These are numbers representative for a specific set of properties. This method allows deriving a properties ID from a list of key/value pairs. + It delivers a unique number for this set. A variant exists that takes a dict object instead of a list of key/value pairs. + + The \properties_array and \properties_hash methods allow converting the properties ID back into a list or dict object. + Individual values for a given key can be extracted using \property in the static (class) method variant. + + @code + pid = RBA::Layout::properties_id([[1, "one"], ["key", "value"]]) + # same as: + # pid = RBA::Layout::properties_id({ 1 => "one", "key" => "value" }) + + RBA::Layout::properties_hash(pid) # -> { 1 => "one", "key" => "value" } + RBA::Layout::property(pid, 1) # -> "one" + @/code + + In previous versions, these function were methods of the Layout object. Since version 0.30, they are static (class) methods. This means, that they provide a universal way of converting property sets into IDs and back, without need for a Layout object. + + @param properties The array of pairs of variants (both elements can be integer, double or string) + @return The unique properties ID for that set + """ + ... + @classmethod + def property(cls, properties_id: int, key: Any) -> Any: + r""" + @brief Extracts a property value for a given key from the properties ID + + From a given properties ID, retrieves the value for a given key. If no value for this particular key exists, 'nil' is returned. + + For details about the properties ID concept see \properties_id. + + Note, that this is a static (class) method that provides a universal way of extracting property values from IDs without need for a Layout object. + + This method has been introduced in version 0.30. + """ + ... def __copy__(self) -> Layout: r""" @brief Creates a copy of self @@ -33449,6 +37954,35 @@ class Layout: The latter may happen, if the object is owned by a C++ object which got destroyed itself. """ ... + def _inst_properties_array(self, properties_id: int) -> Any: + r""" + @hide + """ + ... + def _inst_properties_hash(self, properties_id: int) -> Any: + r""" + @hide + """ + ... + @overload + def _inst_properties_id(self, properties: Dict[Any, Any]) -> int: + r""" + @hide + """ + ... + @overload + def _inst_properties_id(self, properties: Sequence[Any]) -> int: + r""" + @hide + """ + ... + def _inst_property(self, key: Any) -> Any: + r""" + @brief Gets the Layout's user property with the given key + This method is a convenience method that gets the property with the given key. If no property with that key exists, it will return nil. Using that method is more convenient than using the properties ID to retrieve the property value. + This method has been introduced in version 0.24. + """ + ... def _is_const_object(self) -> bool: r""" @brief Returns a value indicating whether the reference is a const reference @@ -34945,70 +39479,31 @@ class Layout: """ ... @overload - def properties(self, properties_id: int) -> List[Any]: + def properties(self, properties_id: int) -> Any: r""" - @brief Gets the properties set for a given properties ID - - Basically this method performs the backward conversion of the 'properties_id' method. Given a properties ID, it returns the properties set as an array. In this array, each key and the value is stored as a pair (an array with two elements). - If the properties ID is not valid, an empty array is returned. - A version that returns a hash instead of pairs of key/values, is \properties_hash. - - @param properties_id The properties ID to get the properties for - @return An array of key/value pairs (see \properties_id) - - The 'properties_array' alias was introduced in version 0.29.7 and the plain 'properties' alias was deprecated. + @hide """ ... - def properties_array(self, properties_id: int) -> List[Any]: + def properties_array(self, properties_id: int) -> Any: r""" - @brief Gets the properties set for a given properties ID - - Basically this method performs the backward conversion of the 'properties_id' method. Given a properties ID, it returns the properties set as an array. In this array, each key and the value is stored as a pair (an array with two elements). - If the properties ID is not valid, an empty array is returned. - A version that returns a hash instead of pairs of key/values, is \properties_hash. - - @param properties_id The properties ID to get the properties for - @return An array of key/value pairs (see \properties_id) - - The 'properties_array' alias was introduced in version 0.29.7 and the plain 'properties' alias was deprecated. + @hide """ ... def properties_hash(self, properties_id: int) -> Any: r""" - @brief Gets the properties set for a given properties ID as a hash - - Returns the properties for a given properties ID as a hash. - It is a convenient alternative to \properties_array, which returns an array of key/value pairs. - - @param properties_id The properties ID to get the properties for - @return The hash representing the properties for the given ID (values vs. key) - - This method has been introduced in version 0.29.7. + @hide """ ... @overload def properties_id(self, properties: Dict[Any, Any]) -> int: r""" - @brief Gets the properties ID for a given properties set - - This variant accepts a hash of value vs. key for the properties instead of array of key/value pairs. Apart from this, it behaves like the other \properties_id variant. - - @param properties A hash of property keys/values (both keys and values can be integer, double or string) - @return The unique properties ID for that set - - This variant has been introduced in version 0.29.7. + @hide """ ... @overload def properties_id(self, properties: Sequence[Any]) -> int: r""" - @brief Gets the properties ID for a given properties set - - Before a set of properties can be attached to a shape, it must be converted into an ID that is unique for that set. The properties set must be given as a list of pairs of variants, each pair describing a name and a value. The name acts as the key for the property and does not need to be a string (it can be an integer or double value as well). - The backward conversion can be performed with the 'properties' method. - - @param properties The array of pairs of variants (both elements can be integer, double or string) - @return The unique properties ID for that set + @hide """ ... def property(self, key: Any) -> Any: @@ -36589,7 +41084,7 @@ class LayoutToNetlist: The LayoutToNetlist object can be persisted into a 'Layout to netlist database' file. This database is a storage for both the netlist and the net or circuit geometries. When reading such file into a new LayoutToNetlist object, there will be no connection to any external layout, but all the essential netlist and geometry information will be available. - The LayoutToNetlist object is also the entry point for netlist-driven algorithms such as antenna checks. + The \LayoutToNetlist object is also the entry point for netlist-driven algorithms such as antenna checks. The use model of the LayoutToNetlist object consists of five steps which need to be executed in this order. @@ -36647,7 +41142,13 @@ class LayoutToNetlist: @li Helper functions: \cell_mapping_into, \const_cell_mapping_into @/li @/ul - The \LayoutToNetlist object is also the entry point for connectivity-aware DRC checks, such as antenna checks. + Layers stored inside the LayoutToNetlist object are addressed in three ways: + + @ul + @li Through a layer index: this is an integer number that addresses the layer. Technically this is the layer index inside the internal layout (see \internal_layout). To get a layer index from a shape collection or name use \layer_index. To get all layer indexes available, use \layer_indexes. Note, that \make_layer, \make_polygon_layer and \make_text_layer also take a layer index, but this the layer index of layer in the \original_layout. @/li + @li Through a name: Alternatively to the layer index, a layer can be addressed by name, provided one was assigned. To assign a name, specify one when creating a layer with \register, \make_layer, \make_polygon_layer or \make_text_layer. To get the layer index of a named layer, use \layer_index. To get the name of a layer use \layer_name. To get the names of all layers, use \layer_names. @/li + @li As a shape collection: a layer can also be represented by a shape collection. A shape collection is either a \Region or a \Texts object. These objects do not only represent a layer, but can be used to derive new layers or do computations on layers. In order to assign names to derived shape collections, use \register. To create a new layer and a corresponding shape collection object, use \make_layer, \make_polygon_layer or \make_text_layer. To get the shape collection for a given layer index, use \layer_index, to get layer name from a shape collection, use \layer_name. To get a shape collection from a layer index, use \layer_by_index, \polygons_by_index or \texts_by_index. @/li + @/ul This class has been introduced in version 0.26. """ @@ -36897,6 +41398,7 @@ class LayoutToNetlist: r""" @brief Creates a new and empty extractor object The main objective for this constructor is to create an object suitable for reading an annotated netlist. + A default-created LayoutToNetlist object is not very useful for other purposes. """ ... @overload @@ -36920,7 +41422,7 @@ class LayoutToNetlist: The shape iterator does not need to be an actual shape iterator. It is merely used to identify the original layout and provides additional parameters such as the top cell to use and advanced options such as subtree pruning. - You can construct a dummy iteraor usable for this purpose without layers using an empty layer set: + You can construct a dummy iterator usable for this purpose without layers using an empty layer set: @code ly = ... # external layout @@ -36964,6 +41466,7 @@ class LayoutToNetlist: r""" @brief Creates a new and empty extractor object The main objective for this constructor is to create an object suitable for reading an annotated netlist. + A default-created LayoutToNetlist object is not very useful for other purposes. """ ... @overload @@ -36985,7 +41488,7 @@ class LayoutToNetlist: The shape iterator does not need to be an actual shape iterator. It is merely used to identify the original layout and provides additional parameters such as the top cell to use and advanced options such as subtree pruning. - You can construct a dummy iteraor usable for this purpose without layers using an empty layer set: + You can construct a dummy iterator usable for this purpose without layers using an empty layer set: @code ly = ... # external layout @@ -37155,7 +41658,7 @@ class LayoutToNetlist: The 'texts' parameter has been added in version 0.27.11. """ ... - def build_all_nets(self, cmap: CellMapping, target: Layout, lmap: Dict[int, Region], net_cell_name_prefix: Optional[Any] = ..., netname_prop: Optional[Any] = ..., hier_mode: Optional[LayoutToNetlist.BuildNetHierarchyMode] = ..., circuit_cell_name_prefix: Optional[Any] = ..., device_cell_name_prefix: Optional[Any] = ...) -> None: + def build_all_nets(self, cmap: CellMapping, target: Layout, lmap: Optional[Any] = ..., net_cell_name_prefix: Optional[Any] = ..., netname_prop: Optional[Any] = ..., hier_mode: Optional[LayoutToNetlist.BuildNetHierarchyMode] = ..., circuit_cell_name_prefix: Optional[Any] = ..., device_cell_name_prefix: Optional[Any] = ...) -> None: r""" @brief Builds a full hierarchical representation of the nets @@ -37165,7 +41668,9 @@ class LayoutToNetlist: If 'netname_prop' is not nil, a property with the given name is created and attached to shapes. The value of the property is the net name. - 'lmap' defines which layers are to be produced. It is map, where the keys are layer indexes in the target layout and the values are Region objects indicating the layer where shapes are to be taken from. Use \layer_by_name or \layer_by_index to get the Region object corresponding to a layer stored inside the LayoutToNetlist database. + 'lmap' defines which layers are to be produced. It is map, where the keys are layer indexes in the target layout and the values are Region or Texts objects or layer indexes, indicating the layer where shapes are to be taken from. 'lmap' can also be left nil, in which case, a layer mapping will be provided based on the layer info attributes of the layers (see \layer_info). + + 'cmap' specifies the cell mapping. Use \create_cell_mapping or \const_create_cell_mapping to define the target cells in the target layout and to derive a cell mapping. The method has three net annotation modes: @ul @@ -37192,17 +41697,19 @@ class LayoutToNetlist: using a name like device_cell_name_prefix + device name. Otherwise the device shapes are treated as part of the net. - @param cmap The mapping of internal layout to target layout for the circuit mapping + @param cmap The cell mapping (see description of this method) @param target The target layout - @param lmap Target layer indexes (keys) and net regions (values) + @param lmap The layer mapping (see description of this method) @param hier_mode See description of this method @param netname_prop An (optional) property name to which to attach the net name @param circuit_cell_name_prefix See method description @param net_cell_name_prefix See method description @param device_cell_name_prefix See above + + The 'lmap' argument has been generalized in version 0.30 and became optional. """ ... - def build_net(self, net: Net, target: Layout, target_cell: Cell, lmap: Dict[int, Region], netname_prop: Optional[Any] = ..., hier_mode: Optional[LayoutToNetlist.BuildNetHierarchyMode] = ..., circuit_cell_name_prefix: Optional[Any] = ..., device_cell_name_prefix: Optional[Any] = ...) -> None: + def build_net(self, net: Net, target: Layout, target_cell: Cell, lmap: Optional[Any] = ..., netname_prop: Optional[Any] = ..., hier_mode: Optional[LayoutToNetlist.BuildNetHierarchyMode] = ..., circuit_cell_name_prefix: Optional[Any] = ..., device_cell_name_prefix: Optional[Any] = ...) -> None: r""" @brief Builds a net representation in the given layout and cell @@ -37211,33 +41718,29 @@ class LayoutToNetlist: If 'netname_prop' is not nil, a property with the given name is created and attached to shapes. The value of the property is the net name. - 'lmap' defines which layers are to be produced. It is map, where the keys are layer indexes in the target layout and the values are Region objects indicating the layer where shapes are to be taken from. Use \layer_by_name or \layer_by_index to get the Region object corresponding to a layer stored inside the LayoutToNetlist database. + 'lmap' defines which layers are to be produced. It is map, where the keys are layer indexes in the target layout and the values are Region or Texts objects or layer indexes, indicating the layer where shapes are to be taken from. 'lmap' can also be left nil, in which case, a layer mapping will be provided based on the layer info attributes of the layers (see \layer_info). + + Also see \build_all_nets for a description of the 'hier_mode' argument. - Net hierarchy is covered in three ways: - @ul - @li No connection indicated (hier_mode == \BNH_Disconnected: the net shapes are simply put into their - respective circuits. The connections are not indicated. @/li - @li Subnet hierarchy (hier_mode == \BNH_SubcircuitCells): for each root net, a full hierarchy is built - to accommodate the subnets (see build_net in recursive mode). @/li - @li Flat (hier_mode == \BNH_Flatten): each net is flattened and put into the circuit it - belongs to. @/li - @/ul 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. Otherwise the device shapes are treated as part of the net. @param target The target layout @param target_cell The target cell - @param lmap Target layer indexes (keys) and net regions (values) + @param lmap The layer mapping (see description of this method) @param hier_mode See description of this method @param netname_prop An (optional) property name to which to attach the net name @param cell_name_prefix Chooses recursive mode if non-null @param device_cell_name_prefix See above + + The 'lmap' argument has been generalized in version 0.30 and became optional. """ ... - def build_nets(self, nets: Sequence[Net], cmap: CellMapping, target: Layout, lmap: Dict[int, Region], net_cell_name_prefix: Optional[Any] = ..., netname_prop: Optional[Any] = ..., hier_mode: Optional[LayoutToNetlist.BuildNetHierarchyMode] = ..., circuit_cell_name_prefix: Optional[Any] = ..., device_cell_name_prefix: Optional[Any] = ...) -> None: + def build_nets(self, nets: Sequence[Net], cmap: CellMapping, target: Layout, lmap: Optional[Any] = ..., net_cell_name_prefix: Optional[Any] = ..., netname_prop: Optional[Any] = ..., hier_mode: Optional[LayoutToNetlist.BuildNetHierarchyMode] = ..., circuit_cell_name_prefix: Optional[Any] = ..., device_cell_name_prefix: Optional[Any] = ...) -> None: r""" @brief Like \build_all_nets, but with the ability to select some nets. + The 'lmap' argument has been generalized in version 0.30 and became optional. """ ... @overload @@ -37344,7 +41847,7 @@ class LayoutToNetlist: r""" @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. + If some required cells do not exist there, they will be flattened into the first existing parent. """ ... def create(self) -> None: @@ -37540,7 +42043,7 @@ class LayoutToNetlist: @ul @li "" no implicit connections.@/li @li "*" to make all labels candidates for implicit connections.@/li - @li "VDD" to make all 'VDD'' nets candidates for implicit connections.@/li + @li "VDD" to make all 'VDD' nets candidates for implicit connections.@/li @li "VDD" to make all 'VDD'+suffix nets candidates for implicit connections.@/li @li "{VDD,VSS}" to all VDD and VSS nets candidates for implicit connections.@/li @/ul @@ -37581,9 +42084,13 @@ class LayoutToNetlist: ... def layer_by_index(self, index: int) -> Region: r""" - @brief Gets a layer object for the given index. + @brief Gets a \Region object for the given index. The returned object is a new Region object representing the layer with the given index. It will refer to a layer inside the internal layout, or more specifically inside the \DeepShapeStorage object (see \dss and \internal_layout). The method returns 'nil' if the index is not a valid layer index. + + It is in the responsibility of the user to use \texts_by_index or \polygons_by_index on the right layers. A layer created for text purpose should not be used with \polygons_by_index or vice versa. + + Starting with version 0.30, the preferred name for this method is \polygons_by_index to differentiate from \texts_by_index. """ ... def layer_by_name(self, name: str) -> Region: @@ -37592,8 +42099,13 @@ class LayoutToNetlist: The returned object is a new Region object representing the named layer. It will refer to a layer inside the internal layout, or more specifically inside the \DeepShapeStorage object (see \dss and \internal_layout). The method returns 'nil' if the name is not a valid layer name. See \register and the make_... methods for a description of layer naming. + + It is in the responsibility of the user to use \texts_by_index or \polygons_by_index on the right layers. A layer created for text purpose should not be used with \polygons_by_index or vice versa. + + Starting with version 0.30, the preferred name for this method is \polygons_by_index to differentiate from \texts_by_index. """ ... + @overload def layer_index(self, l: ShapeCollection) -> int: r""" @brief Gets the layer index for the given data object @@ -37607,6 +42119,14 @@ class LayoutToNetlist: This method has been introduced in version 0.29.3. """ ... + @overload + def layer_index(self, name: str) -> Any: + r""" + @brief Gets the layer index for a given name or nil if the name is not valid. + + This method has been introduced in version 0.30. + """ + ... def layer_indexes(self) -> List[int]: r""" @brief Returns a list of indexes of the layers kept inside the LayoutToNetlist object. @@ -37654,6 +42174,8 @@ class LayoutToNetlist: @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. + + A generalized version of this method is \layer_index. """ ... @overload @@ -37664,6 +42186,7 @@ class LayoutToNetlist: investigating the cluster tree. The variant for Texts collections has been added in version 0.27. + A generalized version of this method is \layer_index. """ ... @overload @@ -37688,25 +42211,93 @@ class LayoutToNetlist: The name is optional. If given, the layer will already be named accordingly (see \register). """ ... + @overload def make_polygon_layer(self, layer_index: int, name: Optional[str] = ...) -> Region: r""" @brief Creates a new region representing an original layer taking polygons only See \make_layer for details. """ ... + @overload + def make_polygon_layer(self, name: Optional[str] = ...) -> Region: + r""" + @brief Creates a new, empty hierarchical region + See \make_layer for details. + + Starting with version 0.30, the original layer index is optional for consistency with \make_layer. + """ + ... + @overload def make_text_layer(self, layer_index: int, name: Optional[str] = ...) -> Texts: r""" - @brief Creates a new region representing an original layer taking texts only + @brief Creates a new text collection representing an original layer taking texts only See \make_layer for details. Starting with version 0.27, this method returns a \Texts object. """ ... + @overload + def make_text_layer(self, name: Optional[str] = ...) -> Texts: + r""" + @brief Creates a new, empty hierarchical text collection + See \make_layer for details. + + Starting with version 0.30, the original layer index is optional, allowing to create empty, internal text layers. + """ + ... def netlist(self) -> Netlist: r""" @brief gets the netlist extracted (0 if no extraction happened yet) """ ... + def original_layout(self) -> Layout: + r""" + @brief Gets the original layout or nil if the LayoutToNetlist object is not attached to one. + + This method has been introduced in version 0.30. + """ + ... + def original_top_cell(self) -> Cell: + r""" + @brief Gets the original top cell or nil if the LayoutToNetlist object is not attached to an original layout. + + This method has been introduced in version 0.30. + """ + ... + def polygons_by_index(self, index: int) -> Region: + r""" + @brief Gets a \Region object for the given index. + The returned object is a new Region object representing the layer with the given index. It will refer to a layer inside the internal layout, or more specifically inside the \DeepShapeStorage object (see \dss and \internal_layout). + The method returns 'nil' if the index is not a valid layer index. + + It is in the responsibility of the user to use \texts_by_index or \polygons_by_index on the right layers. A layer created for text purpose should not be used with \polygons_by_index or vice versa. + + Starting with version 0.30, the preferred name for this method is \polygons_by_index to differentiate from \texts_by_index. + """ + ... + def polygons_by_name(self, name: str) -> Region: + r""" + @brief Gets a layer object for the given name. + The returned object is a new Region object representing the named layer. It will refer to a layer inside the internal layout, or more specifically inside the \DeepShapeStorage object (see \dss and \internal_layout). + The method returns 'nil' if the name is not a valid layer name. + See \register and the make_... methods for a description of layer naming. + + It is in the responsibility of the user to use \texts_by_index or \polygons_by_index on the right layers. A layer created for text purpose should not be used with \polygons_by_index or vice versa. + + Starting with version 0.30, the preferred name for this method is \polygons_by_index to differentiate from \texts_by_index. + """ + ... + def polygons_of_net(self, net: Net, of_layer: int, recursive: Optional[bool] = ..., trans: Optional[ICplxTrans] = ...) -> Region: + r""" + @brief Returns all polygons of a specific net and layer. + If 'recursive' is true, the returned region will contain the shapes of + all subcircuits too. + + The optional 'trans' parameter allows applying a transformation to all shapes. + + This method is similar to \shapes_of_net, but takes a layer index for the layer. It was introduced in version 0.30. + """ + ... @overload def probe_net(self, of_layer: Region, point: DPoint, sc_path_out: Optional[Sequence[SubCircuit]] = ..., initial_circuit: Optional[Circuit] = ...) -> Net: r""" @@ -37786,21 +42377,47 @@ class LayoutToNetlist: @overload def shapes_of_net(self, net: Net, of_layer: Region, recursive: Optional[bool] = ..., trans: Optional[ICplxTrans] = ...) -> Region: r""" - @brief Returns all shapes of a specific net and layer. - If 'recursive'' is true, the returned region will contain the shapes of + @brief Returns all polygons of a specific net and layer. + If 'recursive' is true, the returned region will contain the shapes of all subcircuits too. The optional 'trans' parameter allows applying a transformation to all shapes. It has been introduced in version 0.28.4. """ ... @overload - def shapes_of_net(self, net: Net, of_layer: Region, recursive: bool, to: Shapes, propid: Optional[int] = ..., trans: Optional[ICplxTrans] = ...) -> None: + def shapes_of_net(self, net: Net, of_layer: ShapeCollection, recursive: bool, to: Shapes, propid: Optional[int] = ..., trans: Optional[ICplxTrans] = ...) -> None: r""" @brief Sends 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 + If 'recursive' is true, the returned region will contain the shapes of all subcircuits too. - "prop_id" is an optional properties ID. If given, this property set will be attached to the shapes. + + 'prop_id' is an optional properties ID. If given, this property set will be attached to the shapes. The optional 'trans' parameter allows applying a transformation to all shapes. It has been introduced in version 0.28.4. + The 'of_layer' argument has been generalized in version 0.30 and can be a layer index, a \Region layer or a \Texts layer. + """ + ... + @overload + def shapes_of_net(self, net: Net, of_layer: Texts, recursive: Optional[bool] = ..., trans: Optional[ICplxTrans] = ...) -> Texts: + r""" + @brief Returns all texts of a specific net and layer. + If 'recursive' is true, the returned text collection will contain the shapes of + all subcircuits too. + + The optional 'trans' parameter allows applying a transformation to all shapes. + + This variant has been introduced in version 0.30. + """ + ... + @overload + def shapes_of_net(self, net: Net, of_layer: int, recursive: bool, to: Shapes, propid: Optional[int] = ..., trans: Optional[ICplxTrans] = ...) -> None: + r""" + @brief Sends 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. + + 'prop_id' is an optional properties ID. If given, this property set will be attached to the shapes. + The optional 'trans' parameter allows applying a transformation to all shapes. It has been introduced in version 0.28.4. + The 'of_layer' argument has been generalized in version 0.30 and can be a layer index, a \Region layer or a \Texts layer. """ ... def shapes_of_pin(self, pin: NetSubcircuitPinRef, trans: Optional[ICplxTrans] = ...) -> Dict[int, Region]: @@ -37898,6 +42515,40 @@ class LayoutToNetlist: Soft connections have been introduced in version 0.29. """ ... + def texts_by_index(self, index: int) -> Texts: + r""" + @brief Gets a \Texts object for the given index. + The returned object is a new Texts object representing the layer with the given index. It will refer to a layer inside the internal layout, or more specifically inside the \DeepShapeStorage object (see \dss and \internal_layout). + The method returns 'nil' if the index is not a valid layer index. + + It is in the responsibility of the user to use \texts_by_index or \polygons_by_index on the right layers. A layer created for text purpose should not be used with \polygons_by_index or vice versa. + + This method has been introduced in version 0.30. + """ + ... + def texts_by_name(self, name: str) -> Texts: + r""" + @brief Gets a layer object for the given name. + The returned object is a new Texts object representing the named layer. It will refer to a layer inside the internal layout, or more specifically inside the \DeepShapeStorage object (see \dss and \internal_layout). + The method returns 'nil' if the name is not a valid layer name. + See \register and the make_... methods for a description of layer naming. + + It is in the responsibility of the user to use \texts_by_name or \polygons_by_name on the right layers. A layer created for text purpose should not be used with \polygons_by_name or vice versa. + + This method has been introduced in version 0.30. + """ + ... + def texts_of_net(self, net: Net, of_layer: int, recursive: Optional[bool] = ..., trans: Optional[ICplxTrans] = ...) -> Texts: + r""" + @brief Returns all texts of a specific net and layer. + If 'recursive' is true, the returned region will contain the shapes of + all subcircuits too. + + The optional 'trans' parameter allows applying a transformation to all shapes. + + This method is similar to \shapes_of_net, but takes a layer index for the layer. It was introduced in version 0.30. + """ + ... def write(self, path: str, short_format: Optional[bool] = ...) -> None: r""" @brief Writes the extracted netlist to a file. @@ -39025,204 +43676,6 @@ class LoadLayoutOptions: This method has been added in version 0.26.2. """ - mebes_boundary_datatype: int - r""" - Getter: - @brief Gets the datatype number of the boundary layer to produce - See \mebes_produce_boundary= for a description of this attribute. - - This property has been added in version 0.23.10. - - Setter: - @brief Sets the datatype number of the boundary layer to produce - See \mebes_produce_boundary= for a description of this attribute. - - This property has been added in version 0.23.10. - """ - mebes_boundary_layer: int - r""" - Getter: - @brief Gets the layer number of the boundary layer to produce - See \mebes_produce_boundary= for a description of this attribute. - - This property has been added in version 0.23.10. - - Setter: - @brief Sets the layer number of the boundary layer to produce - See \mebes_produce_boundary= for a description of this attribute. - - This property has been added in version 0.23.10. - """ - mebes_boundary_name: str - r""" - Getter: - @brief Gets the name of the boundary layer to produce - See \mebes_produce_boundary= for a description of this attribute. - - This property has been added in version 0.23.10. - - Setter: - @brief Sets the name of the boundary layer to produce - See \mebes_produce_boundary= for a description of this attribute. - - This property has been added in version 0.23.10. - """ - mebes_create_other_layers: bool - r""" - Getter: - @brief Gets a value indicating whether other layers shall be created - @return True, if other layers will be created. - This attribute acts together with a layer map (see \mebes_layer_map=). Layers not listed in this map are created as well when \mebes_create_other_layers? is true. Otherwise they are ignored. - - This method has been added in version 0.25 and replaces the respective global option in \LoadLayoutOptions in a format-specific fashion. - Setter: - @brief Specifies whether other layers shall be created - @param create True, if other layers will be created. - See \mebes_create_other_layers? for a description of this attribute. - - This method has been added in version 0.25 and replaces the respective global option in \LoadLayoutOptions in a format-specific fashion. - """ - mebes_data_datatype: int - r""" - Getter: - @brief Gets the datatype number of the data layer to produce - - This property has been added in version 0.23.10. - - Setter: - @brief Sets the datatype number of the data layer to produce - - This property has been added in version 0.23.10. - """ - mebes_data_layer: int - r""" - Getter: - @brief Gets the layer number of the data layer to produce - - This property has been added in version 0.23.10. - - Setter: - @brief Sets the layer number of the data layer to produce - - This property has been added in version 0.23.10. - """ - mebes_data_name: str - r""" - Getter: - @brief Gets the name of the data layer to produce - - This property has been added in version 0.23.10. - - Setter: - @brief Sets the name of the data layer to produce - - This property has been added in version 0.23.10. - """ - mebes_invert: bool - r""" - Getter: - @brief Gets a value indicating whether to invert the MEBES pattern - If this property is set to true, the pattern will be inverted. - - This property has been added in version 0.22. - - Setter: - @brief Specify whether to invert the MEBES pattern - If this property is set to true, the pattern will be inverted. - - This property has been added in version 0.22. - """ - mebes_layer_map: LayerMap - r""" - Getter: - @brief Gets the layer map - @return The layer map. - - This method has been added in version 0.25 and replaces the respective global option in \LoadLayoutOptions in a format-specific fashion. - Setter: - @brief Sets the layer map - This sets a layer mapping for the reader. Unlike \mebes_set_layer_map, the 'create_other_layers' flag is not changed. - @param map The layer map to set. - - This convenience method has been added in version 0.26.2. - """ - mebes_num_shapes_per_cell: int - r""" - Getter: - @brief Gets the number of stripes collected per cell - See \mebes_num_stripes_per_cell= for details about this property. - - This property has been added in version 0.24.5. - - Setter: - @brief Specify the number of stripes collected per cell - See \mebes_num_stripes_per_cell= for details about this property. - - This property has been added in version 0.24.5. - """ - mebes_num_stripes_per_cell: int - r""" - Getter: - @brief Gets the number of stripes collected per cell - See \mebes_num_stripes_per_cell= for details about this property. - - This property has been added in version 0.23.10. - - Setter: - @brief Specify the number of stripes collected per cell - This property specifies how many stripes will be collected into one cell. - A smaller value means less but bigger cells. The default value is 64. - New cells will be formed whenever more than this number of stripes has been read - or a new segment is started and the number of shapes given by \mebes_num_shapes_per_cell - is exceeded. - - This property has been added in version 0.23.10. - """ - mebes_produce_boundary: bool - r""" - Getter: - @brief Gets a value indicating whether a boundary layer will be produced - See \mebes_produce_boundary= for details about this property. - - This property has been added in version 0.23.10. - - Setter: - @brief Specify whether to produce a boundary layer - If this property is set to true, the pattern boundary will be written to the layer and datatype specified with \mebes_boundary_name, \mebes_boundary_layer and \mebes_boundary_datatype. - By default, the boundary layer is produced. - - This property has been added in version 0.23.10. - """ - mebes_subresolution: bool - r""" - Getter: - @brief Gets a value indicating whether to invert the MEBES pattern - See \subresolution= for details about this property. - - This property has been added in version 0.23.10. - - Setter: - @brief Specify whether subresolution trapezoids are supported - If this property is set to true, subresolution trapezoid vertices are supported. - In order to implement support, the reader will create magnified instances with a magnification of 1/16. - By default this property is enabled. - - This property has been added in version 0.23.10. - """ - mebes_top_cell_index: int - r""" - Getter: - @brief Gets the cell index for the top cell to use - See \mebes_top_cell_index= for a description of this property. - - This property has been added in version 0.23.10. - - Setter: - @brief Specify the cell index for the top cell to use - If this property is set to a valid cell index, the MEBES reader will put the subcells and shapes into this cell. - - This property has been added in version 0.23.10. - """ oasis_expect_strict_mode: int r""" Getter: @@ -39462,26 +43915,6 @@ class LoadLayoutOptions: This method has been added in version 0.26.2. """ ... - def mebes_select_all_layers(self) -> None: - r""" - @brief Selects all layers and disables the layer map - - This disables any layer map and enables reading of all layers. - New layers will be created when required. - - This method has been added in version 0.25 and replaces the respective global option in \LoadLayoutOptions in a format-specific fashion. - """ - ... - def mebes_set_layer_map(self, map: LayerMap, create_other_layers: bool) -> None: - r""" - @brief Sets the layer map - This sets a layer mapping for the reader. The layer map allows selection and translation of the original layers. - @param map The layer map to set. - @param create_other_layers The flag indicating whether other layers will be created as well. Set to false to read only the layers in the layer map. - - This method has been added in version 0.25 and replaces the respective global option in \LoadLayoutOptions in a format-specific fashion. - """ - ... def select_all_layers(self) -> None: r""" @brief Selects all layers and disables the layer map @@ -42981,17 +47414,24 @@ class Netlist: @overload def circuit_by_cell_index(self, cell_index: int) -> Circuit: r""" - @brief Gets the circuit object for a given cell index. + @brief Gets the circuit object for a given cell index (const version). If the cell index is not valid or no circuit is registered with this index, nil is returned. + + This constness variant has been introduced in version 0.26.8. """ ... @overload def circuit_by_cell_index(self, cell_index: int) -> Circuit: r""" - @brief Gets the circuit object for a given cell index (const version). + @brief Gets the circuit object for a given cell index. If the cell index is not valid or no circuit is registered with this index, nil is returned. - - This constness variant has been introduced in version 0.26.8. + """ + ... + @overload + def circuit_by_name(self, name: str) -> Circuit: + r""" + @brief Gets the circuit object for a given name. + If the name is not a valid circuit name, nil is returned. """ ... @overload @@ -43004,13 +47444,6 @@ class Netlist: """ ... @overload - def circuit_by_name(self, name: str) -> Circuit: - r""" - @brief Gets the circuit object for a given name. - If the name is not a valid circuit name, nil is returned. - """ - ... - @overload def circuits_by_name(self, name_pattern: str) -> List[Circuit]: r""" @brief Gets the circuit objects for a given name filter. @@ -47024,7 +51457,7 @@ class Path: A path consists of an sequence of line segments forming the 'spine' of the path and a width. In addition, the starting point can be drawn back by a certain extent (the 'begin extension') and the end point can be pulled forward somewhat (by the 'end extension'). - A path may have round ends for special purposes. In particular, a round-ended path with a single point can represent a circle. Round-ended paths should have being and end extensions equal to half the width. Non-round-ended paths with a single point are allowed but the definition of the resulting shape in not well defined and may differ in other tools. + A path may have round ends for special purposes. In particular, a round-ended path with a single point can represent a circle. Round-ended paths should have begin and end extensions equal to half the width. Non-round-ended paths with a single point are allowed, but the definition of the resulting shape is not well defined and may differ in other tools. See @The Database API@ for more details about the database objects. """ @@ -47579,6 +52012,265 @@ class Path: ... ... +class PathWithProperties(Path): + r""" + @brief A Path object with properties attached. + This class represents a combination of a Path object an user properties. User properties are stored in form of a properties ID. Convenience methods are provided to manipulate or retrieve user properties directly. + + This class has been introduced in version 0.30. + """ + prop_id: int + r""" + Getter: + @brief Gets the properties ID associated with the object + + Setter: + @brief Sets the properties ID of the object + """ + @overload + @classmethod + def new(cls, path: Path, properties: Dict[Any, Any]) -> PathWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + @classmethod + def new(cls, path: Path, properties_id: Optional[int] = ...) -> PathWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + @overload + def __init__(self, path: Path, properties: Dict[Any, Any]) -> None: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + def __init__(self, path: Path, properties_id: Optional[int] = ...) -> None: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + def __mul__(self, f: float) -> DPathWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __repr__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def __rmul__(self, f: float) -> DPathWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __str__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def _assign(self, other: Path) -> None: + r""" + @brief Assigns another object to self + """ + ... + def _const_cast(self) -> PathWithProperties: + r""" + @brief Returns a non-const reference to self. + Basically, this method allows turning a const object reference to a non-const one. This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects. + + This method has been introduced in version 0.29.6. + """ + ... + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def _dup(self) -> PathWithProperties: + r""" + @brief Creates a copy of self + """ + ... + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def _to_const_object(self) -> PathWithProperties: + r""" + @hide + """ + ... + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def delete_property(self, key: Any) -> None: + r""" + @brief Deletes the user property with the given key + This method is a convenience method that deletes the property with the given key. It does nothing if no property with that key exists. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. + """ + ... + def downcast(self) -> Path: + r""" + @brief Gets the corresponding object without the properties + """ + ... + @overload + def move(self, dx: Optional[int] = ..., dy: Optional[int] = ...) -> PathWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def move(self, v: Vector) -> PathWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param v The distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def moved(self, dx: Optional[int] = ..., dy: Optional[int] = ...) -> PathWithProperties: + r""" + @brief Returns the moved object (does not modify self) + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object. + """ + ... + @overload + def moved(self, v: Vector) -> PathWithProperties: + r""" + @brief Returns the moved object + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param v The distance to move the object. + + @return The moved object. + """ + ... + def properties(self) -> Any: + r""" + @brief Gets the user properties + This method is a convenience method that gets the properties of the shape as a single hash. + """ + ... + def property(self, key: Any) -> Any: + r""" + @brief Gets the user property with the given key + This method is a convenience method that gets the user property with the given key. If no property with that key does not exist, it will return nil. Using that method is more convenient than using the layout object and the properties ID to retrieve the property value. + """ + ... + def set_property(self, key: Any, value: Any) -> None: + r""" + @brief Sets the user property with the given key to the given value + This method is a convenience method that sets the user property with the given key to the given value. If no property with that key exists, it will create one. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. + """ + ... + def to_s(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + @overload + def transform(self, t: ICplxTrans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transform(self, t: Trans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transformed(self, t: CplxTrans) -> DPathWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: ICplxTrans) -> PathWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: Trans) -> PathWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + ... + class Pin(NetlistObject): r""" @brief A pin of a circuit. @@ -49264,7 +53956,7 @@ class Polygon: ... ... -class PolygonFilter: +class PolygonFilter(PolygonFilterBase): r""" @brief A generic polygon filter adaptor @@ -49299,47 +53991,6 @@ class PolygonFilter: This class has been introduced in version 0.29. """ - requires_raw_input: bool - r""" - Getter: - @brief Gets a value indicating whether the filter needs raw (unmerged) input - See \requires_raw_input= for details. - - Setter: - @brief Sets a value indicating whether the filter needs raw (unmerged) input - This flag must be set before using this filter. It tells the filter implementation whether the filter wants to have raw input (unmerged). The default value is 'false', meaning that - the filter will receive merged polygons ('merged semantics'). - - Setting this value to false potentially saves some CPU time needed for merging the polygons. - Also, raw input means that strange shapes such as dot-like edges, self-overlapping polygons, empty or degenerated polygons are preserved. - """ - wants_variants: bool - r""" - Getter: - @brief Gets a value indicating whether the filter prefers cell variants - See \wants_variants= for details. - - Setter: - @brief Sets a value indicating whether the filter prefers cell variants - This flag must be set before using this filter for hierarchical applications (deep mode). It tells the filter implementation whether cell variants should be created (true, the default) or shape propagation will be applied (false). - - This decision needs to be made, if the filter indicates that it will deliver different results - for scaled or rotated versions of the shape (see \is_isotropic and the other hints). If a cell - is present with different qualities - as seen from the top cell - the respective instances - need to be differentiated. Cell variant formation is one way, shape propagation the other way. - Typically, cell variant formation is less expensive, but the hierarchy will be modified. - """ - @classmethod - def new(cls) -> PolygonFilter: - r""" - @brief Creates a new object of this class - """ - ... - def __init__(self) -> None: - r""" - @brief Creates a new object of this class - """ - ... def _const_cast(self) -> PolygonFilter: r""" @brief Returns a non-const reference to self. @@ -49393,6 +54044,157 @@ class PolygonFilter: @brief Marks the object as no longer owned by the script side. Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + ... + +class PolygonFilterBase: + r""" + @hide + """ + requires_raw_input: bool + r""" + Getter: + @brief Gets a value indicating whether the filter needs raw (unmerged) input + See \requires_raw_input= for details. + + Setter: + @brief Sets a value indicating whether the filter needs raw (unmerged) input + This flag must be set before using this filter. It tells the filter implementation whether the filter wants to have raw input (unmerged). The default value is 'false', meaning that + the filter will receive merged polygons ('merged semantics'). + + Setting this value to false potentially saves some CPU time needed for merging the polygons. + Also, raw input means that strange shapes such as dot-like edges, self-overlapping polygons, empty or degenerated polygons are preserved. + """ + wants_variants: bool + r""" + Getter: + @brief Gets a value indicating whether the filter prefers cell variants + See \wants_variants= for details. + + Setter: + @brief Sets a value indicating whether the filter prefers cell variants + This flag must be set before using this filter for hierarchical applications (deep mode). It tells the filter implementation whether cell variants should be created (true, the default) or shape propagation will be applied (false). + + This decision needs to be made, if the filter indicates that it will deliver different results + for scaled or rotated versions of the shape (see \is_isotropic and the other hints). If a cell + is present with different qualities - as seen from the top cell - the respective instances + need to be differentiated. Cell variant formation is one way, shape propagation the other way. + Typically, cell variant formation is less expensive, but the hierarchy will be modified. + """ + @classmethod + def new(cls) -> PolygonFilterBase: + r""" + @brief Creates a new object of this class + """ + ... + @classmethod + def property_filter(cls, name: Any, value: Any, inverse: Optional[bool] = ...) -> PolygonFilterBase: + r""" + @brief Creates a single-valued property filter + @param name The name of the property to use. + @param value The value against which the property is checked (exact match). + @param inverse If true, inverts the selection - i.e. all polygons without a property with the given name and value are selected. + + Apply this filter with \Region#filtered. See \property_glob for an example. + + This feature has been introduced in version 0.30. + """ + ... + @classmethod + def property_filter_bounded(cls, name: Any, from_: Any, to: Any, inverse: Optional[bool] = ...) -> PolygonFilterBase: + r""" + @brief Creates a single-valued property filter + @param name The name of the property to use. + @param from The lower value against which the property is checked or 'nil' if no lower bound shall be used. + @param to The upper value against which the property is checked or 'nil' if no upper bound shall be used. + @param inverse If true, inverts the selection - i.e. all polygons without a property with the given name and value range are selected. + + This version does a bounded match. The value of the propery needs to be larger or equal to 'from' and less than 'to'. + Apply this filter with \Region#filtered. See \property_glob for an example. + + This feature has been introduced in version 0.30. + """ + ... + @classmethod + def property_glob(cls, name: Any, pattern: str, inverse: Optional[bool] = ..., case_sensitive: Optional[bool] = ...) -> PolygonFilterBase: + r""" + @brief Creates a single-valued property filter + @param name The name of the property to use. + @param value The glob pattern to match the property value against. + @param inverse If true, inverts the selection - i.e. all polygons without a matching property are selected. + @param case_sensitive If true, the match is case sensitive (the default), if false, the match is not case sensitive. + + Apply this filter with \Region#filtered: + + @code + # region is a Region object + # filtered_region contains all polygons where the 'net' property starts with 'C': + filtered_region = region.filtered(RBA::PolygonFilterBase::property_glob('net', 'C*')) + @/code + + This feature has been introduced in version 0.30. + """ + ... + def __init__(self) -> None: + r""" + @brief Creates a new object of this class + """ + ... + def _const_cast(self) -> PolygonFilterBase: + r""" + @brief Returns a non-const reference to self. + Basically, this method allows turning a const object reference to a non-const one. This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects. + + This method has been introduced in version 0.29.6. + """ + ... + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def _to_const_object(self) -> PolygonFilterBase: + r""" + @hide + """ + ... + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + Usually it's not required to call this method. It has been introduced in version 0.24. """ ... @@ -49449,6 +54251,227 @@ class PolygonFilter: ... ... +class PolygonNeighborhoodVisitor(PolygonNeighborhoodVisitorBase): + r""" + @brief A visitor for the neighborhood of polygons in the input + + Objects of this class are passed to \PolygonNeighborhoodCompoundOperationNode constructor to handle events on each edge of the primary input along with the neighborhood taken from the additional inputs. + + See \neighbors for the description of the events delivered. + This class has been introduced in version 0.30.0. + """ + result_type: CompoundRegionOperationNode.ResultType + r""" + Getter: + @brief Gets the result type + + Setter: + @brief Configures the result type + Use this method to indicate what type of result you want to deliver. You can use the corresponding 'output' method then to deliver result shapes from one the callbacks (\on_edge, \begin_polygon, \end_polygon). Set this attribute when you create the visitor object. This attribute does not need to be set if no output is indended to be delivered. + """ + def _const_cast(self) -> PolygonNeighborhoodVisitor: + r""" + @brief Returns a non-const reference to self. + Basically, this method allows turning a const object reference to a non-const one. This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects. + + This method has been introduced in version 0.29.6. + """ + ... + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def _to_const_object(self) -> PolygonNeighborhoodVisitor: + r""" + @hide + """ + ... + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + @overload + def output(self, edge: EdgeWithProperties) -> None: + r""" + @brief Outputs an edge + Use this method from one of the callbacks (\on_edge, \begin_polygon, \end_polygon) to deliver a polygon. Note that you have to configure the result type as 'Edges' on construction of the visitor before being able to do so. + 'output' expects an object in original space - i.e. of the input edge. \to_original_trans gives you a suitable transformation to bring objects from 'edge is horizontal' space into the original space. + """ + ... + @overload + def output(self, edge_pair: EdgePairWithProperties) -> None: + r""" + @brief Outputs an edge pair + Use this method from one of the callbacks (\on_edge, \begin_polygon, \end_polygon) to deliver a polygon. Note that you have to configure the result type as 'EdgePairs' on construction of the visitor before being able to do so. + 'output' expects an object in original space - i.e. of the input edge. \to_original_trans gives you a suitable transformation to bring objects from 'edge is horizontal' space into the original space. + """ + ... + @overload + def output(self, polygon: PolygonWithProperties) -> None: + r""" + @brief Outputs a polygon + Use this method from one of the callbacks (\on_edge, \begin_polygon, \end_polygon) to deliver a polygon. Note that you have to configure the result type as 'Region' on construction of the visitor before being able to do so. + + 'output' expects an object in original space - i.e. of the input edge. \to_original_trans gives you a suitable transformation to bring objects from 'edge is horizontal' space into the original space. + """ + ... + ... + +class PolygonNeighborhoodVisitorBase: + r""" + @hide + """ + @classmethod + def new(cls) -> PolygonNeighborhoodVisitorBase: + r""" + @brief Creates a new object of this class + """ + ... + def __copy__(self) -> PolygonNeighborhoodVisitorBase: + r""" + @brief Creates a copy of self + """ + ... + def __deepcopy__(self) -> PolygonNeighborhoodVisitorBase: + r""" + @brief Creates a copy of self + """ + ... + def __init__(self) -> None: + r""" + @brief Creates a new object of this class + """ + ... + def _const_cast(self) -> PolygonNeighborhoodVisitorBase: + r""" + @brief Returns a non-const reference to self. + Basically, this method allows turning a const object reference to a non-const one. This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects. + + This method has been introduced in version 0.29.6. + """ + ... + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def _to_const_object(self) -> PolygonNeighborhoodVisitorBase: + r""" + @hide + """ + ... + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def assign(self, other: PolygonNeighborhoodVisitorBase) -> None: + r""" + @brief Assigns another object to self + """ + ... + def create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def dup(self) -> PolygonNeighborhoodVisitorBase: + r""" + @brief Creates a copy of self + """ + ... + def is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + ... + class PolygonOperator: r""" @brief A generic polygon operator @@ -50051,6 +55074,265 @@ class PolygonToEdgePairOperator: ... ... +class PolygonWithProperties(Polygon): + r""" + @brief A Polygon object with properties attached. + This class represents a combination of a Polygon object an user properties. User properties are stored in form of a properties ID. Convenience methods are provided to manipulate or retrieve user properties directly. + + This class has been introduced in version 0.30. + """ + prop_id: int + r""" + Getter: + @brief Gets the properties ID associated with the object + + Setter: + @brief Sets the properties ID of the object + """ + @overload + @classmethod + def new(cls, polygon: Polygon, properties: Dict[Any, Any]) -> PolygonWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + @classmethod + def new(cls, polygon: Polygon, properties_id: Optional[int] = ...) -> PolygonWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + @overload + def __init__(self, polygon: Polygon, properties: Dict[Any, Any]) -> None: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + def __init__(self, polygon: Polygon, properties_id: Optional[int] = ...) -> None: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + def __mul__(self, f: float) -> DPolygonWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __repr__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def __rmul__(self, f: float) -> DPolygonWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __str__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def _assign(self, other: Polygon) -> None: + r""" + @brief Assigns another object to self + """ + ... + def _const_cast(self) -> PolygonWithProperties: + r""" + @brief Returns a non-const reference to self. + Basically, this method allows turning a const object reference to a non-const one. This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects. + + This method has been introduced in version 0.29.6. + """ + ... + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def _dup(self) -> PolygonWithProperties: + r""" + @brief Creates a copy of self + """ + ... + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def _to_const_object(self) -> PolygonWithProperties: + r""" + @hide + """ + ... + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def delete_property(self, key: Any) -> None: + r""" + @brief Deletes the user property with the given key + This method is a convenience method that deletes the property with the given key. It does nothing if no property with that key exists. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. + """ + ... + def downcast(self) -> Polygon: + r""" + @brief Gets the corresponding object without the properties + """ + ... + @overload + def move(self, dx: Optional[int] = ..., dy: Optional[int] = ...) -> PolygonWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def move(self, v: Vector) -> PolygonWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param v The distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def moved(self, dx: Optional[int] = ..., dy: Optional[int] = ...) -> PolygonWithProperties: + r""" + @brief Returns the moved object (does not modify self) + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object. + """ + ... + @overload + def moved(self, v: Vector) -> PolygonWithProperties: + r""" + @brief Returns the moved object + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param v The distance to move the object. + + @return The moved object. + """ + ... + def properties(self) -> Any: + r""" + @brief Gets the user properties + This method is a convenience method that gets the properties of the shape as a single hash. + """ + ... + def property(self, key: Any) -> Any: + r""" + @brief Gets the user property with the given key + This method is a convenience method that gets the user property with the given key. If no property with that key does not exist, it will return nil. Using that method is more convenient than using the layout object and the properties ID to retrieve the property value. + """ + ... + def set_property(self, key: Any, value: Any) -> None: + r""" + @brief Sets the user property with the given key to the given value + This method is a convenience method that sets the user property with the given key to the given value. If no property with that key exists, it will create one. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. + """ + ... + def to_s(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + @overload + def transform(self, t: ICplxTrans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transform(self, t: Trans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transformed(self, t: CplxTrans) -> DPolygonWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: ICplxTrans) -> PolygonWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: Trans) -> PolygonWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + ... + class PreferredOrientation: r""" @brief This class represents the PreferredOrientation enum used within polygon decomposition @@ -52491,6 +57773,13 @@ class Region(ShapeCollection): ... @overload @classmethod + def new(cls, array: Sequence[PolygonWithProperties], dummy: Optional[bool] = ...) -> Region: + r""" + @hide + """ + ... + @overload + @classmethod def new(cls, array: Sequence[Polygon]) -> Region: r""" @brief Constructor from a polygon array @@ -52509,6 +57798,17 @@ class Region(ShapeCollection): ... @overload @classmethod + def new(cls, box: BoxWithProperties) -> Region: + r""" + @brief Box constructor + + This constructor creates a region from a box with properties. + + This variant has been introduced in version 0.30. + """ + ... + @overload + @classmethod def new(cls, path: Path) -> Region: r""" @brief Path constructor @@ -52518,6 +57818,17 @@ class Region(ShapeCollection): ... @overload @classmethod + def new(cls, path: PathWithProperties) -> Region: + r""" + @brief Path constructor + + This constructor creates a region from a path with properties. + + This variant has been introduced in version 0.30. + """ + ... + @overload + @classmethod def new(cls, polygon: Polygon) -> Region: r""" @brief Polygon constructor @@ -52527,6 +57838,17 @@ class Region(ShapeCollection): ... @overload @classmethod + def new(cls, polygon: PolygonWithProperties) -> Region: + r""" + @brief Polygon constructor + + This constructor creates a region from a polygon with properties. + + This variant has been introduced in version 0.30. + """ + ... + @overload + @classmethod def new(cls, polygon: SimplePolygon) -> Region: r""" @brief Simple polygon constructor @@ -52536,6 +57858,17 @@ class Region(ShapeCollection): ... @overload @classmethod + def new(cls, polygon: SimplePolygonWithProperties) -> Region: + r""" + @brief Simple polygon constructor + + This constructor creates a region from a simple polygon with properties. + + This variant has been introduced in version 0.30. + """ + ... + @overload + @classmethod def new(cls, shape_iterator: RecursiveShapeIterator) -> Region: r""" @brief Constructor from a hierarchical shape set @@ -52754,6 +58087,12 @@ class Region(ShapeCollection): """ ... @overload + def __init__(self, array: Sequence[PolygonWithProperties], dummy: Optional[bool] = ...) -> None: + r""" + @hide + """ + ... + @overload def __init__(self, array: Sequence[Polygon]) -> None: r""" @brief Constructor from a polygon array @@ -52770,6 +58109,16 @@ class Region(ShapeCollection): """ ... @overload + def __init__(self, box: BoxWithProperties) -> None: + r""" + @brief Box constructor + + This constructor creates a region from a box with properties. + + This variant has been introduced in version 0.30. + """ + ... + @overload def __init__(self, path: Path) -> None: r""" @brief Path constructor @@ -52778,6 +58127,16 @@ class Region(ShapeCollection): """ ... @overload + def __init__(self, path: PathWithProperties) -> None: + r""" + @brief Path constructor + + This constructor creates a region from a path with properties. + + This variant has been introduced in version 0.30. + """ + ... + @overload def __init__(self, polygon: Polygon) -> None: r""" @brief Polygon constructor @@ -52786,6 +58145,16 @@ class Region(ShapeCollection): """ ... @overload + def __init__(self, polygon: PolygonWithProperties) -> None: + r""" + @brief Polygon constructor + + This constructor creates a region from a polygon with properties. + + This variant has been introduced in version 0.30. + """ + ... + @overload def __init__(self, polygon: SimplePolygon) -> None: r""" @brief Simple polygon constructor @@ -52794,6 +58163,16 @@ class Region(ShapeCollection): """ ... @overload + def __init__(self, polygon: SimplePolygonWithProperties) -> None: + r""" + @brief Simple polygon constructor + + This constructor creates a region from a simple polygon with properties. + + This variant has been introduced in version 0.30. + """ + ... + @overload def __init__(self, shape_iterator: RecursiveShapeIterator) -> None: r""" @brief Constructor from a hierarchical shape set @@ -52955,11 +58334,13 @@ class Region(ShapeCollection): Note that in Ruby, the '-=' operator actually does not exist, but is emulated by '-' followed by an assignment. This is less efficient than the in-place operation, so it is recommended to use 'not_with' instead. """ ... - def __iter__(self) -> Iterator[Polygon]: + def __iter__(self) -> Iterator[PolygonWithProperties]: r""" @brief Returns each polygon of the region This returns the raw polygons (not merged polygons if merged semantics is enabled). + + Starting with version 0.30, the iterator delivers a RegionWithProperties object. """ ... def __ixor__(self, other: Region) -> Region: @@ -53388,18 +58769,21 @@ class Region(ShapeCollection): @brief Creates a copy of self """ ... - def each(self) -> Iterator[Polygon]: + def each(self) -> Iterator[PolygonWithProperties]: r""" @brief Returns each polygon of the region This returns the raw polygons (not merged polygons if merged semantics is enabled). + + Starting with version 0.30, the iterator delivers a RegionWithProperties object. """ ... - def each_merged(self) -> Iterator[Polygon]: + def each_merged(self) -> Iterator[PolygonWithProperties]: r""" @brief Returns each merged polygon of the region This returns the raw polygons if merged semantics is disabled or the merged ones if merged semantics is enabled. + Starting with version 0.30, the iterator delivers a RegionWithProperties object. """ ... def edges(self, mode: Optional[EdgeMode] = ...) -> Edges: @@ -53586,7 +58970,7 @@ class Region(ShapeCollection): This method has been introduced in version 0.27. """ ... - def filter(self, filter: PolygonFilter) -> None: + def filter(self, filter: PolygonFilterBase) -> None: r""" @brief Applies a generic filter in place (replacing the polygons from the Region) See \PolygonFilter for a description of this feature. @@ -53603,7 +58987,7 @@ class Region(ShapeCollection): This method has been introduced in version 0.28.4. """ ... - def filtered(self, filter: PolygonFilter) -> Region: + def filtered(self, filter: PolygonFilterBase) -> Region: r""" @brief Applies a generic filter and returns a filtered copy See \PolygonFilter for a description of this feature. @@ -53681,6 +59065,14 @@ class Region(ShapeCollection): """ ... @overload + def insert(self, array: Sequence[PolygonWithProperties]) -> None: + r""" + @brief Inserts all polygons with properties from the array into this region + + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, array: Sequence[Polygon]) -> None: r""" @brief Inserts all polygons from the array into this region @@ -53695,6 +59087,16 @@ class Region(ShapeCollection): """ ... @overload + def insert(self, box: BoxWithProperties) -> None: + r""" + @brief Inserts a box + + Inserts a box with properties into the region. + + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, path: Path) -> None: r""" @brief Inserts a path @@ -53703,6 +59105,16 @@ class Region(ShapeCollection): """ ... @overload + def insert(self, path: PathWithProperties) -> None: + r""" + @brief Inserts a path + + Inserts a path with properties into the region. + + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, polygon: Polygon) -> None: r""" @brief Inserts a polygon @@ -53711,6 +59123,16 @@ class Region(ShapeCollection): """ ... @overload + def insert(self, polygon: PolygonWithProperties) -> None: + r""" + @brief Inserts a polygon + + Inserts a polygon with properties into the region. + + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, polygon: SimplePolygon) -> None: r""" @brief Inserts a simple polygon @@ -53719,6 +59141,16 @@ class Region(ShapeCollection): """ ... @overload + def insert(self, polygon: SimplePolygonWithProperties) -> None: + r""" + @brief Inserts a simple polygon + + Inserts a simple polygon with properties into the region. + + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, region: Region) -> None: r""" @brief Inserts all polygons from the other region into this region @@ -55279,7 +60711,7 @@ class Region(ShapeCollection): This method has been introduced in version 0.27. """ ... - def split_filter(self, filter: PolygonFilter) -> List[Region]: + def split_filter(self, filter: PolygonFilterBase) -> List[Region]: r""" @brief Applies a generic filter and returns a copy with all matching shapes and one with the non-matching ones See \PolygonFilter for a description of this feature. @@ -57397,10 +62829,11 @@ class Shape: Starting with version 0.23, this method returns nil, if the shape does not represent an edge. Setter: - @brief Replaces the shape by the given edge (in micrometer units) - This method replaces the shape by the given edge, like \edge= with a \Edge argument does. This version translates the edge from micrometer units to database units internally. + @brief Replaces the shape by the given edge + This method replaces the shape by the given edge. This method can only be called for editable layouts. It does not change the user properties of the shape. + Calling this method will invalidate any iterators. It should not be called inside a loop iterating over shapes. - This method has been introduced in version 0.25. + This method has been introduced in version 0.22. """ edge_pair: Any r""" @@ -57449,10 +62882,11 @@ class Shape: Starting with version 0.23, this method returns nil, if the shape does not represent a path. Setter: - @brief Replaces the shape by the given path (in micrometer units) - This method replaces the shape by the given path, like \path= with a \Path argument does. This version translates the path from micrometer units to database units internally. + @brief Replaces the shape by the given path object + This method replaces the shape by the given path object. This method can only be called for editable layouts. It does not change the user properties of the shape. + Calling this method will invalidate any iterators. It should not be called inside a loop iterating over shapes. - This method has been introduced in version 0.25. + This method has been introduced in version 0.22. """ path_bgnext: int r""" @@ -58473,8 +63907,8 @@ class Shape: class ShapeCollection: r""" - @brief A base class for the shape collections (\Region, \Edges, \EdgePairs and \Texts) - + @brief A base class for the shape collections + This class is the common base class of \Region, \Edges, \EdgePairs and \Texts. This class has been introduced in version 0.27. """ @classmethod @@ -59459,6 +64893,15 @@ class Shapes: """ ... @overload + def insert(self, box: BoxWithProperties) -> Shape: + r""" + @brief Inserts a box with properties into the shapes list + @return A reference to the new shape (a \Shape object) + The property Id must be obtained from the \Layout object's property_id method which associates a property set with a property Id. + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, box: DBox) -> Shape: r""" @brief Inserts a micrometer-unit box into the shapes list @@ -59479,6 +64922,16 @@ class Shapes: """ ... @overload + def insert(self, box: DBoxWithProperties) -> Shape: + r""" + @brief Inserts a micrometer-unit box with properties into the shapes list + @return A reference to the new shape (a \Shape object) + This method behaves like the \insert version with a \BoxWithProperties argument, except that it will internally translate the box from micrometer to database units. + + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, edge: DEdge) -> Shape: r""" @brief Inserts a micrometer-unit edge into the shapes list @@ -59499,6 +64952,16 @@ class Shapes: """ ... @overload + def insert(self, edge: DEdgeWithProperties) -> Shape: + r""" + @brief Inserts a micrometer-unit edge with properties into the shapes list + @return A reference to the new shape (a \Shape object) + This method behaves like the \insert version with a \EdgeWithProperties argument, except that it will internally translate the edge from micrometer to database units. + + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, edge: Edge) -> Shape: r""" @brief Inserts an edge into the shapes list @@ -59516,6 +64979,15 @@ class Shapes: """ ... @overload + def insert(self, edge: EdgeWithProperties) -> Shape: + r""" + @brief Inserts an edge with properties into the shapes list + @return A reference to the new shape (a \Shape object) + The property Id must be obtained from the \Layout object's property_id method which associates a property set with a property Id. + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, edge_pair: DEdgePair) -> Shape: r""" @brief Inserts a micrometer-unit edge pair into the shapes list @@ -59536,6 +65008,16 @@ class Shapes: """ ... @overload + def insert(self, edge_pair: DEdgePairWithProperties) -> Shape: + r""" + @brief Inserts a micrometer-unit edge pair with properties into the shapes list + @return A reference to the new shape (a \Shape object) + This method behaves like the \insert version with a \EdgePairWithProperties argument, except that it will internally translate the edge pair from micrometer to database units. + + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, edge_pair: EdgePair) -> Shape: r""" @brief Inserts an edge pair into the shapes list @@ -59553,6 +65035,15 @@ class Shapes: """ ... @overload + def insert(self, edge_pair: EdgePairWithProperties) -> Shape: + r""" + @brief Inserts an edge pair with properties into the shapes list + @return A reference to the new shape (a \Shape object) + The property Id must be obtained from the \Layout object's property_id method which associates a property set with a property Id. + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, edge_pairs: EdgePairs) -> None: r""" @brief Inserts the edges from the edge pair collection into this shape container @@ -59671,6 +65162,16 @@ class Shapes: """ ... @overload + def insert(self, path: DPathWithProperties) -> Shape: + r""" + @brief Inserts a micrometer-unit path with properties into the shapes list + @return A reference to the new shape (a \Shape object) + This method behaves like the \insert version with a \PathWithProperties argument, except that it will internally translate the path from micrometer to database units. + + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, path: Path) -> Shape: r""" @brief Inserts a path into the shapes list @@ -59689,6 +65190,15 @@ class Shapes: """ ... @overload + def insert(self, path: PathWithProperties) -> Shape: + r""" + @brief Inserts a path with properties into the shapes list + @return A reference to the new shape (a \Shape object) + The property Id must be obtained from the \Layout object's property_id method which associates a property set with a property Id. + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, point: DPoint) -> Shape: r""" @brief Inserts a micrometer-unit point into the shapes list @@ -59727,6 +65237,16 @@ class Shapes: """ ... @overload + def insert(self, polygon: DPolygonWithProperties) -> Shape: + r""" + @brief Inserts a micrometer-unit polygon with properties into the shapes list + @return A reference to the new shape (a \Shape object) + This method behaves like the \insert version with a \PolygonWithProperties argument, except that it will internally translate the polygon from micrometer to database units. + + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, polygon: Polygon) -> Shape: r""" @brief Inserts a polygon into the shapes list @@ -59745,6 +65265,15 @@ class Shapes: """ ... @overload + def insert(self, polygon: PolygonWithProperties) -> Shape: + r""" + @brief Inserts a polygon with properties into the shapes list + @return A reference to the new shape (a \Shape object) + The property Id must be obtained from the \Layout object's property_id method which associates a property set with a property Id. + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, region: Region) -> None: r""" @brief Inserts the polygons from the region into this shape container @@ -59898,6 +65427,16 @@ class Shapes: """ ... @overload + def insert(self, simple_polygon: DSimplePolygonWithProperties) -> Shape: + r""" + @brief Inserts a micrometer-unit simple polygon with properties into the shapes list + @return A reference to the new shape (a \Shape object) + This method behaves like the \insert version with a \SimplePolygonWithProperties argument, except that it will internally translate the simple polygon from micrometer to database units. + + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, simple_polygon: SimplePolygon) -> Shape: r""" @brief Inserts a simple polygon into the shapes list @@ -59916,6 +65455,15 @@ class Shapes: """ ... @overload + def insert(self, simple_polygon: SimplePolygonWithProperties) -> Shape: + r""" + @brief Inserts a simple polygon with properties into the shapes list + @return A reference to the new shape (a \Shape object) + The property Id must be obtained from the \Layout object's property_id method which associates a property set with a property Id. + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, text: DText) -> Shape: r""" @brief Inserts a micrometer-unit text into the shapes list @@ -59936,6 +65484,16 @@ class Shapes: """ ... @overload + def insert(self, text: DTextWithProperties) -> Shape: + r""" + @brief Inserts a micrometer-unit text with properties into the shapes list + @return A reference to the new shape (a \Shape object) + This method behaves like the \insert version with a \TextWithProperties argument, except that it will internally translate the text from micrometer to database units. + + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, text: Text) -> Shape: r""" @brief Inserts a text into the shapes list @@ -59954,6 +65512,15 @@ class Shapes: """ ... @overload + def insert(self, text: TextWithProperties) -> Shape: + r""" + @brief Inserts a text with properties into the shapes list + @return A reference to the new shape (a \Shape object) + The property Id must be obtained from the \Layout object's property_id method which associates a property set with a property Id. + This variant has been introduced in version 0.30. + """ + ... + @overload def insert(self, texts: Texts) -> None: r""" @brief Inserts the texts from the text collection into this shape container @@ -61254,6 +66821,265 @@ class SimplePolygon: ... ... +class SimplePolygonWithProperties(SimplePolygon): + r""" + @brief A SimplePolygon object with properties attached. + This class represents a combination of a SimplePolygon object an user properties. User properties are stored in form of a properties ID. Convenience methods are provided to manipulate or retrieve user properties directly. + + This class has been introduced in version 0.30. + """ + prop_id: int + r""" + Getter: + @brief Gets the properties ID associated with the object + + Setter: + @brief Sets the properties ID of the object + """ + @overload + @classmethod + def new(cls, polygon: SimplePolygon, properties: Dict[Any, Any]) -> SimplePolygonWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + @classmethod + def new(cls, polygon: SimplePolygon, properties_id: Optional[int] = ...) -> SimplePolygonWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + @overload + def __init__(self, polygon: SimplePolygon, properties: Dict[Any, Any]) -> None: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + def __init__(self, polygon: SimplePolygon, properties_id: Optional[int] = ...) -> None: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + def __mul__(self, f: float) -> DSimplePolygonWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __repr__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def __rmul__(self, f: float) -> DSimplePolygonWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __str__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def _assign(self, other: SimplePolygon) -> None: + r""" + @brief Assigns another object to self + """ + ... + def _const_cast(self) -> SimplePolygonWithProperties: + r""" + @brief Returns a non-const reference to self. + Basically, this method allows turning a const object reference to a non-const one. This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects. + + This method has been introduced in version 0.29.6. + """ + ... + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def _dup(self) -> SimplePolygonWithProperties: + r""" + @brief Creates a copy of self + """ + ... + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def _to_const_object(self) -> SimplePolygonWithProperties: + r""" + @hide + """ + ... + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def delete_property(self, key: Any) -> None: + r""" + @brief Deletes the user property with the given key + This method is a convenience method that deletes the property with the given key. It does nothing if no property with that key exists. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. + """ + ... + def downcast(self) -> SimplePolygon: + r""" + @brief Gets the corresponding object without the properties + """ + ... + @overload + def move(self, dx: Optional[int] = ..., dy: Optional[int] = ...) -> SimplePolygonWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def move(self, v: Vector) -> SimplePolygonWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param v The distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def moved(self, dx: Optional[int] = ..., dy: Optional[int] = ...) -> SimplePolygonWithProperties: + r""" + @brief Returns the moved object (does not modify self) + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object. + """ + ... + @overload + def moved(self, v: Vector) -> SimplePolygonWithProperties: + r""" + @brief Returns the moved object + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param v The distance to move the object. + + @return The moved object. + """ + ... + def properties(self) -> Any: + r""" + @brief Gets the user properties + This method is a convenience method that gets the properties of the shape as a single hash. + """ + ... + def property(self, key: Any) -> Any: + r""" + @brief Gets the user property with the given key + This method is a convenience method that gets the user property with the given key. If no property with that key does not exist, it will return nil. Using that method is more convenient than using the layout object and the properties ID to retrieve the property value. + """ + ... + def set_property(self, key: Any, value: Any) -> None: + r""" + @brief Sets the user property with the given key to the given value + This method is a convenience method that sets the user property with the given key to the given value. If no property with that key exists, it will create one. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. + """ + ... + def to_s(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + @overload + def transform(self, t: ICplxTrans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transform(self, t: Trans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transformed(self, t: CplxTrans) -> DSimplePolygonWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: ICplxTrans) -> SimplePolygonWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: Trans) -> SimplePolygonWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + ... + class SubCircuit(NetlistObject): r""" @brief A subcircuit inside a circuit. @@ -61358,17 +67184,17 @@ class SubCircuit(NetlistObject): @overload def circuit(self) -> Circuit: r""" - @brief Gets the circuit the subcircuit lives in. + @brief Gets the circuit the subcircuit lives in (non-const version). This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \circuit_ref. + + This constness variant has been introduced in version 0.26.8 """ ... @overload def circuit(self) -> Circuit: r""" - @brief Gets the circuit the subcircuit lives in (non-const version). + @brief Gets the circuit the subcircuit lives in. This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \circuit_ref. - - This constness variant has been introduced in version 0.26.8 """ ... @overload @@ -62053,8 +67879,7 @@ class Text: Setter: @brief Sets the horizontal alignment - This property specifies how the text is aligned relative to the anchor point. - This property has been introduced in version 0.22 and extended to enums in 0.28. + This is the version accepting integer values. It's provided for backward compatibility. """ size: int r""" @@ -62090,7 +67915,8 @@ class Text: Setter: @brief Sets the vertical alignment - This is the version accepting integer values. It's provided for backward compatibility. + This property specifies how the text is aligned relative to the anchor point. + This property has been introduced in version 0.22 and extended to enums in 0.28. """ x: int r""" @@ -62505,7 +68331,7 @@ class Text: ... ... -class TextFilter: +class TextFilter(TextFilterBase): r""" @brief A generic text filter adaptor @@ -62541,33 +68367,6 @@ class TextFilter: This class has been introduced in version 0.29. """ - wants_variants: bool - r""" - Getter: - @brief Gets a value indicating whether the filter prefers cell variants - See \wants_variants= for details. - - Setter: - @brief Sets a value indicating whether the filter prefers cell variants - This flag must be set before using this filter for hierarchical applications (deep mode). It tells the filter implementation whether cell variants should be created (true, the default) or shape propagation will be applied (false). - - This decision needs to be made, if the filter indicates that it will deliver different results - for scaled or rotated versions of the shape (see \is_isotropic and the other hints). If a cell - is present with different qualities - as seen from the top cell - the respective instances - need to be differentiated. Cell variant formation is one way, shape propagation the other way. - Typically, cell variant formation is less expensive, but the hierarchy will be modified. - """ - @classmethod - def new(cls) -> TextFilter: - r""" - @brief Creates a new object of this class - """ - ... - def __init__(self) -> None: - r""" - @brief Creates a new object of this class - """ - ... def _const_cast(self) -> TextFilter: r""" @brief Returns a non-const reference to self. @@ -62621,6 +68420,143 @@ class TextFilter: @brief Marks the object as no longer owned by the script side. Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + ... + +class TextFilterBase: + r""" + @hide + """ + wants_variants: bool + r""" + Getter: + @brief Gets a value indicating whether the filter prefers cell variants + See \wants_variants= for details. + + Setter: + @brief Sets a value indicating whether the filter prefers cell variants + This flag must be set before using this filter for hierarchical applications (deep mode). It tells the filter implementation whether cell variants should be created (true, the default) or shape propagation will be applied (false). + + This decision needs to be made, if the filter indicates that it will deliver different results + for scaled or rotated versions of the shape (see \is_isotropic and the other hints). If a cell + is present with different qualities - as seen from the top cell - the respective instances + need to be differentiated. Cell variant formation is one way, shape propagation the other way. + Typically, cell variant formation is less expensive, but the hierarchy will be modified. + """ + @classmethod + def new(cls) -> TextFilterBase: + r""" + @brief Creates a new object of this class + """ + ... + @classmethod + def property_filter(cls, name: Any, value: Any, inverse: Optional[bool] = ...) -> TextFilterBase: + r""" + @brief Creates a single-valued property filter + @param name The name of the property to use. + @param value The value against which the property is checked (exact match). + @param inverse If true, inverts the selection - i.e. all texts without a property with the given name and value are selected. + + Apply this filter with \Texts#filtered. See \property_glob for an example. + + This feature has been introduced in version 0.30. + """ + ... + @classmethod + def property_filter_bounded(cls, name: Any, from_: Any, to: Any, inverse: Optional[bool] = ...) -> TextFilterBase: + r""" + @brief Creates a single-valued property filter + @param name The name of the property to use. + @param from The lower value against which the property is checked or 'nil' if no lower bound shall be used. + @param to The upper value against which the property is checked or 'nil' if no upper bound shall be used. + @param inverse If true, inverts the selection - i.e. all texts without a property with the given name and value range are selected. + + This version does a bounded match. The value of the propery needs to be larger or equal to 'from' and less than 'to'. + Apply this filter with \Texts#filtered. See \property_glob for an example. + + This feature has been introduced in version 0.30. + """ + ... + @classmethod + def property_glob(cls, name: Any, pattern: str, inverse: Optional[bool] = ..., case_sensitive: Optional[bool] = ...) -> TextFilterBase: + r""" + @brief Creates a single-valued property filter + @param name The name of the property to use. + @param value The glob pattern to match the property value against. + @param inverse If true, inverts the selection - i.e. all texts without a matching property are selected. + @param case_sensitive If true, the match is case sensitive (the default), if false, the match is not case sensitive. + + Apply this filter with \Texts#filtered: + + @code + # texts is a Texts object + # filtered_texts contains all texts where the 'net' property starts with 'C': + filtered_texts = texts.filtered(RBA::TextFilterBase::property_glob('net', 'C*')) + @/code + + This feature has been introduced in version 0.30. + """ + ... + def __init__(self) -> None: + r""" + @brief Creates a new object of this class + """ + ... + def _const_cast(self) -> TextFilterBase: + r""" + @brief Returns a non-const reference to self. + Basically, this method allows turning a const object reference to a non-const one. This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects. + + This method has been introduced in version 0.29.6. + """ + ... + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def _to_const_object(self) -> TextFilterBase: + r""" + @hide + """ + ... + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + Usually it's not required to call this method. It has been introduced in version 0.24. """ ... @@ -63318,6 +69254,265 @@ class TextToPolygonOperator: ... ... +class TextWithProperties(Text): + r""" + @brief A Text object with properties attached. + This class represents a combination of a Text object an user properties. User properties are stored in form of a properties ID. Convenience methods are provided to manipulate or retrieve user properties directly. + + This class has been introduced in version 0.30. + """ + prop_id: int + r""" + Getter: + @brief Gets the properties ID associated with the object + + Setter: + @brief Sets the properties ID of the object + """ + @overload + @classmethod + def new(cls, text: Text, properties: Dict[Any, Any]) -> TextWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + @classmethod + def new(cls, text: Text, properties_id: Optional[int] = ...) -> TextWithProperties: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + @overload + def __init__(self, text: Text, properties: Dict[Any, Any]) -> None: + r""" + @brief Creates a new object from a property-less object and a properties hash. + """ + ... + @overload + def __init__(self, text: Text, properties_id: Optional[int] = ...) -> None: + r""" + @brief Creates a new object from a property-less object and a properties ID. + """ + ... + def __mul__(self, f: float) -> DTextWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __repr__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def __rmul__(self, f: float) -> DTextWithProperties: + r""" + @brief Scales the object by some factor + + Returns the scaled object. All coordinates are multiplied with the given factor and, if necessary, rounded. + """ + ... + def __str__(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + def _assign(self, other: Text) -> None: + r""" + @brief Assigns another object to self + """ + ... + def _const_cast(self) -> TextWithProperties: + r""" + @brief Returns a non-const reference to self. + Basically, this method allows turning a const object reference to a non-const one. This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects. + + This method has been introduced in version 0.29.6. + """ + ... + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def _dup(self) -> TextWithProperties: + r""" + @brief Creates a copy of self + """ + ... + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def _to_const_object(self) -> TextWithProperties: + r""" + @hide + """ + ... + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def delete_property(self, key: Any) -> None: + r""" + @brief Deletes the user property with the given key + This method is a convenience method that deletes the property with the given key. It does nothing if no property with that key exists. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. + """ + ... + def downcast(self) -> Text: + r""" + @brief Gets the corresponding object without the properties + """ + ... + @overload + def move(self, dx: Optional[int] = ..., dy: Optional[int] = ...) -> TextWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def move(self, v: Vector) -> TextWithProperties: + r""" + @brief Moves the object. + + Moves the object by the given offset and returns the + moved object. The object is overwritten. + + @param v The distance to move the object. + + @return The moved object (self). + """ + ... + @overload + def moved(self, dx: Optional[int] = ..., dy: Optional[int] = ...) -> TextWithProperties: + r""" + @brief Returns the moved object (does not modify self) + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param dx The x distance to move the object. + @param dy The y distance to move the object. + + @return The moved object. + """ + ... + @overload + def moved(self, v: Vector) -> TextWithProperties: + r""" + @brief Returns the moved object + + Moves the object by the given offset and returns the + moved object. The object is not modified. + + @param v The distance to move the object. + + @return The moved object. + """ + ... + def properties(self) -> Any: + r""" + @brief Gets the user properties + This method is a convenience method that gets the properties of the shape as a single hash. + """ + ... + def property(self, key: Any) -> Any: + r""" + @brief Gets the user property with the given key + This method is a convenience method that gets the user property with the given key. If no property with that key does not exist, it will return nil. Using that method is more convenient than using the layout object and the properties ID to retrieve the property value. + """ + ... + def set_property(self, key: Any, value: Any) -> None: + r""" + @brief Sets the user property with the given key to the given value + This method is a convenience method that sets the user property with the given key to the given value. If no property with that key exists, it will create one. Using that method is more convenient than creating a new property set with a new ID and assigning that properties ID. + This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. + """ + ... + def to_s(self) -> str: + r""" + @brief Returns a string representing the polygon + """ + ... + @overload + def transform(self, t: ICplxTrans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transform(self, t: Trans) -> None: + r""" + @brief Transforms the object (in-place version) + """ + ... + @overload + def transformed(self, t: CplxTrans) -> DTextWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: ICplxTrans) -> TextWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + @overload + def transformed(self, t: Trans) -> TextWithProperties: + r""" + @brief Returns the transformed object + + Returns a copy of the object, transformed by the given transformation. The result is equivalent to 'tr * self'. + """ + ... + ... + class Texts(ShapeCollection): r""" @brief Texts (a collection of texts) @@ -63343,9 +69538,16 @@ class Texts(ShapeCollection): ... @overload @classmethod + def new(cls, array: Sequence[TextWithProperties], dummy: Optional[bool] = ...) -> Texts: + r""" + @hide + """ + ... + @overload + @classmethod def new(cls, array: Sequence[Text]) -> Texts: r""" - @brief Constructor from an text array + @brief Constructor from a text array This constructor creates an text collection from an array of \Text objects. """ @@ -63440,11 +69642,22 @@ class Texts(ShapeCollection): @classmethod def new(cls, text: Text) -> Texts: r""" - @brief Constructor from a single edge pair object + @brief Constructor from a single text object This constructor creates an text collection with a single text. """ ... + @overload + @classmethod + def new(cls, text: TextWithProperties) -> Texts: + r""" + @brief Constructor from a single text object + + This constructor creates an text collection with a single text with properties. + + This variant has been introduced in version 0.30. + """ + ... def __add__(self, other: Texts) -> Texts: r""" @brief Returns the combined text collection of self and the other one @@ -63504,9 +69717,15 @@ class Texts(ShapeCollection): """ ... @overload + def __init__(self, array: Sequence[TextWithProperties], dummy: Optional[bool] = ...) -> None: + r""" + @hide + """ + ... + @overload def __init__(self, array: Sequence[Text]) -> None: r""" - @brief Constructor from an text array + @brief Constructor from a text array This constructor creates an text collection from an array of \Text objects. """ @@ -63595,14 +69814,26 @@ class Texts(ShapeCollection): @overload def __init__(self, text: Text) -> None: r""" - @brief Constructor from a single edge pair object + @brief Constructor from a single text object This constructor creates an text collection with a single text. """ ... - def __iter__(self) -> Iterator[Text]: + @overload + def __init__(self, text: TextWithProperties) -> None: + r""" + @brief Constructor from a single text object + + This constructor creates an text collection with a single text with properties. + + This variant has been introduced in version 0.30. + """ + ... + def __iter__(self) -> Iterator[TextWithProperties]: r""" @brief Returns each text of the text collection + + Starting with version 0.30, the iterator delivers TextWithProperties objects. """ ... def __len__(self) -> int: @@ -63730,9 +69961,11 @@ class Texts(ShapeCollection): @brief Creates a copy of self """ ... - def each(self) -> Iterator[Text]: + def each(self) -> Iterator[TextWithProperties]: r""" @brief Returns each text of the text collection + + Starting with version 0.30, the iterator delivers TextWithProperties objects. """ ... def edges(self) -> Edges: @@ -63773,7 +70006,7 @@ class Texts(ShapeCollection): This method acts like the other version of \extents, but allows giving different enlargements for x and y direction. """ ... - def filter(self, filter: TextFilter) -> None: + def filter(self, filter: TextFilterBase) -> None: r""" @brief Applies a generic filter in place (replacing the texts from the Texts collection) See \TextFilter for a description of this feature. @@ -63790,7 +70023,7 @@ class Texts(ShapeCollection): This method has been introduced in version 0.28.4. """ ... - def filtered(self, filtered: TextFilter) -> Texts: + def filtered(self, filtered: TextFilterBase) -> Texts: r""" @brief Applies a generic filter and returns a filtered copy See \TextFilter for a description of this feature. @@ -63826,6 +70059,14 @@ class Texts(ShapeCollection): """ ... @overload + def insert(self, text: TextWithProperties) -> None: + r""" + @brief Inserts a text into the collection + + This variant accepting a text with properties has been introduced in version 0.30. + """ + ... + @overload def insert(self, texts: Texts) -> None: r""" @brief Inserts all texts from the other text collection into this collection @@ -65308,6 +71549,19 @@ class Trans: """ ... @overload + def __mul__(self, box: BoxWithProperties) -> BoxWithProperties: + r""" + @brief Transforms a box with properties + + 't*box' or 't.trans(box)' is equivalent to box.transformed(t). + + @param box The box to transform + @return The transformed box + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, d: int) -> int: r""" @brief Transforms a single distance @@ -65336,6 +71590,45 @@ class Trans: """ ... @overload + def __mul__(self, edge: EdgeWithProperties) -> EdgeWithProperties: + r""" + @brief Transforms an edge with properties + + 't*edge' or 't.trans(edge)' is equivalent to edge.transformed(t). + + @param edge The edge to transform + @return The transformed edge + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __mul__(self, edge_pair: EdgePair) -> EdgePair: + r""" + @brief Transforms an edge pair + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __mul__(self, edge_pair: EdgePairWithProperties) -> EdgePairWithProperties: + r""" + @brief Transforms an edge pair with properties + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, p: Point) -> Point: r""" @brief Transforms a point @@ -65363,6 +71656,19 @@ class Trans: """ ... @overload + def __mul__(self, path: PathWithProperties) -> PathWithProperties: + r""" + @brief Transforms a path with properties + + 't*path' or 't.trans(path)' is equivalent to path.transformed(t). + + @param path The path to transform + @return The transformed path + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, polygon: Polygon) -> Polygon: r""" @brief Transforms a polygon @@ -65376,6 +71682,45 @@ class Trans: """ ... @overload + def __mul__(self, polygon: PolygonWithProperties) -> PolygonWithProperties: + r""" + @brief Transforms a polygon with properties + + 't*polygon' or 't.trans(polygon)' is equivalent to polygon.transformed(t). + + @param polygon The polygon to transform + @return The transformed polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __mul__(self, simple_polygon: SimplePolygon) -> SimplePolygon: + r""" + @brief Transforms a simple polygon + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __mul__(self, simple_polygon: SimplePolygonWithProperties) -> SimplePolygonWithProperties: + r""" + @brief Transforms a simple polygon with properties + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, t: Trans) -> Trans: r""" @brief Returns the concatenated transformation @@ -65400,6 +71745,19 @@ class Trans: """ ... @overload + def __mul__(self, text: TextWithProperties) -> TextWithProperties: + r""" + @brief Transforms a text with properties + + 't*text' or 't.trans(text)' is equivalent to text.transformed(t). + + @param text The text to transform + @return The transformed text + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, v: Vector) -> Vector: r""" @brief Transforms a vector @@ -65440,6 +71798,19 @@ class Trans: """ ... @overload + def __rmul__(self, box: BoxWithProperties) -> BoxWithProperties: + r""" + @brief Transforms a box with properties + + 't*box' or 't.trans(box)' is equivalent to box.transformed(t). + + @param box The box to transform + @return The transformed box + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, d: int) -> int: r""" @brief Transforms a single distance @@ -65468,6 +71839,45 @@ class Trans: """ ... @overload + def __rmul__(self, edge: EdgeWithProperties) -> EdgeWithProperties: + r""" + @brief Transforms an edge with properties + + 't*edge' or 't.trans(edge)' is equivalent to edge.transformed(t). + + @param edge The edge to transform + @return The transformed edge + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __rmul__(self, edge_pair: EdgePair) -> EdgePair: + r""" + @brief Transforms an edge pair + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __rmul__(self, edge_pair: EdgePairWithProperties) -> EdgePairWithProperties: + r""" + @brief Transforms an edge pair with properties + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, p: Point) -> Point: r""" @brief Transforms a point @@ -65495,6 +71905,19 @@ class Trans: """ ... @overload + def __rmul__(self, path: PathWithProperties) -> PathWithProperties: + r""" + @brief Transforms a path with properties + + 't*path' or 't.trans(path)' is equivalent to path.transformed(t). + + @param path The path to transform + @return The transformed path + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, polygon: Polygon) -> Polygon: r""" @brief Transforms a polygon @@ -65508,6 +71931,45 @@ class Trans: """ ... @overload + def __rmul__(self, polygon: PolygonWithProperties) -> PolygonWithProperties: + r""" + @brief Transforms a polygon with properties + + 't*polygon' or 't.trans(polygon)' is equivalent to polygon.transformed(t). + + @param polygon The polygon to transform + @return The transformed polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __rmul__(self, simple_polygon: SimplePolygon) -> SimplePolygon: + r""" + @brief Transforms a simple polygon + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __rmul__(self, simple_polygon: SimplePolygonWithProperties) -> SimplePolygonWithProperties: + r""" + @brief Transforms a simple polygon with properties + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, text: Text) -> Text: r""" @brief Transforms a text @@ -65521,6 +71983,19 @@ class Trans: """ ... @overload + def __rmul__(self, text: TextWithProperties) -> TextWithProperties: + r""" + @brief Transforms a text with properties + + 't*text' or 't.trans(text)' is equivalent to text.transformed(t). + + @param text The text to transform + @return The transformed text + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, v: Vector) -> Vector: r""" @brief Transforms a vector @@ -65713,6 +72188,19 @@ class Trans: """ ... @overload + def trans(self, box: BoxWithProperties) -> BoxWithProperties: + r""" + @brief Transforms a box with properties + + 't*box' or 't.trans(box)' is equivalent to box.transformed(t). + + @param box The box to transform + @return The transformed box + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, edge: Edge) -> Edge: r""" @brief Transforms an edge @@ -65726,6 +72214,45 @@ class Trans: """ ... @overload + def trans(self, edge: EdgeWithProperties) -> EdgeWithProperties: + r""" + @brief Transforms an edge with properties + + 't*edge' or 't.trans(edge)' is equivalent to edge.transformed(t). + + @param edge The edge to transform + @return The transformed edge + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def trans(self, edge_pair: EdgePair) -> EdgePair: + r""" + @brief Transforms an edge pair + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def trans(self, edge_pair: EdgePairWithProperties) -> EdgePairWithProperties: + r""" + @brief Transforms an edge pair with properties + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, p: Point) -> Point: r""" @brief Transforms a point @@ -65753,6 +72280,19 @@ class Trans: """ ... @overload + def trans(self, path: PathWithProperties) -> PathWithProperties: + r""" + @brief Transforms a path with properties + + 't*path' or 't.trans(path)' is equivalent to path.transformed(t). + + @param path The path to transform + @return The transformed path + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, polygon: Polygon) -> Polygon: r""" @brief Transforms a polygon @@ -65766,6 +72306,45 @@ class Trans: """ ... @overload + def trans(self, polygon: PolygonWithProperties) -> PolygonWithProperties: + r""" + @brief Transforms a polygon with properties + + 't*polygon' or 't.trans(polygon)' is equivalent to polygon.transformed(t). + + @param polygon The polygon to transform + @return The transformed polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def trans(self, simple_polygon: SimplePolygon) -> SimplePolygon: + r""" + @brief Transforms a simple polygon + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def trans(self, simple_polygon: SimplePolygonWithProperties) -> SimplePolygonWithProperties: + r""" + @brief Transforms a simple polygon with properties + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, text: Text) -> Text: r""" @brief Transforms a text @@ -65779,6 +72358,19 @@ class Trans: """ ... @overload + def trans(self, text: TextWithProperties) -> TextWithProperties: + r""" + @brief Transforms a text with properties + + 't*text' or 't.trans(text)' is equivalent to text.transformed(t). + + @param text The text to transform + @return The transformed text + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, v: Vector) -> Vector: r""" @brief Transforms a vector @@ -66840,6 +73432,19 @@ class VCplxTrans: """ ... @overload + def __mul__(self, box: DBoxWithProperties) -> BoxWithProperties: + r""" + @brief Transforms a box with properties + + 't*box' or 't.trans(box)' is equivalent to box.transformed(t). + + @param box The box to transform + @return The transformed box + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, d: float) -> int: r""" @brief Transforms a single distance @@ -66868,6 +73473,32 @@ class VCplxTrans: """ ... @overload + def __mul__(self, edge: DEdgeWithProperties) -> EdgeWithProperties: + r""" + @brief Transforms an edge with properties + + 't*edge' or 't.trans(edge)' is equivalent to edge.transformed(t). + + @param edge The edge to transform + @return The transformed edge + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __mul__(self, edge_pair: DEdgePairWithProperties) -> EdgePairWithProperties: + r""" + @brief Transforms an edge pair with properties + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, p: DPoint) -> Point: r""" @brief Transforms a point @@ -66909,6 +73540,19 @@ class VCplxTrans: """ ... @overload + def __mul__(self, path: DPathWithProperties) -> PathWithProperties: + r""" + @brief Transforms a path with properties + + 't*path' or 't.trans(path)' is equivalent to path.transformed(t). + + @param path The path to transform + @return The transformed path + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, polygon: DPolygon) -> Polygon: r""" @brief Transforms a polygon @@ -66922,6 +73566,32 @@ class VCplxTrans: """ ... @overload + def __mul__(self, polygon: DPolygonWithProperties) -> PolygonWithProperties: + r""" + @brief Transforms a polygon with properties + + 't*polygon' or 't.trans(polygon)' is equivalent to polygon.transformed(t). + + @param polygon The polygon to transform + @return The transformed polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __mul__(self, simple_polygon: DSimplePolygonWithProperties) -> SimplePolygonWithProperties: + r""" + @brief Transforms a simple polygon with properties + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __mul__(self, t: CplxTrans) -> ICplxTrans: r""" @brief Multiplication (concatenation) of transformations @@ -66967,6 +73637,19 @@ class VCplxTrans: This convenience method has been introduced in version 0.25. """ ... + @overload + def __mul__(self, text: DTextWithProperties) -> TextWithProperties: + r""" + @brief Transforms a text with properties + + 't*text' or 't.trans(text)' is equivalent to text.transformed(t). + + @param text The text to transform + @return The transformed text + + This convenience method has been introduced in version 0.30. + """ + ... def __ne__(self, other: object) -> bool: r""" @brief Tests for inequality @@ -66995,6 +73678,19 @@ class VCplxTrans: """ ... @overload + def __rmul__(self, box: DBoxWithProperties) -> BoxWithProperties: + r""" + @brief Transforms a box with properties + + 't*box' or 't.trans(box)' is equivalent to box.transformed(t). + + @param box The box to transform + @return The transformed box + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, d: float) -> int: r""" @brief Transforms a single distance @@ -67023,6 +73719,32 @@ class VCplxTrans: """ ... @overload + def __rmul__(self, edge: DEdgeWithProperties) -> EdgeWithProperties: + r""" + @brief Transforms an edge with properties + + 't*edge' or 't.trans(edge)' is equivalent to edge.transformed(t). + + @param edge The edge to transform + @return The transformed edge + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __rmul__(self, edge_pair: DEdgePairWithProperties) -> EdgePairWithProperties: + r""" + @brief Transforms an edge pair with properties + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, p: DPoint) -> Point: r""" @brief Transforms a point @@ -67064,6 +73786,19 @@ class VCplxTrans: """ ... @overload + def __rmul__(self, path: DPathWithProperties) -> PathWithProperties: + r""" + @brief Transforms a path with properties + + 't*path' or 't.trans(path)' is equivalent to path.transformed(t). + + @param path The path to transform + @return The transformed path + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, polygon: DPolygon) -> Polygon: r""" @brief Transforms a polygon @@ -67077,6 +73812,32 @@ class VCplxTrans: """ ... @overload + def __rmul__(self, polygon: DPolygonWithProperties) -> PolygonWithProperties: + r""" + @brief Transforms a polygon with properties + + 't*polygon' or 't.trans(polygon)' is equivalent to polygon.transformed(t). + + @param polygon The polygon to transform + @return The transformed polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def __rmul__(self, simple_polygon: DSimplePolygonWithProperties) -> SimplePolygonWithProperties: + r""" + @brief Transforms a simple polygon with properties + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def __rmul__(self, text: DText) -> Text: r""" @brief Transforms a text @@ -67089,6 +73850,19 @@ class VCplxTrans: This convenience method has been introduced in version 0.25. """ ... + @overload + def __rmul__(self, text: DTextWithProperties) -> TextWithProperties: + r""" + @brief Transforms a text with properties + + 't*text' or 't.trans(text)' is equivalent to text.transformed(t). + + @param text The text to transform + @return The transformed text + + This convenience method has been introduced in version 0.30. + """ + ... def __str__(self, lazy: Optional[bool] = ..., dbu: Optional[float] = ...) -> str: r""" @brief String conversion @@ -67354,6 +74128,19 @@ class VCplxTrans: """ ... @overload + def trans(self, box: DBoxWithProperties) -> BoxWithProperties: + r""" + @brief Transforms a box with properties + + 't*box' or 't.trans(box)' is equivalent to box.transformed(t). + + @param box The box to transform + @return The transformed box + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, edge: DEdge) -> Edge: r""" @brief Transforms an edge @@ -67367,6 +74154,32 @@ class VCplxTrans: """ ... @overload + def trans(self, edge: DEdgeWithProperties) -> EdgeWithProperties: + r""" + @brief Transforms an edge with properties + + 't*edge' or 't.trans(edge)' is equivalent to edge.transformed(t). + + @param edge The edge to transform + @return The transformed edge + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def trans(self, edge_pair: DEdgePairWithProperties) -> EdgePairWithProperties: + r""" + @brief Transforms an edge pair with properties + + 't*edge_pair' or 't.trans(edge_pair)' is equivalent to edge_pair.transformed(t). + + @param edge_pair The edge pair to transform + @return The transformed edge pair + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, p: DPoint) -> Point: r""" @brief Transforms a point @@ -67408,6 +74221,19 @@ class VCplxTrans: """ ... @overload + def trans(self, path: DPathWithProperties) -> PathWithProperties: + r""" + @brief Transforms a path with properties + + 't*path' or 't.trans(path)' is equivalent to path.transformed(t). + + @param path The path to transform + @return The transformed path + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, polygon: DPolygon) -> Polygon: r""" @brief Transforms a polygon @@ -67421,6 +74247,32 @@ class VCplxTrans: """ ... @overload + def trans(self, polygon: DPolygonWithProperties) -> PolygonWithProperties: + r""" + @brief Transforms a polygon with properties + + 't*polygon' or 't.trans(polygon)' is equivalent to polygon.transformed(t). + + @param polygon The polygon to transform + @return The transformed polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload + def trans(self, simple_polygon: DSimplePolygonWithProperties) -> SimplePolygonWithProperties: + r""" + @brief Transforms a simple polygon with properties + + 't*polygon' or 't.trans(simple_polygon)' is equivalent to simple_polygon.transformed(t). + + @param simple_polygon The simple polygon to transform + @return The transformed simple polygon + + This convenience method has been introduced in version 0.30. + """ + ... + @overload def trans(self, text: DText) -> Text: r""" @brief Transforms a text @@ -67433,6 +74285,19 @@ class VCplxTrans: This convenience method has been introduced in version 0.25. """ ... + @overload + def trans(self, text: DTextWithProperties) -> TextWithProperties: + r""" + @brief Transforms a text with properties + + 't*text' or 't.trans(text)' is equivalent to text.transformed(t). + + @param text The text to transform + @return The transformed text + + This convenience method has been introduced in version 0.30. + """ + ... ... class Vector: diff --git a/src/pymod/distutils_src/klayout/rdbcore.pyi b/src/pymod/distutils_src/klayout/rdbcore.pyi index 423b6ba4d..237cf2d15 100644 --- a/src/pymod/distutils_src/klayout/rdbcore.pyi +++ b/src/pymod/distutils_src/klayout/rdbcore.pyi @@ -1627,6 +1627,20 @@ class ReportDatabase: """ ... @overload + def create_items(self, cell_id: int, category_id: int, trans: db.CplxTrans, array: Sequence[db.EdgePairWithProperties], with_properties: Optional[bool] = ...) -> None: + r""" + @brief Creates new edge pair items for the given cell/category combination + This version takes \EdgePairWithProperties objects. If \with_properties is true (the default), the + properties are added to the item as tagged values. + @param cell_id The ID of the cell to which the item is associated + @param category_id The ID of the category to which the item is associated + @param trans The transformation to apply + @param polygons The list of edge pairs (with properties) for which the items are created + @param with_properties If true, the properties are transferred into the item as well + This variant has been introduced in version 0.30. + """ + ... + @overload def create_items(self, cell_id: int, category_id: int, trans: db.CplxTrans, array: Sequence[db.EdgePair]) -> None: r""" @brief Creates new edge pair items for the given cell/category combination @@ -1642,6 +1656,20 @@ class ReportDatabase: """ ... @overload + def create_items(self, cell_id: int, category_id: int, trans: db.CplxTrans, array: Sequence[db.EdgeWithProperties], with_properties: Optional[bool] = ...) -> None: + r""" + @brief Creates new edge items for the given cell/category combination + This version takes \EdgeWithProperties objects. If \with_properties is true (the default), the + properties are added to the item as tagged values. + @param cell_id The ID of the cell to which the item is associated + @param category_id The ID of the category to which the item is associated + @param trans The transformation to apply + @param polygons The list of edges (with properties) for which the items are created + @param with_properties If true, the properties are transferred into the item as well + This variant has been introduced in version 0.30. + """ + ... + @overload def create_items(self, cell_id: int, category_id: int, trans: db.CplxTrans, array: Sequence[db.Edge]) -> None: r""" @brief Creates new edge items for the given cell/category combination @@ -1657,6 +1685,20 @@ class ReportDatabase: """ ... @overload + def create_items(self, cell_id: int, category_id: int, trans: db.CplxTrans, array: Sequence[db.PolygonWithProperties], with_properties: Optional[bool] = ...) -> None: + r""" + @brief Creates new polygon items for the given cell/category combination + This version takes \PolygonWithProperties objects. If \with_properties is true (the default), the + properties are added to the item as tagged values. + @param cell_id The ID of the cell to which the item is associated + @param category_id The ID of the category to which the item is associated + @param trans The transformation to apply + @param polygons The list of polygons (with properties) for which the items are created + @param with_properties If true, the properties are transferred into the item as well + This variant has been introduced in version 0.30. + """ + ... + @overload def create_items(self, cell_id: int, category_id: int, trans: db.CplxTrans, array: Sequence[db.Polygon]) -> None: r""" @brief Creates new polygon items for the given cell/category combination From 2668b42d2991e7a414a984fb5d7d3a38223d43c9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Mar 2025 20:35:15 +0100 Subject: [PATCH 085/392] Separating tl::hfunc from std namespace. Needed to support int128 in tl::Variant for 64bit coordinate builds. --- src/db/db/dbAsIfFlatRegion.cc | 4 +- src/db/db/dbHash.h | 352 ++++++++++++++-------------- src/db/db/dbPropertiesRepository.cc | 2 +- src/db/db/dbShape.cc | 6 +- src/db/db/gsiDeclDbBox.cc | 2 +- src/db/db/gsiDeclDbCell.cc | 2 +- src/db/db/gsiDeclDbEdge.cc | 2 +- src/db/db/gsiDeclDbEdgePair.cc | 2 +- src/db/db/gsiDeclDbLayout.cc | 2 +- src/db/db/gsiDeclDbPath.cc | 2 +- src/db/db/gsiDeclDbPoint.cc | 2 +- src/db/db/gsiDeclDbPolygon.cc | 4 +- src/db/db/gsiDeclDbText.cc | 2 +- src/db/db/gsiDeclDbTrans.cc | 4 +- src/db/db/gsiDeclDbVector.cc | 2 +- src/tl/tl/tlHash.h | 209 +++++++++-------- src/tl/tl/tlVariant.cc | 50 ++-- 17 files changed, 338 insertions(+), 311 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 339af0235..1ea27bb98 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -53,7 +53,7 @@ struct ResultCountingInserter { typedef db::Polygon value_type; - ResultCountingInserter (std::unordered_map > &result) + ResultCountingInserter (std::unordered_map > &result) : mp_result (&result) { // .. nothing yet .. @@ -70,7 +70,7 @@ struct ResultCountingInserter } private: - std::unordered_map > *mp_result; + std::unordered_map > *mp_result; }; } diff --git a/src/db/db/dbHash.h b/src/db/db/dbHash.h index 0770b03b4..7daa982c5 100644 --- a/src/db/db/dbHash.h +++ b/src/db/db/dbHash.h @@ -55,7 +55,7 @@ namespace db * for use with std::unordered_map and std::unordered_set */ -namespace std +namespace tl { inline size_t hfunc_coord (db::DCoord d) { @@ -89,15 +89,6 @@ namespace std return hfunc_coord (o.x (), hfunc_coord (o.y ())); } - template - struct hash > - { - size_t operator() (const db::point &o) const - { - return hfunc (o); - } - }; - /** * @brief Hash value for a vector */ @@ -114,15 +105,6 @@ namespace std return hfunc_coord (o.x (), hfunc_coord (o.y ())); } - template - struct hash > - { - size_t operator() (const db::vector &o) const - { - return hfunc (o); - } - }; - /** * @brief Hash value for a box */ @@ -139,15 +121,6 @@ namespace std return hfunc (o.p1 (), hfunc (o.p2 ())); } - template - struct hash > - { - size_t operator() (const db::box &o) const - { - return hfunc (o); - } - }; - /** * @brief Hash value for an edge */ @@ -164,15 +137,6 @@ namespace std return hfunc (o.p1 (), hfunc (o.p2 ())); } - template - struct hash > - { - size_t operator() (const db::edge &o) const - { - return hfunc (o); - } - }; - /** * @brief Hash value for an edge pair */ @@ -189,15 +153,6 @@ namespace std return hfunc (o.lesser (), hfunc (o.greater (), hfunc (int (o.is_symmetric ())))); } - template - struct hash > - { - size_t operator() (const db::edge_pair &o) const - { - return hfunc (o); - } - }; - /** * @brief Hash value for a text object */ @@ -220,15 +175,6 @@ namespace std return hfunc (o, size_t (0)); } - template - struct hash > - { - size_t operator() (const db::text &o) const - { - return hfunc (o); - } - }; - /** * @brief Hash value for a path */ @@ -259,15 +205,6 @@ namespace std return hfunc (o, size_t (0)); } - template - struct hash > - { - size_t operator() (const db::path &o) const - { - return hfunc (o); - } - }; - /** * @brief Hash value for a polygon contour */ @@ -294,15 +231,6 @@ namespace std return hfunc (o, size_t (0)); } - template - struct hash > - { - size_t operator() (const db::path &o) const - { - return hfunc (o); - } - }; - /** * @brief Hash value for a polygon */ @@ -330,15 +258,6 @@ namespace std return hfunc (o, size_t (0)); } - template - struct hash > - { - size_t operator() (const db::polygon &o) const - { - return hfunc (o); - } - }; - /** * @brief Hash value for a simple polygon */ @@ -355,15 +274,6 @@ namespace std return hfunc (o, size_t (0)); } - template - struct hash > - { - size_t operator() (const db::simple_polygon &o) const - { - return hfunc (o); - } - }; - /** * @brief Hash value for a simple transformation */ @@ -380,15 +290,6 @@ namespace std return hfunc (o, size_t (0)); } - template - struct hash > - { - size_t operator() (const db::simple_trans &o) const - { - return hfunc (o); - } - }; - /** * @brief A hash function for a displacement transformation */ @@ -405,15 +306,6 @@ namespace std return hfunc (o, size_t (0)); } - template - struct hash > - { - size_t operator() (const db::disp_trans &o) const - { - return hfunc (o); - } - }; - /** * @brief Hash value for a complex transformation */ @@ -434,15 +326,6 @@ namespace std return hfunc (o, size_t (0)); } - template - struct hash > - { - size_t operator() (const db::complex_trans &o) const - { - return hfunc (o); - } - }; - /** * @brief A hash value for a db::CellInstArray and db::DCellInstArray */ @@ -482,15 +365,6 @@ namespace std return hfunc (o, size_t (0)); } - template - struct hash > > - { - size_t operator() (const db::array > &o) const - { - return hfunc (o); - } - }; - /** * @brief Hash value for an object with properties */ @@ -507,15 +381,6 @@ namespace std return hfunc ((const O &) o, db::hash_for_properties_id (o.properties_id ())); } - template - struct hash > - { - size_t operator() (const db::object_with_properties &o) const - { - return hfunc (o); - } - }; - /** * @brief A hash function for a shape reference */ @@ -532,15 +397,6 @@ namespace std return hfunc (*o.ptr (), hfunc (o.trans ())); } - template - struct hash > - { - size_t operator() (const db::shape_ref &o) const - { - return hfunc (o); - } - }; - /** * @brief A hash function for a polygon reference */ @@ -557,15 +413,6 @@ namespace std return hfunc (*o.ptr (), hfunc (o.trans ())); } - template - struct hash > - { - size_t operator() (const db::polygon_ref &o) const - { - return hfunc (o); - } - }; - /** * @brief A hash function for a path reference */ @@ -582,15 +429,6 @@ namespace std return hfunc (*o.ptr (), hfunc (o.trans ())); } - template - struct hash > - { - size_t operator() (const db::path_ref &o) const - { - return hfunc (o); - } - }; - /** * @brief A hash function for a text reference */ @@ -607,15 +445,6 @@ namespace std return hfunc (*o.ptr (), hfunc (o.trans ())); } - template - struct hash > - { - size_t operator() (const db::text_ref &o) const - { - return hfunc (o); - } - }; - /** * @brief A hash value for a db::LayerProperties object */ @@ -637,14 +466,191 @@ namespace std return hfunc (o, size_t (0)); } +} + +namespace std +{ + + template + struct hash > + { + size_t operator() (const db::point &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const db::vector &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const db::box &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const db::edge &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const db::edge_pair &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const db::text &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const db::path &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const db::path &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const db::polygon &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const db::simple_polygon &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const db::simple_trans &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const db::disp_trans &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const db::complex_trans &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > > + { + size_t operator() (const db::array > &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const db::object_with_properties &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const db::shape_ref &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const db::polygon_ref &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const db::path_ref &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const db::text_ref &o) const + { + return tl::hfunc (o); + } + }; + template <> struct hash { size_t operator() (const db::LayerProperties &o) const { - return hfunc (o); + return tl::hfunc (o); } }; + } #endif diff --git a/src/db/db/dbPropertiesRepository.cc b/src/db/db/dbPropertiesRepository.cc index b70850859..790b2a919 100644 --- a/src/db/db/dbPropertiesRepository.cc +++ b/src/db/db/dbPropertiesRepository.cc @@ -296,7 +296,7 @@ PropertiesSet::hash () const tl::MutexLocker locker (&lock); if (! m_hash) { - m_hash = std::hfunc (to_map ()); + m_hash = tl::hfunc (to_map ()); if (! m_hash) { // avoid 0 value as this is reserved for "not computed yet" m_hash = size_t (1); diff --git a/src/db/db/dbShape.cc b/src/db/db/dbShape.cc index 572c66139..75de096c5 100644 --- a/src/db/db/dbShape.cc +++ b/src/db/db/dbShape.cc @@ -867,15 +867,15 @@ size_t Shape::hash_value () const { size_t h = size_t (m_type); - h = std::hcombine (h, std::hfunc (m_trans)); + h = tl::hcombine (h, tl::hfunc (m_trans)); if (m_stable) { // Use the bytes of the iterator binary pattern (see operator<) for (unsigned int i = 0; i < sizeof (tl::reuse_vector::const_iterator); ++i) { - h = std::hcombine (h, size_t (m_generic.iter[i])); + h = tl::hcombine (h, size_t (m_generic.iter[i])); } } else { - h = std::hcombine (h, size_t (m_generic.any)); + h = tl::hcombine (h, size_t (m_generic.any)); } return h; diff --git a/src/db/db/gsiDeclDbBox.cc b/src/db/db/gsiDeclDbBox.cc index a1493570a..9b7d7177b 100644 --- a/src/db/db/gsiDeclDbBox.cc +++ b/src/db/db/gsiDeclDbBox.cc @@ -124,7 +124,7 @@ struct box_defs static size_t hash_value (const C *box) { - return std::hfunc (*box); + return tl::hfunc (*box); } static const C &bbox (const C *box) diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index 9dd9d1bc9..4b4bc70a5 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -448,7 +448,7 @@ struct cell_inst_array_defs static size_t hash_value (const C *i) { - return std::hfunc (*i); + return tl::hfunc (*i); } static bool less (const C *i, const C &other) diff --git a/src/db/db/gsiDeclDbEdge.cc b/src/db/db/gsiDeclDbEdge.cc index 6bef103c3..552365a38 100644 --- a/src/db/db/gsiDeclDbEdge.cc +++ b/src/db/db/gsiDeclDbEdge.cc @@ -170,7 +170,7 @@ struct edge_defs static size_t hash_value (const C *e) { - return std::hfunc (*e); + return tl::hfunc (*e); } static gsi::Methods methods () diff --git a/src/db/db/gsiDeclDbEdgePair.cc b/src/db/db/gsiDeclDbEdgePair.cc index 811c46dc5..8c34e5507 100644 --- a/src/db/db/gsiDeclDbEdgePair.cc +++ b/src/db/db/gsiDeclDbEdgePair.cc @@ -62,7 +62,7 @@ struct edge_pair_defs static size_t hash_value (const C *ep) { - return std::hfunc (*ep); + return tl::hfunc (*ep); } static gsi::Methods methods () diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index 2337f4712..19ab98411 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -126,7 +126,7 @@ db::LayerProperties li_from_string (const char *s, bool as_target) static size_t hash_value (const db::LayerProperties *l) { - return std::hfunc (*l); + return tl::hfunc (*l); } static bool log_equal_ext (const db::LayerProperties *lp1, const db::LayerProperties &lp2) diff --git a/src/db/db/gsiDeclDbPath.cc b/src/db/db/gsiDeclDbPath.cc index 2a1f84665..5fe72e8e9 100644 --- a/src/db/db/gsiDeclDbPath.cc +++ b/src/db/db/gsiDeclDbPath.cc @@ -111,7 +111,7 @@ struct path_defs static size_t hash_value (const C *e) { - return std::hfunc (*e); + return tl::hfunc (*e); } static gsi::Methods methods () diff --git a/src/db/db/gsiDeclDbPoint.cc b/src/db/db/gsiDeclDbPoint.cc index 9bf1ac090..e67501799 100644 --- a/src/db/db/gsiDeclDbPoint.cc +++ b/src/db/db/gsiDeclDbPoint.cc @@ -95,7 +95,7 @@ struct point_defs static size_t hash_value (const C *pt) { - return std::hfunc (*pt); + return tl::hfunc (*pt); } static C move_d (C *p, const vector_type &d) diff --git a/src/db/db/gsiDeclDbPolygon.cc b/src/db/db/gsiDeclDbPolygon.cc index 636d66119..4ebb782a0 100644 --- a/src/db/db/gsiDeclDbPolygon.cc +++ b/src/db/db/gsiDeclDbPolygon.cc @@ -275,7 +275,7 @@ struct simple_polygon_defs static size_t hash_value (const C *p) { - return std::hfunc (*p); + return tl::hfunc (*p); } static bool touches_box (const C *p, const db::box &box) @@ -1215,7 +1215,7 @@ struct polygon_defs static size_t hash_value (const C *p) { - return std::hfunc (*p); + return tl::hfunc (*p); } static bool touches_box (const C *p, const db::box &box) diff --git a/src/db/db/gsiDeclDbText.cc b/src/db/db/gsiDeclDbText.cc index ec665c9d5..54e3636f4 100644 --- a/src/db/db/gsiDeclDbText.cc +++ b/src/db/db/gsiDeclDbText.cc @@ -169,7 +169,7 @@ struct text_defs static size_t hash_value (const C *box) { - return std::hfunc (*box); + return tl::hfunc (*box); } static gsi::Methods methods () diff --git a/src/db/db/gsiDeclDbTrans.cc b/src/db/db/gsiDeclDbTrans.cc index dfa6b2df6..f9c579986 100644 --- a/src/db/db/gsiDeclDbTrans.cc +++ b/src/db/db/gsiDeclDbTrans.cc @@ -225,7 +225,7 @@ struct trans_defs static size_t hash_value (const C *t) { - return std::hfunc (*t); + return tl::hfunc (*t); } static gsi::Methods methods () @@ -853,7 +853,7 @@ struct cplx_trans_defs static size_t hash_value (const C *t) { - return std::hfunc (*t); + return tl::hfunc (*t); } static gsi::Methods methods () diff --git a/src/db/db/gsiDeclDbVector.cc b/src/db/db/gsiDeclDbVector.cc index 028ca9dca..19ce2b168 100644 --- a/src/db/db/gsiDeclDbVector.cc +++ b/src/db/db/gsiDeclDbVector.cc @@ -114,7 +114,7 @@ struct vector_defs static size_t hash_value (const C *v) { - return std::hfunc (*v); + return tl::hfunc (*v); } static db::point add_with_point (const C *v, const db::point &p) diff --git a/src/tl/tl/tlHash.h b/src/tl/tl/tlHash.h index 82b379a92..48ab308ae 100644 --- a/src/tl/tl/tlHash.h +++ b/src/tl/tl/tlHash.h @@ -47,7 +47,7 @@ * for use with std::unordered_map and std::unordered_set */ -namespace std +namespace tl { inline size_t hcombine (size_t h1, size_t h2) { @@ -57,14 +57,14 @@ namespace std template inline size_t hfunc (const T &t) { - hash hf; + std::hash hf; return hf (t); } template inline size_t hfunc (const T &t, size_t h) { - hash hf; + std::hash hf; return hcombine (h, hf (t)); } @@ -93,15 +93,6 @@ namespace std return hfunc (o.first, hfunc (o.second)); } - template - struct hash > - { - size_t operator() (const std::pair &o) const - { - return hfunc (o); - } - }; - /** * @brief Generic hash for an unordered set */ @@ -118,15 +109,6 @@ namespace std return hfunc (o, size_t (0)); } - template - struct hash > - { - size_t operator() (const std::unordered_set &o) const - { - return hfunc (o); - } - }; - /** * @brief Generic hash for a vector */ @@ -143,15 +125,6 @@ namespace std return hfunc (o, size_t (0)); } - template - struct hash > - { - size_t operator() (const std::vector &o) const - { - return hfunc (o); - } - }; - /** * @brief Generic hash for a list */ @@ -168,15 +141,6 @@ namespace std return hfunc (o, size_t (0)); } - template - struct hash > - { - size_t operator() (const std::list &o) const - { - return hfunc (o); - } - }; - /** * @brief Generic hash for a tl::slist */ @@ -193,15 +157,6 @@ namespace std return hfunc (o, size_t (0)); } - template - struct hash > - { - size_t operator() (const tl::slist &o) const - { - return hfunc (o); - } - }; - /** * @brief Generic hash for an ordered set */ @@ -218,15 +173,6 @@ namespace std return hfunc (o, size_t (0)); } - template - struct hash > - { - size_t operator() (const std::set &o) const - { - return hfunc (o); - } - }; - /** * @brief Generic hash for std::multiset */ @@ -243,15 +189,6 @@ namespace std return hfunc (o, size_t (0)); } - template - struct hash > - { - size_t operator() (const std::multiset &o) const - { - return hfunc (o); - } - }; - /** * @brief Generic hash for an unordered map */ @@ -271,15 +208,6 @@ namespace std return hfunc (o, size_t (0)); } - template - struct hash > - { - size_t operator() (const std::unordered_map &o) const - { - return hfunc (o); - } - }; - /** * @brief Generic hash for an ordered map */ @@ -299,15 +227,6 @@ namespace std return hfunc (o, size_t (0)); } - template - struct hash > - { - size_t operator() (const std::map &o) const - { - return hfunc (o); - } - }; - /** * @brief Generic hash for std::multimap */ @@ -327,15 +246,6 @@ namespace std return hfunc (o, size_t (0)); } - template - struct hash > - { - size_t operator() (const std::multimap &o) const - { - return hfunc (o); - } - }; - /** * @brief Create a pointer hash from the pointer's value */ @@ -344,7 +254,7 @@ namespace std { size_t operator() (const X *ptr) const { - return ptr ? hash () (*ptr) : 0; + return ptr ? std::hash () (*ptr) : 0; } }; @@ -400,6 +310,117 @@ namespace std #endif +#if defined(HAVE_64BIT_COORD) + + inline size_t hfunc (__int128 v) + { + return hcombine (hfunc (uint64_t (v)), hfunc (uint64_t (v >> 64))); + } + + inline size_t hfunc (__int128 v, size_t h) + { + return hcombine (hfunc (v), h); + } + +#endif + +} + +// provide some missing std::hash implementations based on our tl::hfunc + +namespace std +{ + + template + struct hash > + { + size_t operator() (const std::pair &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const std::unordered_set &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const std::vector &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const std::list &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const tl::slist &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const std::set &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const std::multiset &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const std::unordered_map &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const std::map &o) const + { + return tl::hfunc (o); + } + }; + + template + struct hash > + { + size_t operator() (const std::multimap &o) const + { + return tl::hfunc (o); + } + }; + } #endif diff --git a/src/tl/tl/tlVariant.cc b/src/tl/tl/tlVariant.cc index 38b5bf14b..c928ece63 100644 --- a/src/tl/tl/tlVariant.cc +++ b/src/tl/tl/tlVariant.cc @@ -992,63 +992,63 @@ Variant::hash () const size_t h = 0; if (m_type == t_double) { - h = std::hfunc (m_var.m_double); + h = tl::hfunc (m_var.m_double); } else if (m_type == t_float) { - h = std::hfunc (m_var.m_float); + h = tl::hfunc (m_var.m_float); } else if (m_type == t_bool) { - h = std::hfunc (m_var.m_bool); + h = tl::hfunc (m_var.m_bool); } else if (m_type == t_uchar) { - h = std::hfunc (m_var.m_uchar); + h = tl::hfunc (m_var.m_uchar); } else if (m_type == t_schar) { - h = std::hfunc (m_var.m_schar); + h = tl::hfunc (m_var.m_schar); } else if (m_type == t_char) { - h = std::hfunc (m_var.m_char); + h = tl::hfunc (m_var.m_char); } else if (m_type == t_ushort) { - h = std::hfunc (m_var.m_ushort); + h = tl::hfunc (m_var.m_ushort); } else if (m_type == t_short) { - h = std::hfunc (m_var.m_short); + h = tl::hfunc (m_var.m_short); } else if (m_type == t_uint) { - h = std::hfunc (m_var.m_uint); + h = tl::hfunc (m_var.m_uint); } else if (m_type == t_int) { - h = std::hfunc (m_var.m_int); + h = tl::hfunc (m_var.m_int); } else if (m_type == t_ulong) { - h = std::hfunc (m_var.m_ulong); + h = tl::hfunc (m_var.m_ulong); } else if (m_type == t_long) { - h = std::hfunc (m_var.m_long); + h = tl::hfunc (m_var.m_long); } else if (m_type == t_longlong) { - h = std::hfunc (m_var.m_longlong); + h = tl::hfunc (m_var.m_longlong); } else if (m_type == t_ulonglong) { - h = std::hfunc (m_var.m_ulonglong); + h = tl::hfunc (m_var.m_ulonglong); #if defined(HAVE_64BIT_COORD) } else if (m_type == t_int128) { - h = std::hfunc (m_var.m_int128); + h = tl::hfunc (m_var.m_int128); #endif } else if (m_type == t_id) { - h = std::hfunc (m_var.m_id); + h = tl::hfunc (m_var.m_id); } else if (m_type == t_bytearray) { - h = std::hfunc (*m_var.m_bytearray); + h = tl::hfunc (*m_var.m_bytearray); #if defined(HAVE_QT) } else if (m_type == t_qstring) { - h = std::hfunc (*m_var.m_qstring); + h = tl::hfunc (*m_var.m_qstring); } else if (m_type == t_qbytearray) { - h = std::hfunc (*m_var.m_qbytearray); + h = tl::hfunc (*m_var.m_qbytearray); #endif } else if (m_type == t_stdstring) { - h = std::hfunc (*m_var.m_stdstring); + h = tl::hfunc (*m_var.m_stdstring); } else if (m_type == t_string) { for (const char *cp = m_string; *cp; ++cp) { - h = std::hfunc (*cp, h); + h = tl::hfunc (*cp, h); } } else if (m_type == t_list) { - h = std::hfunc (*m_var.m_list); + h = tl::hfunc (*m_var.m_list); } else if (m_type == t_array) { - h = std::hfunc (*m_var.m_array); + h = tl::hfunc (*m_var.m_array); } else if (m_type == t_user) { // NOTE: this involves pointers ... - h = std::hfunc (m_var.mp_user.object, std::hfunc (m_var.mp_user.cls, 0)); + h = tl::hfunc (m_var.mp_user.object, tl::hfunc (m_var.mp_user.cls, 0)); } else if (m_type == t_user_ref) { const WeakOrSharedPtr *ptr = reinterpret_cast (m_var.mp_user_ref.ptr); - h = std::hfunc (ptr->get (), std::hfunc (m_var.mp_user_ref.cls, 0)); + h = tl::hfunc (ptr->get (), tl::hfunc (m_var.mp_user_ref.cls, 0)); } return h; From 2191febc38aa9c517b8c4d522beffae28c52f8bd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Mar 2025 20:41:24 +0100 Subject: [PATCH 086/392] Updating test data for better robustness --- testdata/drc/drcSimpleTests_au70.gds.2 | Bin 0 -> 72504 bytes testdata/drc/drcSimpleTests_au70d.gds.2 | Bin 0 -> 53058 bytes testdata/python/dbLayoutTest.py | 16 ++++++++-------- 3 files changed, 8 insertions(+), 8 deletions(-) create mode 100644 testdata/drc/drcSimpleTests_au70.gds.2 create mode 100644 testdata/drc/drcSimpleTests_au70d.gds.2 diff --git a/testdata/drc/drcSimpleTests_au70.gds.2 b/testdata/drc/drcSimpleTests_au70.gds.2 new file mode 100644 index 0000000000000000000000000000000000000000..313ee77ceaacf39c4ff2fc88b07bbde10a121d8e GIT binary patch literal 72504 zcmcItOUz}*Q9l1;?tjOzJx)9h4@JpXnuQY&LP{KG0uq@R7NA%mi%1z|1t`D*5;iC# z2(btufP}zENEorPAczeDBY}i?i3Nm&SB4}&5R~<|0`el-_HT3>EwlJ+CM|&c);^lR^+dID1PqBhoao>4w2(+`LmD4&z*fV%AG$? z-}!nx6QP896Rkm4EcHC?D{_G(G;4^4~<^P4i9pvm5bqXO~evBgb#FpMT|g zl)rL6%4g*G^|t)QKFVL*i}D#czHiI#|2xXVepo+|EVzC z?-Pi17`*c2w)yB`hz57{sP$F`O7hV|sEOZ$CEUY{FSy*h~v%9Z75&J#-nk&z} zR}lXMD?ahnlx|w4BK_E3id|pm+NvQfxua+4hV=GzLvzU+2RvJ{hLXM-P$uBEj+MoU)a$K;;`^_C*`h|WXa$HdJYTLi!z_mBIxGt`f z$nnK`n_OHA*GA-cV~r0l*GL(15IHWWby>-CA2#-9w)Y|?R$R0NZA0X^w$UOE9$Bty zKrg+nl{}wG9f%wkY|7Jqv?r0{3%fqLLH}%5X5h5fR$SDXYanu5aF~nwb3H_k3)Z-< zE!RjHau7K#sBOHGAMkP3Wf6NTF4}^&A#z;XXc2pl>2(d}{H)i)cZO>)|7Whn8V%QA zEy!GpHC|t?5kA6N;WN}VBF6f&+D!$Z^fJu)X05y`ZcSh_&sdCm>>tX+E!U5fiUHL@!w*ctd?(=xBIh zGpx&DyCGiK4{HQs&9Ebp;|u?0-j*F;GsGp=e)F6cu) z&@V)eFZ`qHnc;$7kO6|Dmm?PW#&AI|ryrVnfeU(F6Q2(3In8xL`w{U5e*xjY-JKaZ zE?AWxjDxB?)_~^#xhIJn?>(P>rJso$*ZyAQ$dxT1Gnyk?Tv(6CM~l6-(hFRpxnaTe zr1tj)dmH%;{x-%CBN@yTFp7kL-He!Y%)=!-RO^oilx5Rclw zh70;&F9WZ=G}PNwxeoNN(d)ptTX7-ZfD?IccV|Y93u>QM*9bd72FRRA;)Mh zIGnxVYK;RqxwghP=vW=Jy|$5&hiwL8y(&K#M@@N;T^BXQtOdHjuE1+A_50*S4lrEa z*lFnnA7PE7G2P;VUY;D#S|jxF#NmSLX&r|f;=ssnh(jYk=zAT9Mt;z)Re3yrAdai@ z8}_l$e=x?X&mWBUs{CLqY56zm_}QSpk>8-dk>B7yBY)`nH^iTL{)YHB^3Wf30Ej*h zb(hF-!K(kDzq5{*=R<$U1KDR1Ilg%Qn+}X0BflYjjQoc9G4dPY$H;GpA0xjZevJHv z_%ZSu;>XBuh@WQ;j2|PvA%2YfhWIh^8{)^vZ-^fwzaf5%{D$~3@*CpE$Zv?BHy;>3 zMt(#582JtHW8^o)kCERHKSq8-{22KS@nhsS#E+5R5I^tJ@iX{tH_n98Xx`dAb@!IM zDE@l;CXQiLZ$yW;tf3*_npSfTM!v<@K?mdD8|eF6dxHgzyJZQ~A9H#rSMT$HCteP>gTsFv2ruH{`wocv*e-UoqZZ zGk*8cGp=D&Sv6-`cg8i0olML3s^?Xgs)AuWO2##eN6EN`@hBPBFdil28pfk!T*G*j zjB6N=l5q{=Q8Mm|@!*WRVmvtGt{9nFnQ4g}?`B-X$ZQMToN*1~Q8KP!JW9s>o{bsz zq}t;eMn}dSWOQWQK}JW$9b|N5+(AZ1#vNpIWZcMD>T!*)9T|6!(UEZzW1+`2Iyy4$ zAfqGWCdNXKYjgzTV8(SY4rbiMSm<$AIx@2|(-JwJGVUOwBjYBrs!oL7|}%y3ot z!F*PgAIx4&`GH*3lpn}UO?l=U<{u)*1*`IdS*I#Lm}9E)gBeB3e_%uYXrDg~<6uT{ zFb?Jz2jgJYaWD?%AqV4NW@!*~>58^)vf+Ato)*M{*ZzW${4HEyMl zn}9ff@*K*1mF9I`F!CX8UzNw_ke37Ai+@oBzD2&vNJr&f>r%PUAT~HYh80^OH$kKfE`S>j4f%wt! z#POE@PVdIQc!b~XN0x+qM2@fi5|Ys${=2vr{ioI&?7?xts{W8iyat|q+J^}_E?AY{ z-HCec?nJ#2kIn5WHZzvKJ1~rcQ8zQ*ES&QV z<6s=jj6*si6NL!Bo-%YojtkC=Hw*VAM#sTepBYO!{<)4Goh|5NB7B|ZXU3a_y9mR$ zjOS%NNb`!Z5RZm&8IRBlIs);N<`rX4$HAJX7<)P*`wvtR#aOs6F{?(@FiJA>jk(UH zc?To*F>D2d-L$7TE;uuGqh~Pp&5Wg+RkLd4&!D4@&*W#UVP@>===OD&aj;^~I+il7 zS+$$7t7FJH_q$b9=~&1;Mn^EBFAB_yY{qdx#dv%Dt+!#sp2;2dz1TTPrw}@*JigbA zg&b}eRfOaW(ascw%4Hbc5kBM@-@fj7M(<;+w$ec;&-7Le&#;$cFH7XOpknNK#;@Z@ z#?9viiS%&ZA9{2c+?yAt$@hFwctokTtF2m@yW2qJzMt9#+>ZA^1?CR*wW?jakbfjk0 z{yI}QAsI$g1z(qPd)@6B#-nrqhS6WI3sqsocyNAPF&>;BSB$<|SjgckMpk}yc|?xe zZm-9P=s_nBR1C{IK~)tBJ%!QnD7RdO(H}j%RUhScW~HMqdV0H1cY8U~Q{^vrd%Zp5 z&Su>l?yrR-*^Fn@+~KD)0>`^&*Oh0o{Up+H`ar}7EOt`82>0j5!dYRZBX=M7Gm+zV zU-xu$_dVTO=&v)qc=Xr8o*khhqhu_4dOEs$iqZ+m_}ag(d%pIcK67*9FXJoyWf>Ob`s?Ae8>?wE~vk|G4lG`bnGpD z2glw$%~$0i!yjZ#{ljs=syyx@@Hrr4`JE$><1Kl9$A;<*dGhR^iQk>2`ReoeogEPJ z(`yZ78c_5G9sLP{w!gF|Ii@3-0fgEo=ALnO$4hT8y zTO!9>^7ssYAcwdka=ayvbtC@?rSfcK*ux=_gHar^GdUv0?$ z>YP#N;SD1+8K?+~u~0`>jI0H$4MdI$IvDk?4-qcAKBC$6Gh?X>Gde<=KiEV1hvR|{ zMnn-Lfncjt76+rwReGNmA0w*7cfw4f`DSEwpc%*Zr1} z54k`r2ZXiQw=pWyd{v(3DrPM9+1z<)zA7KOgL`r1sNLvOh!<&Guqw|wz+8d#VsA1# zrTMCS@Ui$5xV(wrZM|Sso;jYGh36dRvNW&dKdt?T&m%h7Colb%=Bx6sH{{u8F8!D0 ztMbd|vv1~(?>#@*?S9#R>h`nxd@Y}E|Mqj=n&zwWI$qEN^4aaTH+ww ze-Jrt`)%Vt=r?eaRek<)|G}5ohd}%k_TTFHjMIFtePdn^^jpNQk;l5T-DSUF{n&Q_ zlOJThsz2flIs&1ujwg-_R^{>htoLPnF6@ct=kXhLdF&TFhxra&G4x<}oaU>~S2hlE zdOv|T%~$22f6g2Cquz*n=%2^!dCS-z`wY+Fe$)A!`UPAS-5FPl_%ZVOd5l{k^$^th8~L#BxX&I$yWzR8U*4B?{~390FFbddKMVEG z$Zy!cMt+0;jr<1x8+qu@Je&9NzL`Ib{D%5z*Sk}WduNVs_b;G!fN9+p3B@jRBcr2W&4dYV_{z#9S3WtgE9Fs=t&4on;9Kp$ALai=~&3&hVdx*(J;Cryt`_Dems6W_Ux$lG4@KO15uvo zMGqo8*XjGQrz@N(6=P3FcgF2{Mr8xtCx!k`WszCOE~7h}bs7Ctp=U?GuY2(bU$b*+ zoL^=;mVWEj?!vL(zMdJocJ#+%;X7fYO6eP1qoccWbs7D> zF4P&LqdQZSs)Av3M^Bg0-8FaP(aqS^(Vxu<`O(6d!WwvI)K8hg0bUK zZZr+!QSO=zqd&q6os?lbN@s5v;p+?O$$*S>BF6<)Z`Sh+R()`1Z*=tU!k!)7^}5vA zuXOaSy0@?45uRJ}-q|}l`<0FdclImBteZqpc&~w&bd`A(Jq6GFo$l=68UCREMr|Z= zTu^^wWaQD4;B!F84ZLA)1QY>wvu z@m%z(M2@$f&u{jDs8|EPVPw`1{ov7n;z{U@JP?rM_KlU5JoW+P@vRu-GIHF$aj=p{ zR{(iLKjbrV+`jQ)YMg9ZAet15S*e}V8N){*qE zz3@5OjeZ1jT(ByS&+rHK*FPL@$!q`N^Rqrx|NXtr|Jr|z_?s(Ashc&7gFDi}h}ERV) z;V#|SQD+LQdd`<|)oFfa?AmdzOEKeC>ZF5F*BR^?b`Xos&%ub5!jAMaSYgk=p2lT< zW-Q%&8#}_+`4n;)yW7I)L^0l8-|T$e*6XMv><8U=?qapTxag?+4I|IBaBb|HxEIrW zRX)_>d$C{Vy#?_ijSDvA2j1SR%ClB6BH5=ggQoeaJfnm8k$om3KFwF z(!7@c2kk%Tix>lz{Fmm3<#}#qP3p;Gy?8!z#M1w1zWV%7@$Z-Y&)XBhs{En$+u(m= zf2^H*fOa5qT(GLY`b_X2WZ_33e$sqZ9`yY`XJn|Ht!+eEvHuT`RJIz<+H|$^Ie;vR1CT&08 zd!+g5^Ra&ByT&&ZReA6ad{b1F$MXm7J*)DlZLmLU9sZqh%W=V~Jf1)BEmBn;c?!>A zzDo5f|89Mn*YffX$@~|6&VN(ne^%mG@bnKwmk-a#aY0p43q7M@oc=5_0>OClb2EZ5 z%{v$&b^7~}b^qqbM&!7lgYon|(szAUnBF7$d`cqd$B7&dI5U>|F|%s79lMPCUq~w! zgdMMb=ZvsOnxAznomY*HZpN;T%KAY^*b%w{mDgq+yNtVEiIPAtUA%urFs6A27JubQdzaaJGcNB8`ajj$F`k>eMdv2b2p zF`m6HGM;@YGG^rX>%5F7Pe;a+|BQ?oIsUYlasOYD@#>#OMk2=rm1laZM$}wYj-MuYlTI_3N-i!Bh z+?#O`*}mxM?Lv6w%0EWWTwINwA#z+$*|E20+}W&~!&O!dzDAC0*TRu(cJ{Av)mNT~ zt0r<>P_4RWM|j4UDS8p^&yT$>1v+y7az7I}ZufOhM|a=Tt%d$N(~C!cE$rDb)IeAC z^mIhVVwAI&AaYz##p6hXbN_Nb6FF}8bhM+GF zQEw23sXi-xe03T6sQ0`Sk53=oXQIv@lILOP{N9|&8@~8S{YtoBw|@8AC%>0>UGEE@ zKB;ao7L}cSaTwxfAM@BD@dKW0XY>1pzIE~YKE$EKz`U+f{`q3P&#d-Q=DXdrzZyK% zICSzbX8^nL)6j?CZ>H}{HrprdbADg6nWx$xmio-bx~l!E)9=-N;pC~-3nx!KZxr@v z!#?#qc6(hf8a(wpU#No(o_fDg*zXOVdi^T&haDdE2F4uErT(YGQ_f?4eadyv#{<9T z`fmF0LOpG)E1d)RUWu}g&+qV;dXHT3_}0buDzMKE$@7TA^7+EIFY0yB7thu6sWTs< z??FEXl$xRJ?8_U$Uve%$97*4yczpJOo$Lvl7_!R{M}FY66SpB z^B4SHz6bEFi|?7G_m1XumHhrs^**!ON0s`+29K*>ZSYj%*{P4K@9pSQ&SQmmZt%GJ z=LV1Kp0>f`>cbm6RlgrA4xK#JKD<=F8tZbM3%2kaYh6yBYCd%GxXvjJ`&9c4r#`Op zPD7u1{VMb=4IbCIslijP%eU9(rUnn{S3Vb*dn6xEz5gln;SGIU=ePz>=nq+!O@COZ zrwty|4ZmRy~bh`g(@L*l#^MzlZ;qz%npK|}>*C)rbxv%r_ z)cbHB5BxQJUT)Y2Jj371GRrEh8zs`N;hI)$HgSu+(4}E!~zEA!B z8omc;*vIvKQ-jBKpU~hz{FL{pFVENWhL5LyZt~^TkPr8zdww5JwNG%ybG2`A^3=}- zzJ3Gert&$(#{+-m_%Y{hpWorH^7ll3o}9-_9W0!?8~XzBT&~MLo_gQnI}g_TC?5~( zT&_F5b=B*jkEeR>z8l-s_!}JkzNLB(;N+?Iy}rB=^6KdQSR)SW=K`PK>*o|7Pko>I z>{HJhKA!5ivUFe9uyggC>*RrbkT1(vow47`eYlUu z^?i3^U1Rr|;t0H-`Oo?{uNF6A)7j62Z)61JP0!Ea<~QK%Ig>$3jHmC6lBe&COpCj+ zDa|_=vFlGh7Js`DnGIDWBga3cqN}T;&LgLvh>RJf&rb6W9m9>6=(4!^ioXHLahHx@ zV$ToV!rdG(odsmxp<~$9qRV1;$J>35yL1Fo{t?|m{vpnD4jn^hD7q{<#Q2+p9Czuc zdj;4r^sq$HG0l5)d^Se-?%5dOi&MYb>_N&tSg+vip*Ygp^lP^cfT!9}%<~;soua3BzgDz)ph)fwde#6U{ zPhGD~z9x!if}P!-Is0PL@iOU1%b<=0>mQnME%X;t(D}Jmwz9 z#Rxm`7VhdUuA0bkLA7e#4OZh(a!rW4dy(;i$nj5j7-2s|ctA1{IbQ8pOFF`iU<@cs zupe{`$Z;1Vbc4NsJf#vj?z1CwgCr14(2dA(A7k){tS%rk1d-#eRfFluXVPw4W?YV& zY}Sh&cPC|Tp=1%OuG!t~bs2S+f}5z_Z-}Q7qUx@?lrH0y55(RK2&To|%arETF4T9) zrBz>iS7Zc&adEpnrFjRV_6!*JzZ{u?Saq6rFlsvvb$eYqf)O24z!&1G(feTad0cm- zT}FSV(6{p?&-k-hj}bbaNfixv88ZcLHjy#%_zj(LdyLQ#k^zM&+zAmm?qY;)xk5dN zy0t6Uh>pJM+GB){%&g3`M2_2x+hc_NAPEE$>`3Iej}bchI?@px!3Z5!9Vy36C)Hzg zpV-YCPkY(E%bO1{x^5M^jNxP?S!?)&G{l%sYm#%2ck++qq}3^|%Lq1PRvox#0lM|8YmZUKBN)$rFRmJhRi}A}9nXF^N&>;S zxTBuZyn_*)6f$nWp%c<=^;S&D*I>%0wFfcxM~<6QTu(&8Z0QG5WJvk5TCn^y=Pdnr}o(b_8SAQ?e}>k#z&k*|@oC=}FXIOMB#7#a?%( zDWI@le^<0$K#t$lIeZ;GmJvG+`xuBG0{3P_jteRs*ZI*hKK^ghG`&yuv*1HN9d#sf zTu?EtI{?dw)k3d;FGogXS*$vbKd%{kt43#n)d8{E-JKaZ?prl<153bhlj>|I+?=Hh;jK=Bv*~{6S|RZak1J zh#VKJ%Io#R?v?Cal@A$}`;_~Z$Z@-Wjs3O#ux>n`sOGBA$GRaC?C^1$H90O=mDlly z=PomvC;lKGdXj|DKgDPD`Oup+4b?u>;e;F)tjcfL|Hl51hu#4f&(Gs`>(5W|2?#t( z`=$A&{;(OI&$!LV@n`DKhy9>uhy%u5LXHbopAR`aAIKhq$Zu(LMXP^gvuv)MXIWDMH-P<#M9Si-vu_Js9y+V}K>x$B`w`wpkqc<|WV(jfgohe{H z_?ieirg_EKiyqPN@c!P|(Vr=Lb_63<8L(cl)T(=oD&_`rc+ZaTHFOM7wAeEo7gRcq z#3OSIa}JT?Huv|6YZs0%`gUP2dh{-AbkNGzz33V2Nfl$S7V7HC zYCuHvr+LL#(DAnJd$f*VLJShYnC2B@K}W-gcmzwp7wt(VkN2wUTeXh=nc#v24d)4oCDr#}Gw}J;QOq*^cJ#fbGSVa))hn)O!ZHfrY5% z6k{(__;noFGt8{av_y{EjN6NFzpqDh^wpW3j{ZzB!syEsy*=ZP@Lu%jE(Ni;%oIbt znTc?Ju1{HX8UjQpTRRrzH$g%13|o=x*r`N8OK%45yo+0}DzRbJ(l zpm(KCSLFwHMpb?=#;Wq#f3Q1p5D{xj^Hq81Kd=w0@;d%lPl$Many<>kZ@I%-RFOPx zzjrY4o8k))*1g=LX}&7#QSbv(Y%ENz<3HW05v>D_t#MS51{{d-V6mRbKo2?0s%}^V8g$fD%j!J|yOfs4;03Y;yIbYPz;pF7z@WAF|_s!vSxPQ2JxNkVs{~j(*wm05!eHgBup8x1m zzy8G2pZTNzdfSix#rJ*u_uuo(@W8o`+itf{4a3>RFl_HU88*ZAu9IOHuHAE*{;R&u zKal>%tzn>4TW4=h_{)3z{lH%y@y`y!$)|_ycWiH_pTJM;hC{j}3(oFHT=zmNVUbIX zc)PQCZQNSeIlb-eoWJVr>|QQA%iehX^=~ILhat&VhT-BlCAofC>|FB3*^Tt`zW{H* z-|$Ah%1QS54f_22>Zr-EOFwY-!myWEhh>!8C)3Z}O3uoXxFnbO$C%=0!3l|2UOZML z*OMIa*@~oJiD}BhVf(zcV(F)L?;EvLk?e2R*9sKd1c{)W6mR4PVfn8s9Cg zA5PTqD@k-!*h|UvB(;48fBve>1=I^&9$(w%xjsr4e|lSfj(KvHPp`8VxL(JXpBKHM z2lS^tdEdSM<~6wv)eMaX!@;zMww}-@L>Aoz^eMe?J{L z`4Q#|=6jg0_*>%c)SvKq9otwO%*Z#2ad~nz8ct(^+|@as_v1dy_b3N)xbV$$(C8QJ z2@M_$cDb;IQx6`@>H`)xWnmz~!LC&7omcVS>nd)_>1F*4-wN?Cd`<33F2!V+VD;DXGsX1=-qjD9OrvEggV!Q~n;d`&gy<#az|K*J`BLmIA9b;jlu zs?PAOa26ZB6{`4)wu+^Lvf7~UfxBme=b6JZ2w8aG?%Ckw>RE?$uY?={exEC{rDtV> z=RF-~5>!3#tp2?3;&=H3fdnq_^x;b#{;PE@!xz*i%qQ-X|I)9NYOnCkyP(l87;oY8 zYS=IaBUsb$%Q_;#iqjck@cYcmnaIqdo_Sy6zoM@ntWX!v%!Ndxn3>FsyzkMtZ&Gr7l(l*=e5cF0F- zqv82iMU(Tdie}qCpyc{Q!}(-t_;xP|ZugS#T>LI2*W38cJ}H)+eNrqv{~9INmBq_* zkkPLx!XXcCKOvfIKOvg!{)v+76Ace|u-W1Rd`&qM@ZjcK63NZCB(f)eNy&8;;eZF> z9U$;uVH#ef_AMvF$#8MEe0upRK2giFBcCOUwRV_=ojJ30 zvak>5zAP*=A05_)=WIR~iZ&`24zuu+G5K`sWPxXJR%Qelh3r( zJX2v=^siS<6&_A)`4f6Yn|aPuSU7v;&zW_yxS7`!rNy4t;hEeSI0tvyXAg{R@Xt*|uonF>q8sa;`d=vEaLx3cHms>0IH^C~QhRnU<$i#6H7 zgPaLYA{DC|I&g)>_3*p{S6JL=H?b_%WOL73>=4Z?$QgWPw)$yM@l4aHQsHTs(P}LG z^l#;Cx{&*(uj{8DlguAZ|64K`5?^_L@nl%yJNZ+=L81~S6*}boFyAER!{*z<3yCii z#YrwK={Nk9;2=4tQ;s{bAJ*^e4}}jBJd=7E@_yk{b+e@3q-uxr{}5hCeC7Qx-}d(e z2MPTq9csw?VZKRk8g~C8ypZ_H`xU;;wVZ$B-@cwclpX=RHRahuNw+7H{19bY|5)O_ zN-6834e963KS10l%k|VLB>vrBZ~R->%{9S|GVz~&NccyY^vL_dH#-5A7)K2glA2z6 zuP~01>u*)l;w%HShR{xZCk;0r6~0mK_Y_~mV(?rO4{kn6nR(%nk5&1;_YH`cdLFwW ze@ld1d@vpkUilur_WkeRa|Rvux5huNJMvWj-TOq3QI>pi@zsh-_`L7pONB5`DB+uZ zg#_@Tw^Lyb9@kLcG5;I?75nA-W2Z1q zc|U-c>*Me0(HR;41^g2_e@gu1`i}qW9nN87CQyo{p`K|NgE?Nh`n+cvzG5>qqg>E% zaZ1=2zF-W)`jwm%z04_BYO?Und%DrDiO)O56eGN(zWu5TYgi&I7CTiG<|`H_Jz33^ z9Ug4fZ=QkaOfTsv1b{?12@R9oN>oUfF zCBC9xN0%{t#eWhHXLcy@nD^-))>Rygubu@AKB$k25Bev77k@bQ3R-cVb+O$(^Aqn%4;bWqSm~Wvm&bQU-5Q^!Wn>s21Q+h2<_2 zmUF$1M|6NkmH>ZtgOclYJnoQz2NvIWZVGR1MfidXG2F9Ro8yLsuo+*1F5v5D-EmG|@Fkbz$F@6K& zrN0@5`+liNJpIV~!gyDHH`Y8h=Iu_aMB6Z~5^ck{O0*5*D$zEKDx&?-HjFDp+q{XY zA=>ihsHNIAj1AEaGB!jz$k-6=AY((cgNzN)-VA&Z{Fe+^nd*XFpR;7G%yBZ)W8^wS_5M+j&(-!J~K{icAL~^}MViz)r89YAkfSCXizd^)oK*{wa-RtAGT=+Ri;GjPx*K2s_ zil2i74*#a)dJR9m_ccwTm|($yiCCoApZ8t;F{xkt-!MR8l0l_Way`i^9uvW*-M_hj|xo+P$YvKnSexo5k7OdI^ z#$X*bFy_dQ8NiI7RGHDhh#U#jnlSp4iSZ;j1NGO;8OZ4-&S0Ny^d>tTJ02z1lWfv5 z*i9O>-1o8DZht$ZrFqZPDm}w6u2SKKaizR8oU25~Fs@XQGfw6o^O2J4fxI-FtMqik z2+LQfal^Sv4>pXeoD+r-cCTHJtMoRbF1{KMDs_-k9iN2eb& zFzO74j<|xz-VE2-gN!cu*_%B&)?h5z)EjFZ88DXE3uAj^f}KVptCzQ)hq+d1a_3-Uw;`eR07W#u~q7ayD>T|6R->qF|< z3~O9Z(!~cSL>C{to2udi)4q$>HsjxjA0_P1`z{{(^WKN|LX=!jvWgE(?Nxl>$&1FD z_da&ykHL53zrlCpkHL53ufccJzrhFZ++F{7q$0ou2MNO%LB7Ola|5rQ8@8=?4?h` z%(M)g_)S^{PtgV~gXdD6(Y!@+l`do$SLqpsah0B77=!acgCAGw8AeNRvW{8Tlw1$g z_l$FeK4dso=^2I*maot=4Cg96!!Sb26?%r@T%~6i##MTTebcJ?G&HK6^9FlKzRT9r zsloFp0G|ptPeoijuE}ql{Q8^gNxJx=uV8ssPkr*fi|11XK5=rMWx4p^b0!zB{Ri8f zCx|XyUmtkqDXEJu&&9-lf#0o|`0FQkh*$Ba%&&Rhy?)6TxqjexE9Uxq3dFb}-ta>- zf3^O=F%Ni79nwGe#7*1(CH=P>!RyygFT)u0Xal3FLsOsdm)dJ!)KLRNsp5twpT_Ev zcM^Z*1Ek8!1|5Ty*}$l+mKEgA{_&4H53H|fFix1BBEyNeIrN4WTo{5Sar{R5xCbgv)0!_av1&g!$L zQ!h$y5kGr6osusmUDBJ$MRI+V^^Rf~Pu?xalXnZ#>90_7y_NCGuO_{&q)$mhrgwB- z{c|O`e&yhkF{35k`MvqgWOtHy%QWn!`S$G+hwa-XF1w$lmd~xNLr_ zNUpcV=i+yS5fY4-ezHg~=KaQqpFSvd<8A)1doQ{EV4K}|(>t_*gqDa7CD+^R#{b^{ zkdRzrpzDTp1V*Rhc)8alxNGM zG{PA_&cUx8qY)Tzszatz{s`OjB$;NIqin;oJ!rIh`528jN#)6@TO*$6d4BKafkk*8 zILh0{SjICtKiTNkh_hP0v#s+~-X_jjJI}t|Je=|K%-_v3&OooGJ}R@2@c0#5u?&?8 zFQIaiJW3-Q$~{6OFkl{sOqF{C)8x+nDBDt{%HNz_9HSBM55{}jBfO1DMbuEa z$3_i*{RdyC%00r@s8nc#$~{IS-c5n)AW!A3yG>_og{P_VD?CkSb%m#?gH(9XqZ`ir z3KMb%e}Ov6s|Rza!qhZ9RCtzt zH1+EW4}8=xVO5ymqlV63VQQMyDm+amNQI|q=Bw~DokSHL#H?XmSZ~dY>~h z83ab;@hCqdj3<;_cPHZ|yWvZ9N{>FoSqoq8*DH4|lk;2F8#$q;I=M&v z#@{<6J-J@H-|_SW7JBk5r{sDo=j1dWcJG&L-@RY5J?}SU`}T1`ZXXw>$vHpd{RT$( zQl0RlFL6#I=KJ-^oto!MVBt$}P0oH4=kcvIDc<|_huZrku!#45 zUbroB->)}vjuS)j*<^B%zkcR=lP~lAwA@!GAC9vGW%6a-Z}8D&(}YYFTyN45 zO#APo+c;lPmc6Quo5%H>apgY~WBPkWNSqW`-&G{nlT^Q>v-ngW{QsXBdG2^v_#pHD zFAb!NAFq2ut`866`jnVZ@_rSME{C5(wgXSe^(3o!d{P1UJAOm(lw41;iXWa8JR*bZ zQ*u2?jenxU{}NGmKCy4^hb_D|b)n>XlFgiq$M$(QPR*Q*5o1Ql^(31(gZU_&&Kx!O zCNqJVLCN(b4{2%Mv5g3^x++!I%*p67+LT-mM17{`3Q?bNGP;a5CD#K{H=L_P-7t>* zZL=Y^dTHVMd&i>q%DetT6u}m5H-rtj)m~qh;^bIA~wT~uwaOc!6UZ46< zay?0H*9^~VQU^+|Ct1bQfAl9M*C&3xt3&_2UsK&_U1nU=nKn>zJ;^W^^`||QTu;*D zx_fFPG321+dXn0%8J@9ZY$>^()J-kQ1;461H7DRwUQ8jgy@Cu4D8{ zHX^3u`99In@WN-XtHF06Uic3-Lc(_Vk&^4$cCByC5AY@O77}?3pHgzYlFu{Sp-V#! z?rlG$BYX-Skvq_llIuE$wC%N6!I!WR5;nu9lw6P64&7ioB;s-?H?@uv+aH|fDRrT4 zlw7aqXtmeOS)C7DF3Eyv@ERyR{Xjo+@xgp?@z5Xf zgG4-?I(G56KI#H8SZ_1?nLo@YO0Fkay?!8AA+AH)KUGr(MRV>^7x-p*c6$@L^PZ`F2Sper~U&wis9 zaww47S})JuN2WPXyJ#mR*DLlKF4%d5y@o5;OSHW@4u-3vPF${zymPra`hv>^8ykC< z%LNZ zt&f^st$A3}t2GZNdZoX8EzhGm^2p#j^2p$WvDbNM@WB|l_+TEpc<3L&ay`lF^#f C1}Pr^ literal 0 HcmV?d00001 diff --git a/testdata/python/dbLayoutTest.py b/testdata/python/dbLayoutTest.py index 5d55fca21..1179f898b 100644 --- a/testdata/python/dbLayoutTest.py +++ b/testdata/python/dbLayoutTest.py @@ -1239,23 +1239,23 @@ class DBLayoutTest(unittest.TestCase): pid1 = pya.Layout.properties_id(ps1) # deprecated, for backward compatibility: - self.assertEqual(ly.properties_array(pid1).__repr__(), "[[1, 'one'], ['key', 17]]") - self.assertEqual(ly.properties_hash(pid1).__repr__(), "{1: 'one', 'key': 17}") + self.assertEqual(ly.properties_array(pid1), [[1, 'one'], ['key', 17]]) + self.assertEqual(ly.properties_hash(pid1), {1: 'one', 'key': 17}) self.assertEqual(pid1, ly.properties_id(ps1)) # static method versions - self.assertEqual(pya.Layout.properties_array(pid1).__repr__(), "[[1, 'one'], ['key', 17]]") - self.assertEqual(pya.Layout.properties_hash(pid1).__repr__(), "{1: 'one', 'key': 17}") + self.assertEqual(pya.Layout.properties_array(pid1), [[1, 'one'], ['key', 17]]) + self.assertEqual(pya.Layout.properties_hash(pid1), {1: 'one', 'key': 17}) self.assertEqual(pya.Layout.property(pid1, 42).__repr__(), "None") self.assertEqual(pya.Layout.property(pid1, 1).__repr__(), "'one'") pid2 = pya.Layout.properties_id(ps2) # deprecated, for backward compatibility: self.assertEqual(pid2, ly.properties_id(ps2)) - self.assertEqual(ly.properties_array(pid2).__repr__(), "[[2, 'two'], ['key', 42]]") - self.assertEqual(ly.properties_hash(pid2).__repr__(), "{2: 'two', 'key': 42}") + self.assertEqual(ly.properties_array(pid2), [[2, 'two'], ['key', 42]]) + self.assertEqual(ly.properties_hash(pid2), {2: 'two', 'key': 42}) # static method versions - self.assertEqual(pya.Layout.properties_array(pid2).__repr__(), "[[2, 'two'], ['key', 42]]") - self.assertEqual(pya.Layout.properties_hash(pid2).__repr__(), "{2: 'two', 'key': 42}") + self.assertEqual(pya.Layout.properties_array(pid2), [[2, 'two'], ['key', 42]]) + self.assertEqual(pya.Layout.properties_hash(pid2), {2: 'two', 'key': 42}) self.assertEqual(pya.Layout.property(pid2, 42).__repr__(), "None") self.assertEqual(pya.Layout.property(pid2, 2).__repr__(), "'two'") From a727ed0b1d284b39bbe832acef403969809a70b6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Mar 2025 20:47:28 +0100 Subject: [PATCH 087/392] Fixed a compiler warning --- src/db/db/dbDeepTexts.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/db/dbDeepTexts.cc b/src/db/db/dbDeepTexts.cc index cabf79bfe..107a3f6ed 100644 --- a/src/db/db/dbDeepTexts.cc +++ b/src/db/db/dbDeepTexts.cc @@ -462,7 +462,7 @@ DeepTexts::apply_filter (const TextFilterBase &filter, bool with_true, bool with for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::Texts); ! si.at_end (); ++si) { db::Text text; si->text (text); - if (filter.selected (text.transformed (*v), si->prop_id ())) { + if (filter.selected (text.transformed (tr), si->prop_id ())) { if (st_true) { st_true->insert (*si); } From 6228668fa12b40f734e11383ecbea3debf5746e4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Mar 2025 21:47:22 +0100 Subject: [PATCH 088/392] Fixing issue #1996: Providing a more robust triangulation --- src/db/db/dbTriangle.cc | 38 ++++++++++------ src/db/db/dbTriangles.cc | 62 ++++++++++----------------- src/db/unit_tests/dbTrianglesTests.cc | 14 +++--- 3 files changed, 57 insertions(+), 57 deletions(-) diff --git a/src/db/db/dbTriangle.cc b/src/db/db/dbTriangle.cc index 13201bffb..867187295 100644 --- a/src/db/db/dbTriangle.cc +++ b/src/db/db/dbTriangle.cc @@ -363,21 +363,33 @@ Triangle::Triangle (TriangleEdge *e1, TriangleEdge *e2, TriangleEdge *e3) } mp_v[2] = mp_e[1]->other (mp_v[1]); - // establish link to edges - for (int i = 0; i < 3; ++i) { - TriangleEdge *e = mp_e[i]; - int side_of = e->side_of (*mp_v[i == 0 ? 2 : i - 1]); - // NOTE: in the degenerated case, the triangle is not attached to an edge! - if (side_of < 0) { - e->set_left (this); - } else if (side_of > 0) { - e->set_right (this); - } + // enforce clockwise orientation + int s = db::vprod_sign (*mp_v[2] - *mp_v[0], *mp_v[1] - *mp_v[0]); + if (s < 0) { + std::swap (mp_v[2], mp_v[1]); + } else if (s == 0) { + // Triangle is not orientable + tl_assert (false); } - // enforce clockwise orientation - if (db::vprod_sign (*mp_v[2] - *mp_v[0], *mp_v[1] - *mp_v[0]) < 0) { - std::swap (mp_v[2], mp_v[1]); + // establish link to edges + for (int i = 0; i < 3; ++i) { + + TriangleEdge *e = mp_e[i]; + + unsigned int i1 = 0; + for ( ; e->v1 () != mp_v[i1] && i1 < 3; ++i1) + ; + unsigned int i2 = 0; + for ( ; e->v2 () != mp_v[i2] && i2 < 3; ++i2) + ; + + if ((i1 + 1) % 3 == i2) { + e->set_right (this); + } else { + e->set_left (this); + } + } } diff --git a/src/db/db/dbTriangles.cc b/src/db/db/dbTriangles.cc index 0d49c53a8..6ebbe6940 100644 --- a/src/db/db/dbTriangles.cc +++ b/src/db/db/dbTriangles.cc @@ -364,12 +364,6 @@ Triangles::insert (db::Vertex *vertex, std::list > *n // the new vertex is outside the domain if (tris.empty ()) { - // @@@ - if (m_is_constrained) { - dump("debug.gds"); // @@@ - find_triangle_for_point (*vertex); - } - // @@@ tl_assert (! m_is_constrained); insert_new_vertex (vertex, new_triangles); return vertex; @@ -397,37 +391,7 @@ Triangles::insert (db::Vertex *vertex, std::list > *n } else if (! on_edges.empty ()) { tl_assert (on_edges.size () == size_t (1)); -// @@@ -auto v1 = on_edges.front()->v1(); -auto v2 = on_edges.front()->v2(); -unsigned int ns1 = 0, ns2 = 0; -for (auto e = v1->begin_edges (); e != v1->end_edges (); ++e) { - if ((*e)->is_segment()) { ++ns1; } -} -for (auto e = v2->begin_edges (); e != v2->end_edges (); ++e) { - if ((*e)->is_segment()) { ++ns2; } -} -std::string vs = vertex->to_string(); -std::string es = on_edges.front()->to_string(); -if (vs == "(-12.9999999999, 3.50126953125)" && es == "((-13, 3.5328125), (-12.9999999998, 3.4697265625))") { - printf("@@@ BANG!\n"); fflush(stdout); -} -// @@@ split_triangles_on_edge (vertex, on_edges.front (), new_triangles); -// @@@ -unsigned int ns1p = 0, ns2p = 0; -for (auto e = v1->begin_edges (); e != v1->end_edges (); ++e) { - if ((*e)->is_segment()) { ++ns1p; } -} -for (auto e = v2->begin_edges (); e != v2->end_edges (); ++e) { - if ((*e)->is_segment()) { ++ns2p; } -} -if (ns1 != ns1p || ns2 != ns2p) { - printf("@@@ on '%s' '%s' - BANG!\n", vs.c_str(), es.c_str()); fflush(stdout); -} -tl_assert(ns1 == ns1p); -tl_assert(ns2 == ns2p); -// @@@ return vertex; } else if (tris.size () == size_t (1)) { @@ -1638,7 +1602,6 @@ Triangles::refine (const TriangulateParameters ¶meters) if (tl::verbosity () >= parameters.base_verbosity + 10) { tl::info << "Iteration " << nloop << " .."; } - printf("@@@ iteration %d ..\n", (int)nloop); fflush(stdout); std::list > to_consider; for (auto t = new_triangles.begin (); t != new_triangles.end (); ++t) { @@ -1667,7 +1630,28 @@ Triangles::refine (const TriangulateParameters ¶meters) auto cr = (*t)->circumcircle(); auto center = cr.first; - if ((*t)->contains (center) >= 0) { + int s = (*t)->contains (center); + if (s >= 0) { + + if (s > 0) { + + double snap = 1e-3; + + // Snap the center to a segment center if "close" to it. + // This avoids generating very skinny triangles that can't be fixed as the + // segment cannot be flipped. This a part of the issue #1996 problem. + for (unsigned int i = 0; i < 3; ++i) { + if ((*t)->edge (i)->is_segment ()) { + auto e = (*t)->edge (i)->edge (); + auto c = e.p1 () + e.d () * 0.5; + if (c.distance (center) < e.length () * 0.5 * snap - db::epsilon) { + center = c; + break; + } + } + } + + } if (tl::verbosity () >= parameters.base_verbosity + 20) { tl::info << "Inserting in-triangle center " << center.to_string () << " of " << (*t)->to_string (true); @@ -1677,7 +1661,7 @@ Triangles::refine (const TriangulateParameters ¶meters) } else { db::Vertex *vstart = 0; - for (int i = 0; i < 3; ++i) { + for (unsigned int i = 0; i < 3; ++i) { db::TriangleEdge *edge = (*t)->edge (i); vstart = (*t)->opposite (edge); if (edge->side_of (*vstart) * edge->side_of (center) < 0) { diff --git a/src/db/unit_tests/dbTrianglesTests.cc b/src/db/unit_tests/dbTrianglesTests.cc index a76f1e292..c3855ce29 100644 --- a/src/db/unit_tests/dbTrianglesTests.cc +++ b/src/db/unit_tests/dbTrianglesTests.cc @@ -700,6 +700,9 @@ TEST(triangulate_basic) EXPECT_GT (tri.num_triangles (), size_t (100)); EXPECT_LT (tri.num_triangles (), size_t (150)); + // for debugging: + // tri.dump ("debug.gds"); + param.min_b = 1.0; param.max_area = 0.1; @@ -914,8 +917,8 @@ TEST(triangulate_problematic) EXPECT_GE (t->b (), param.min_b); } - EXPECT_GT (tri.num_triangles (), size_t (470)); - EXPECT_LT (tri.num_triangles (), size_t (490)); + EXPECT_GT (tri.num_triangles (), size_t (540)); + EXPECT_LT (tri.num_triangles (), size_t (560)); } TEST(triangulate_thin) @@ -978,7 +981,7 @@ TEST(triangulate_issue1996) db::Triangles::TriangulateParameters param; param.min_b = 0.5; - param.max_area = 500.0 * dbu * dbu; + param.max_area = 5000.0 * dbu * dbu; TestableTriangles tri; db::DCplxTrans trans = db::DCplxTrans (dbu) * db::DCplxTrans (db::DTrans (db::DPoint () - poly.box ().center ())); @@ -990,9 +993,10 @@ TEST(triangulate_issue1996) // tri.dump ("debug.gds"); for (auto t = tri.begin (); t != tri.end (); ++t) { + EXPECT_LE (t->area (), param.max_area); EXPECT_GE (t->b (), param.min_b); } - EXPECT_GT (tri.num_triangles (), size_t (13000)); - EXPECT_LT (tri.num_triangles (), size_t (13200)); + EXPECT_GT (tri.num_triangles (), size_t (128000)); + EXPECT_LT (tri.num_triangles (), size_t (132000)); } From 85bb9be5c078414621a02091634c62cae8f569f0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Mar 2025 21:55:44 +0100 Subject: [PATCH 089/392] Fixed Python tests for Python 2 backward compatibility --- testdata/python/dbEdgeNeighborhoodWithNets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testdata/python/dbEdgeNeighborhoodWithNets.py b/testdata/python/dbEdgeNeighborhoodWithNets.py index 6456786e2..c25d8e3c4 100644 --- a/testdata/python/dbEdgeNeighborhoodWithNets.py +++ b/testdata/python/dbEdgeNeighborhoodWithNets.py @@ -42,7 +42,7 @@ class EdgeNeighborhoodWithNetsVisitor(pya.EdgeNeighborhoodVisitor): for (x1, x2), polygons in neighborhood: for inp, poly in polygons.items(): poly_str = "/".join(sorted([ str(p) for p in poly ])) - edge_data.append(f"{x1},{x2} -> {inp}: {poly_str}") + edge_data.append(str(x1) + "," + str(x2) + " -> " + str(inp) + ": " + poly_str) def dump(self): res = "" From 21bfe7a632e9578a835dbec2c1733186f966a18c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Mar 2025 22:36:15 +0100 Subject: [PATCH 090/392] New golden testdata variants --- testdata/drc/drcSimpleTests_au70.gds.3 | Bin 0 -> 72504 bytes testdata/drc/drcSimpleTests_au70d.gds.3 | Bin 0 -> 53058 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 testdata/drc/drcSimpleTests_au70.gds.3 create mode 100644 testdata/drc/drcSimpleTests_au70d.gds.3 diff --git a/testdata/drc/drcSimpleTests_au70.gds.3 b/testdata/drc/drcSimpleTests_au70.gds.3 new file mode 100644 index 0000000000000000000000000000000000000000..ddccef174dd05c1ce43ce5216092f6e963c6ff03 GIT binary patch literal 72504 zcmcItOUz|iRX+7U?*CWQFX+GV5dzgo4H~+P2^8H0CQ`HxG*plQ2^~ggPzMT545Bd( z5L!tLwuy;toDju0G1?}Y7)9ei5qwbsD#ivGm{<@8CblCVxW2vjcg|Yp?zQ(`=f75R z^HuM2*LT)yuf6s@r*2QX>FDU0=~Y*b&b~SwPp_OVrl+Pi>%ULuN4q=U{q!_l9G`ys zTR-~L+dlOh|Nf@${QYnJ_^-VCoztsM8*;ndJHO{0({yq^O}htk-0}=A&x_kfKC$c;ca>xTBnxKb@vWe{w;N3%c?TKAhzfzBEmb{H**pQFzPsru^}>{JrCQS$;;2Uu)n0^difj zzL@1_;W&=r>Zj=^01*;lC2UKG*eCLt1J_&(jU*?RGTLsIG95B_M1({%s8EGWL4FfKTF zZ+siI7ATtQIVX0n=O^Yy_R%V{D&I}j`>yuq{j)z6uagO|D(6x2PL4FJk&3TkDK19O zajbg|tkDmCQPvH?hdw?{FMH>L92ZPJ@#LW=6_0ZZz z%8-M|aY60lJ^6tc&$_H~Z;y+Q^a4VkbLk0)(5qaZ^;+e`t1qLM_zS#geP8Klc%cLA z0>Vz{NaVP-ca#_YLw*5~Z}20LjiRvgbeCAO1sLIlYE^fB0=6FSY#R>--t;e|4Q&%{{{#oyU6pYw-@ody~G1 z7xYH_g1w5d9Xc}h%mpIH1+|XN_&^4621jr$-P3Cz*R@{wd+n^r@WN*UIUMF4%yzVPYBSR1ZO#@cYf-b=*VaBY|;ItL9G^a|DpmkW9YbI|45kRL7=asf61xqcBj zE~s;@$rYRrn(K8Na@Ca|%uiQ-FejVxk=$9;{ykqX7K{y%<2ptyu4o>v`Z=RlG!I)` z(Rtgl5xE@6!+Z+I`s9WDW;YNglb8)e^=5g1aqwMp#rTGfj)QNX2N(z6Cs&L& zJ34}K;0^I0{MF5SG`zWTNMoBK`O3cJV?bgj0dT> zhVdX3*DxNW;u^+-R9wS&kcw*<4^naW7%yFM_ZTl-arYQmSy^d`9Pd_K!^mn2++1-D z<3TE}VLV91{eg`Y_ln!&8pc4y9b^nt+(E`b#T{e}RNO(vK*b$o3{>3ASnF|3tOFHy zkTFnk3uC3nH97_=?jU2J;ugkAk85-U<6y-NFb-DS!dU5X_jF`sWu+x@yj0vl#z4g_ zjFleO=oqNDg|X7(8pc4yEsT{O_XjsrT)nR}@`Lr&l^?98uKZx_bma#tr7J&J7hUvME0mi{P6kr^zOl`&=Ie4rM<3VC=7!ML_!+4Nb8^(jg z+Atm@)}Pj~MqPj}fv98nB?=&oR@8(}r zf!|T@=W+W>BF6vl6YfcWX@79McYmy_xIf~B`_9O5`%5V1{_r#ILx1Dx8T<(3c?d+uuqltDgl+xdID+gT3&f;^wX zmh1ZdpV?49m(}-rWyfn62Q69{YbX7NaWF;!#zC(I7}s?G^$dB;x>l|)jI~oDW3{df z>zSo0gJP_lml(#uxuzH^r$m3Dvqx8JSnI4_im~$i*D&h!i~b{GUYF|uMo2L_iXky^ zTySBm<)hJYu;v69l^*lz0jr^}(tlyBWslKua9vv%yH+C~*%7IgWnOv9^@XukDU6PT zYnNiIoIC9?4yc(+F<;Pw%Lx#s9)3X|DJ9Gi+ zOu`t)V>-f*=vdPHy~r6&ojES3tnTSpDnV~gr5JnJ1IuVF>mrfkf{Jlu&ajHHUnX)~ zP%-v&)bp4t77;q`uUtJHvgXK2(N|LdyL@>53O8tRXZ=4F7>kH ztBQ3m!`FU<7vN7IVqLB)#-7#T^}6fFa5d}tad5ArtnRHd@H}fZYd4YOf{L-1;c*=+ zjDM+n+&E*!YQq?gOs(6qjAOgV#gDaE8^(jIT!t}TA1kY?VO-C`el_bch9lGU<3Tdq zSbdQCXc)saylb^ulgE18E@QYpb{XUI`qut?n8uIc$aLp*yaTALTt>$@W6$dNysq>; zM#qDk&KO3-`qH}(!+4N0DZ?1A6unr(>bc*!JF<7J^teXH_^Qxb3lDN8Wpsoe&%|1w z>g3Zo1;+)KwXk+hVHjaGnYc$Fa=g1&FpS}fTkG}=Bdq1=%pCJ(kK2oNe9zTeUE_@1 zN-_5IM#Z|)hZ{eJSFYEjMc}Uj`4gvd^M}+D8EIt{9j1Yvo3kY z#b;&%{}%`&pDK;av4m1_;6+rJgdDfeKKJBNKOm2K0y!eb1@*HVBd?#Pr}Lol42x@1 zx$eqCh9AsL{o%NvD~}y7z6XRXpSb}!-je4tDy#vJC(rrK{7kD{ckj<o`2Z;NZ z>#qFZ8xyWPpV{&M9)o!aJ%G4>x$epjzF*4S6=52i`AAM{s5^YX9KY>#jm$KbB^@08KxPAA))IZ4Kq&ne?Sq?oAa zsH`Z45t^X;N_aPGQdtocW2Fn*W8_-EwSmZS!2qM4^(n)pdSuMArd(ebYddM9Bc%Dk z9MT_-3kDdGMUVu7%~@FjjJj6odA?d}0*qyKNvjMPoz*A6sOupZafghi9tIc(SEvA^ zuDQ^SYu~Eo1{eodu>hmaaOmi)YXL@`J*#&SKN zBUn(!v!yu~IU_kPxG>hvvrMHJTrmTTsI>E1c`sMo${MN|E9Y58$H7%qF;;e0hH-G6 z4KTv{{BXr3a@#jU&9A_5$e9q-^-IY(ZNFoPBv@jcph+4Vs z%Cj$Kt>8X~)u~)}<=GbkabNa*%+GS&l}|Y$IRWGw0z~z+PRoB*#}B@SjjSu$9~>8S zolS%ozp$6Nj%Jn47uk9#oBk$?C*k>i4{JmSx%!|>z4yS97&XP(YH z*K!@XZv|} zEa=MXdW$^>&&`0W!-B3n@)UY9Uv)j=xS%Vq^;h@H`@LexYro z`CgmF{a=jV9dEJ$Ik=AYEMX7^! z{WkaCP``})y8eR?cCa2+>c5eP|7XA5&HA4aOC6W%uKn;oz7Jg2PmT*}`OoY6xt>2K zFJ%3JYW}EwOy!B7VH}(him|fSGK{c|HDZo+i7m$k6=P)|v&Ts9G`yx5D`$F!ac~`2 z7;7hRhHgE6Aa_vdZrjF=eLG2tYa-QhB2;V z<$S_0YQNQ1k#b#GU70h6aj>oh7*#cBb*hT7az0^n#JnC@`xIl(kAthBV(j^GaD7ya zl}cf(2IE}CxKa_96-zNzs+nOt{JcI$?J+v)8s6yh0#?J111qOu9Er?8FQ^!Mk%`B; zSIq|dC8c9;79M2f+KY8;UibVsxJy%3SGtrv9l1ZAv#9i292ZoKz3f4TQ*w;4XLUTo zd)c#IXPj?J>k{0+ogo(OqVgd zx^@}ET}qd6y`OSLU-S9{_$yvG=y*A>RUJ9uYr7%^uqz2`EF2ia*F z#)DJ}!-#4&_dEC8^KF%nm1<@f53-*zjBy=r?f6(j0f3$7{>U#uJmSx5q^yIJtK^B-+0$O2~?Hh z@UiaIp7_dDSzV1E53>6(jPZHhi%htas-1G}>Bv5Dj`{iCQ@cQ{VRekLXEic>)@>*s zd)X83QYyO-o^N{b z4N&+THUN`t`Q2w%p3f?Pup4?1VSl;q%iqcO;ajqh$Ngzfd6UeQ*Zx!b${Vfuoz8OI zmFJsnw3~Xc4=dMQdA?bO-II>}>aA&49=_7K!#8Ds92a!u@g06}fBoTjOMc0hEK56p zkT2KW`)m90{ly;h-mfdK{SSG*IXLQno&T^M`ern53A^`C{mr%c&9eDTGptBU~4TzB=y zb?kr=zPR*Uke|ox8v~~PK@NRU!n;}ijL32O#NNo`4F%}WH?0Q#g`$ziebJ!+`L9NZ z92eB~|AVgo@HO)udFRaErT46@%!X0VC@|s<8O@9eFb?L}!dTl$8yzvrSRJ67Q#BUG zuGNEeJis`(vIQ8CBXdo-mva?o9;;fhFkZj4f3jozsAJ76%D6SwX&!Oj+;!)Z(y_Ad zH9De-@Pk}*Dx6}hbc%)%Iu6tv#aP*0?J>^P#;-yF#=*WuF;+T7qvK$uQjC?|m0^S* zS)*99h#VJGjJ;UvS%`Vfu5i^&EsWiHJ-EIH7zbCpg|S=Bw2rXa=>ZqUuGN^sm@}Ny zxqjz5&T+wov3AB`Vm;XBER0KGm+5O22cF>nl%y)!TDTdGt4&yI4CgSAiTCbmch{xw>(m z#vCr!U3u;c8Ex1LPjV$H*Iju;0RCe}qfTN~0CHT=m1keiT;V>C*;uY?`G3^$yR7^M z`_28~E7%TH_Rg7EuDklfUdRFAH}?JP(aLpK9(gnHoW+&jkUz$LIzSbARL+{Lg&T{^Yozdwr z^4ed}dtLuV@^9c|+`Yf@(HsYi!SBkqt~~t5dWl|#`#vDY1zmYvzvnny%k|f(-zNXy zFX+kj0{cDa&wXFH?%p5s9P*s+dOhN}peqmk>3?N!>tFcDdsoXZ`aJS8;_KIly}+-E z|9(#)$6upX)a#Wl)G(g>wpjG@G9v|#{z$a@@dY_9xUA8&UdJ-NO*)Xz3*+0Q&VD8{ zX5@Ing|XJ_7)Dro`bf6=^h??589Dxl*0HkVFpS4PEmr=a_)hTrjiT%CEXZ-eMaQny zr$3e%fnePI`hs9A*8_~<$aMYq;6oDY|B&uQ@c1$D=SLUhxS-PU*8UFI_;LF4oIOBf z&y`OCodlv*_xgKDI_aO-cHQq8VBk*nF2|IF2FMvgxb@#E3EvMxvO%DN%Lq2uvKI3Cq8UMYI9MuwlA zWvkErHd{R-$IrCYmD3IrnfR*ETMN&ANanFr2_S0t`7bZXaY2>gm1pusNBHsVvw6;( z|4*JXM2-t8#u2N}|110P>Yrsl5;-myU<_B>+KJ4b)u;cItv|W92;0=on}0R*LAV(DkFTY;5JK{Z^;38gqtO!mJ^3+-6U2&cyqk-kgDs zvHIB45wnna$2=r*+~(uqtC`kOro`@nqv=bfv)?^-G#yRn4;@R@mI?i^AofGp-y}5u zZ;}`f_lsq}?aYt9K0NO!=Qj(F>F4@9>HOeDsR;QTVc{{oLZ64{2W9`Sc=WvQ^T0pY zM;AYB=7ApA|A9wv;o0Dy`5DzU#bcgD_3h$07j(LeKHM*t{qbhIcy3V62{!X^|BZe3 zW$a4lIQMcMioX|qf_{d7VOu#*R6MafEb}2hhriJJ=t|U&JI@!3eM%qQIrn*ZzE;lD z6pvm7`#ihb**E3fcH!ADFL1sCyX5?G;R)6ap1YTG)P*Og56_{JJ}+(82l8ikvt4p7 zxaebkv%T+UtP?nwO(-k=6G!6r`)}+XH|IZn9?UD$!*Z^rc=W2>=YfA_yWHQR@AJSv z>Y1q0NB7Bn9-i}+b3ersvn$2xTsm9WcZqnRZoto_ekgrnbpy4BAK_Cxv3bgK%yO=& zcw+u3^@2Jno|rz!W1NoyCH{&h*8lLF7HItwvkQ9gJth0=8Zmv4H#q-A9tkSDVs#$+ z;5kAY`7rJ~w#amwG*{__@L3 zuY;9&+Tii?Fve5*R6al3>>q#qs`LpByWIXK7%#Uz2YKB36XbF0VUP!TSl`FpQYY*E zdExf+!TE)_&-2~vD`WE_)vt@Z4$3@R^03xFH}VH{1M3&i*5}H4(ct0x4~W}k>qfoK z$NaxYeAKV zF}vWOdcP6larevStgk=c{XQY44|_wP^>b|9@%z=-JjFT)F3eLw6|Y#_VGnpPzbsEo zALMg=e-X=Ptjprx$~jWZF6dL==f?B_5Bfvok=>`n>PF(ZTic&j^!aDM&-B;97!UNB z*DrH!6XS9FsM>nkuq)K}ws`KObwb`ZPlE?> zAMV2&Jbs^0sbdYEP(R<`@#lL?pU}Rbp^uw~;rvO~YV15nn-y1xk=R^&j&^bVZ$6p6y^DEV{ z>be}~IjimqD*MxhU5Hox96Qca?=xfjf;8V>Ea&s4?~UOn8v1~zzVD9dgZvpjw`u6(_E8(+g|!)c_FSTJ4$#mC{;BWZ zWBx(h=Y5=c-W213O~~iccc^@h?RU^;^3y$!YWRm7K+6+b2YtI@JZ_&E%pbS!4f42s zxOqdcoI`f}3?G3{P{lpwAAg?4>Z)HiVm$u7J2p@KbAT9+-?zl38yotB z^TBh-MjrZoLd>pISMT=DGeu#%pZOL2FRygxee|hv(o2*oRIaNTqAR1vc;(qFi8+Q` znvvtr>Nz*Uh>8eZP_0k?U_p)x26R0BiCnLNU|j8ErgA;Nc>4a#2n6G522JI9fN?dK zr`>O5W+1FC*8_~@bSIzkNQ@B`#5|!e?w$Ua-Eu9~X`d}IUG29MBXXcz59kOfSO

K-F1#F?xF3D;J0 z{8Ks~M;MWJV7$7^ek5|-=3|c$5rO1*hW8lbv#`gQ{3jtx_==J}M2?&68DTvAT&dyf z+QV`A|LZNQLXR=t?de@$Egwr)9Oy`UjH(lvyOVa8(qoKwQawhkBf6-x8z+8p?y^*5 zMi^&3pUaHs_|m@o*|`%8Fk-I6RxZ8stHl~~W?n@lAJeKzypaFS*OiVvM$8p3CVX|C zGwm*Agz@;xnNdAwO0|3*H=R_E5q^xVTs=nkX`L>nbw(6sR_5R_FWE`LAYX zAgV~YzL62N=jwZeiMW|D?K0(EP1GDP&M37fy~|+EDSB4x9XoVOr>8{Gaev1?!U!E9 z3EjXrBgZ%Eh*O1 zGjxOHGjcqnW0_@nwh3Ez7It;iwFkO&D@B(P7Kgf&Ax5yKkLcL#q`Hi$LOdI&6hn;A zFI6qkv0Kghj8Ep-wtF&X(CTfCX_v1DX5s2Rjp^(InfdGk`A+3}fbr<J-m5c{!U>kj5xgyVTLHGViPVNt6MI++=<$6;dKEwCWJ*IV_Ajbt=`OC(CL;jik zf&9RF=IXEWkClu#l7F~A{FLyM*?*PQ&%J;0`aA*91HTuJG!Amf%eT+L9##*mpbSzb+S9O$*l@9Ha zjQh`~uIw=~0~?vCbnGz>)(XYA|8~m0j;!*H>aQ4kIwC{(LFPF1Kr!y0vs)bpE1Y87 zKQpw9)DMWc=FC~eIO0dJbXGCN*z@CHT~mxbt97M?)rdP0o+#H9uW!og!!!0`4QmH_2&H3XPi$&Wyi)XLp^kN$FE`7)5!{wN z@mbi@5wVWV!k!=FwP%De7VD9G#GHZkV>P_D7V4e^{#>uu<+{qpUZsf7>xz!Ibl(HU zd4KbK+3ye~C(CuEV~7^xo+{)3)GbxmbYZ_dDvU;&=^Ck%?Q=SMKbvZrUY zUR_}=;!cFsL%o^tW1O+)$9N6zS)C$sCr4)P@;p5DkyYhqe+?5}!V@-MPCb+vGa$K+}Kd=gG l`ES$p19^-4Bz)f8f4cHIe-Jlt5fQ&~J;}@eis$5H`agiG72yB? literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSimpleTests_au70d.gds.3 b/testdata/drc/drcSimpleTests_au70d.gds.3 new file mode 100644 index 0000000000000000000000000000000000000000..8ae2681d6b87be398488c6d0e76b593ef93418c8 GIT binary patch literal 53058 zcmcItU8rr>RbG3aefB;#y~#~`Zv#p&x8Or!u810ww1Q2pzLbR8hd@K0Dnxy0)u&QH zd?;;IkXk{o{yYR4^+jkD1))`Z(Dp{Fi6+|CCfWzFDfl2ZJ}HUgTXTMUj%q*G~?2uhzS74QIn$!?od|;a&RQ!^L|0_+!?m;X zA9?gw9(wGHfAC-L{INg#zNdfZ15XThpVxWY?e@`O*jx<5_V)F#8n$<=hheyJ=b8Mk z`a1up{GVIHKvYwkw+jFAo_`nkmq-3n!?6Csu>FqhlkyYzsoikQ7FogOHqm+;tV$~B zQX}8atX>TK{JaXWuf$ZcaM(VhS8?d4b{`t8RFThXeX1kBw)Z2WZo9F)_t6_S@3X!$dO7OJ zN3u4o2Wtf4E6=<2b}u9zaP-w@3SuVD7riIj_T}6gfY1{^?h$%<-mRDOe*i*HYHg3u z%kxR^viR@2-}C)53x9Iqh#0*EB4YFwh=|c!ARfLi#zb^FnqFeDS=H=c|0|3i#sg03CF9>gD+=|LoOt zedgIF?|z$SIX+^Qk6i>`+)bc^>r*e!SNW^g(sh_8MUUg{>z}`zevWZ1{@{3@fcVPuMQ`3= z|3=&A%#Wfsx6kO6_$Iri>&^3SJ*WTudilJbye0p;k59^{T+e>JJ-_6|f&55%wH?KH z&{;77MQ07T*BpwmYt7CoFBTrj>hOw#def@Q*K^zF+!b1Sf2*r_X}txyg0auJi^S_(VORsvCrvmx1Q7gNw3xg|K3Do(x;i3 z^lUzT>4+LWGl!YP%%mstyyiczukYN)s{D;q5WaxO#Od|(e3kF)u~j}ZpOrB6U!Je> z%jq`R&&pz?5BT5sZ@PX~HZ$+o`kniJ)qZEkuJWDdBF+Ctoj)D+8~&-+@5q01{f_)I z{EqrD{EqxJ{EqxH{EqxF{Eqs0(d3V>|CJ6sKLr{+XFe3Y`3~}uw$B-#qBpnC=sEMn zt>>&izuqJsm44{fbLOX8FBFg34s0Ug%;039OVQcW%9@Yl;M9k^eEI}(?-g|GIeT7U z#m+9`ws5-Q%C6ed`zKw+3sjxaTcGN!-sx3m^cKiKqZf|HybBt=MXJ~+E>gutagi!9 ziVIYU(F;dq-e-(nC@P1aP8huf_TqoGRh;@A&-hqB?m6g(e9xrMs)oPI{SpYCE}GJ`CsYqU+JpzDmHp*&3iu_ z@P}K^8K0sz&u3$w)89$2wzC^WX9QXnepN?g+O?S3Ig|Oj7adW)D!NPveuU*SAJfW{69pFk66~bq+YE< zsTE78K9?-nd@fmb{tA)fF$>q9PrUW#lg`<K_V%SP$#r;!67T@-=*-mY=`LJ)q=rNQIpx?_@y_WfNPCSND0sB=lD z3ip(}$0U_mejZ;FJOgX8hdQm{j+=MbZfi3C$Y)=VuZky@d;;iP70)sG+)`9RxtH1e z1XHI9&j9&M&{Fx=dNpx}&!1-NRN*;olRlGHROZ#xuw{Q*xh4HZo`D_uoOq6OL*-x{ zOuFiE6;$@DCTrEyu%;TNkY^5NZ-+HJ&#u3qJlvsku)4!O&K;b;lPmF_S5wo&<67h{ zQ>|&TOC67Qwd>7o`L}vC1@@+fN}whiD!G62bDqV!^UQ0g1S-3s64)nOD*WW%@&3?K zIaJw)bGM-q=rU22T0J&&LfxvNQ>}tf&IEd1!f999XOiyA8m><0~%!0D=?!Y3DwU8SjljFFQ>`pkOi!J%EHT9r!WBHcHA zLqGi({vur+$g>P_oJsS=Dplo&^d>!TSiPQff%r0&G0CArdYj)*JRt8lh?vRq#l9o! zo%EgI{6CT|5MO!Tt+)Q;!~?>vNv#ZdzUUqC-=t!Pv;R)IKz!wSx8C-*6AuXcCRI4( z`J#8kf0OzfcK?!ef%wYv4ZYQkoPXorzMfr|=MWFex~_|Dj|hI8DC55?+SiC_d|ZS4 z-1$ez8?jt(=HLDG#=nJK-AKFWOQ7?>eIxeoUi1z za9pui!j)h8VA2_p;}5GlpIOATeORsfhMk|zb^-TuN^kDL?|l;@rk=;Hs9z#<+@Dpx z(|=XIGeatT?NjAv`n~vD^B>e5xl}~rkC`s~NybyFe|f%g{hYUB zo*u5)9G@z`TtD&gcjf2|yALV59@fwESH^$S_>VkU2l>aYU;Ll0YdVswE6-Q$FZq-B zrXL7D&pY_Z|CJ7<`48#*DeF(ypLWbV-|9z3&*=}hp3^^Xj$W;!S5}-NP^mz-g(V_M zuac2PZ{E?3eZ_;#3`u&f6&DMgIdjCqP|i3kly&YqXIgW|E@pgKx=hlmbh%bcWDhRy zO(}ZGgO#q;(tD@Y3rA$WYa6{#L=K+IS4OYoV6)4W^)~HWB)-PJ5|8fq8okhZYZ+}6 zOGJ`|9bLxgm5fPx9bLxgl@%ww%0ATYGDfjj*xhA}Uh!bktL)SP3rp5?SMj@a6&DMe zG1hyz;tR*u^(y@RS>y1%$>Fnzy1wQ&bD(|GKx;W1r(j03@AE%8&Dj7-y_)zM3gRlu%Ma?C|>)MF@6K^(q9e3+kc@Tp3da? zqH+q{V?5bgBls6(}F6a&$A zDh8tMR18GhsThd1Q!xHqc)U=*DZ2`D;a6i{?VEuiR(V_VU@&kTy#lY#h+)_97_$ZD5;@-Dub)gOKAZrt2An7pa=e@bFVacS*gw9vRZfyPfr5tS!z8d@UNq^+yH?Cu)66GY6=d0I; zui;ZK=9x{BF9^N*oQaaj`%{dAE$GyV?Tb;_8)$u2tIye3f>+$ ze!b4;9n1ES6nEqw;?Iti=e7UL``euP*RDvT=(H%H=*;o9qIstoJcQ^15pDdN$Z^4d zqBD;Iiq6gyP;~aHfFiQiQT<7=e%3Lu87txX(TWIbvYWBH5jidx*2KLU>}_H;=9rzlbv`TeCSTWyk}~W>M@FoRF6?yqm6R&A9#GWT z3)|R5c;3kK0mYJk$;L`Q2q@|dhmDmE5m40GgN!cu*_%B&)=(_j)EjFZ87P+6C&hSV zoSi25v3^fy-kZrT&8V~M)A~HGyO-fRPhwR*^OWA)9^d$-|&&Y%roX4k>iv6oAHDBH{%EM z&+t3y-|%(L;@{}sMC5OtujX$@{hRA|^gr{SQfK_zX9J_?+>ru`&Khegns;q3Vxv)X z_K}E<&!i5B4uLE}WfPHExpM>*oxL)k=&YWA;_|U}R#>vJerhw(hNh$Igf$(VFs$h~ z9fdX7+1crd92X2}F4BcegmT{A>d`1J(ldie@VKf)%8AfrDo?+h9%DZ^p z)$=Z!w&!_$Iy8Lelbb4EUms_N%G0STpHBh!RA4Jl;84FjKjrxSuF8k4{Kj?lH^&95eC>byd|6L=hLq>4eBOoecN&$atW|z_u9j?f{BFkN zPl;FJ(+=Jx=K1RNwf$IsvBx~;tMYaJ!1uUbLi1Fw%6C3-)BLaKzuhSIHvQM$vyGzm z2v$;wXh6~Fqky6_>H$SnE2f6v0W}v;bXImiQTIC7RIU(s-dl}zBl`n3I{QgLv2>)A z;gt>>P;}N@Qmo%E8BgkUBWJiD@Z^@~16Jy2L$R{61r&81BHE>jOs{Gynzv$emuDt& z7v+`myzX9x&s~<5uxqmi<@qX~-erz(*QM|ByykyRch8RN8$P_q4x!Ih7~{BL{rbh*CK;W(W)`Hnk=Cf{+Aukt(c*Tf&|=jooOeImyN>(_ra#UFRI z$RE@X5&4tvcB_2nU0ju~a~A(b{Sa~eJg@oYeanNqoqP3bvO@~u?b54XFUWDhc1JOa zc$+quKq%tf-$ahb6fb@=DFUH%=_d+8G0z8V#M`RD1VVB5;es5G*@$<3g9(J<*(VBe zJf^sLOnSP!FMjiwMCZBW$AlaoF|g{Zza%PG<#VXOSLD+uf2xqmR)9yxwbT=U|8ljfzrPMSoH3#OIVcXwmw_T9;A+jl2#BI5Ao?!z4K@#gCOWF_8h zkCpF{cp}Ruv~C@oov~w&C^a&^+4?{g z9gi3kuy>F6>ZwZDZJ{K)?+G^X+?mgzCwU3yB3LAUA8>+2Jjv#h?MW8lbG@)=_rg(& z8gHrQxd4?b+1a+Jp~JIIKKpd*@UQkzxu@8Jn4{YAS-jgGo}r+1iaori%4gJWdvHd? zU*(N>?Ucwv0e_!1;`LLNa88DjRBl>Zy^A!mhSzrebTY)= zbp~nZggRqGCv=u+=!80KLkD|m;7rs|LiY*u>V^_*3Y@MQN@KV8cm0MEYzmyy8cMJ! zuzxg^LMOh4PG}!$=)gyTlVd|^+{s>Z?`RFB&>6I$6WSRYI`C28EZb0mk34(qMY@mu zhJJQD?y8@ZJM3GjJEBAGzd8QNyl>Y&e=}1=uN_a;UrcIiBFC#I?n5??JFVC<*>Tgm z&o~~k5lXOeM6qq$gNYmu**NaLV#{O~PH)=cc*w?lCy?IjA-0YCGEr>I^Ocj?AwP~g zve+`&nbTkLaXe&WzRO5=8)DnIR};m?JRh)e+@-~q$!?u?ZH|X*gwp<7Y#aA)Vwnrr zIN7;}^N*!$KmV9KnR*G?G-5z;b2BkFH_azEabKV;5%;$ZrjekXEV zu$g%)qd0ovxJ5~^y2H#h9bO7+GD8}f6V_zZnFU0S3x+kBiOftQ#|4jRnzwf6ssik* zEgW^DxKPw*nhQjIrnx}WXPS&IqfO+vBkD6vMwihha@-Mh^Cod;jC8M<@g4DK^63xy zhsbfkD&Lv4RX*15h<%e!-!m3OjteUOaHe}Le0RrJQ^j3*dKljF#4r#!F4*K>{`bVc z{SOnL$Z^3Y|IRNb{#$<|@rfK4)clRMA9usB8ThuplzsM*f*cpD@^Kf4p96w_@jyY2 zxA?fz#m@o3#~Fdh@d^JnZNJn1=%lM*dt>{kEK&Zw#=mb$>Zo^z&vfa3#)HUl!GpR# zdPJ9TVVsB@7u34i#cT(HTf|L9L5$0vTh zqr?8aU#0G}Ei*0J%ry`>F6h>x{ag=`7S>xN^OfAFu$76OOkD!ITf)56L|nc zUSPdMjyLkdXu&pcfY5-AM2>s4U=Mr*yz+Fii^y^9JH6hPAE5_-0U1{!$9w#U+=E?# z9NfpJWiNaQT_DzfEQh>)fSp(`5bNJQQjp_LKio6TQ^uOHCvv=z14aumVyv-tEst8$8}CmdV{eGJ^}{_jbrsO z(RzoDH#8hMt^IBI@Dcbx);p2olXx3G{DGeXGEaycZ}FWms@jk1qDFwI8RRRGttIS;@#|01C*jhV$inRl;NN!hZ^Puiz?f2=NKXL6w>%<%~S{=2h^U-K^ zBAB))>s^rWSh%dkc}{jUHqC)se$hyP$l7|y5`CG{HJgOrP48J1}4Zouf4Br`VoyUgn%)KVxkt0>UGcT%q#En}TuqW%myl<>K Date: Sat, 22 Mar 2025 23:49:00 +0100 Subject: [PATCH 091/392] Timer::memory_size() implementation for macOS --- src/tl/tl/tlTimer.cc | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/tl/tl/tlTimer.cc b/src/tl/tl/tlTimer.cc index f77efd009..c7cb1b11c 100644 --- a/src/tl/tl/tlTimer.cc +++ b/src/tl/tl/tlTimer.cc @@ -33,6 +33,7 @@ # include # include # include +# include #else # include # include @@ -206,8 +207,19 @@ Timer::memory_size () return mem; -#else +#elif defined(__APPLE__) + pid_t pid = getpid(); + struct proc_taskinfo taskinfo; + if (proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &taskinfo, sizeof(taskinfo)) <= 0) { + perror("proc_pidinfo failed"); + return 0; + } + + return taskinfo.pti_resident_size; + +#elif defined(__linux__) + unsigned long memsize = 0; FILE *procfile = fopen ("/proc/self/stat", "r"); if (procfile != NULL) { @@ -258,9 +270,10 @@ Timer::memory_size () memsize = 0; } } - return size_t (memsize); +#else +# error Unsupported platform #endif } From 73364ee40667499f8f3ee047f96a1ed0e1cbe60a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Mar 2025 00:57:25 +0100 Subject: [PATCH 092/392] Solving issue #2002 by allowing variable widths on the path segments due to 45 degree segment snapping. --- src/db/db/dbPath.cc | 9 +-------- src/db/unit_tests/dbPathTests.cc | 12 ++++++++++++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/db/db/dbPath.cc b/src/db/db/dbPath.cc index 59bcdb451..6136a0ee1 100644 --- a/src/db/db/dbPath.cc +++ b/src/db/db/dbPath.cc @@ -372,14 +372,7 @@ void path::create_shifted_points (C start, C end, C width, bool forward, Iter double l1 = db::vprod (nnd - nd, eed) / dv; double l2 = db::vprod (nd - nnd, ed) / dv; - if ((l1 < -db::epsilon) != (l2 < -db::epsilon)) { - - // No well-formed intersection (reflecting edge) -> - // create a direct connection - *pts++ = *pp + vector (nd); - *pts++ = *pp + vector (nnd); - - } else if (l1 < l1min - db::epsilon || l2 < l2min - db::epsilon) { + if (l1 < l1min - db::epsilon || l2 < l2min - db::epsilon) { // Segments are too short - the won't intersect: In this case we create a loop of three // points which define the area in self-overlapping way but confined to the path within diff --git a/src/db/unit_tests/dbPathTests.cc b/src/db/unit_tests/dbPathTests.cc index 10a8be0f1..4bb44cb5b 100644 --- a/src/db/unit_tests/dbPathTests.cc +++ b/src/db/unit_tests/dbPathTests.cc @@ -362,3 +362,15 @@ TEST(11) EXPECT_EQ (to_string (pts), "(-100,10;1010,10;1010,-10;0,-10;0,10;1010,10;1010,-10;-100,-10)"); } +// issue #2002 +TEST(12) +{ + db::Path path; + db::Path::pointlist_type pts; + + tl::Extractor ("(143,381;262,260;381,141) w=400 bx=0 ex=0 r=false").read (path); + + path.hull (pts, 4); + EXPECT_EQ (to_string (pts), "(286,521;454,350;522,282;240,0;70,170;0,241)"); +} + From 986474d4653174a6f3246055fffcb8e0b0614576 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Mar 2025 08:57:06 +0100 Subject: [PATCH 093/392] Added one more testcase --- src/db/unit_tests/dbPathTests.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/db/unit_tests/dbPathTests.cc b/src/db/unit_tests/dbPathTests.cc index 4bb44cb5b..deb3fe22e 100644 --- a/src/db/unit_tests/dbPathTests.cc +++ b/src/db/unit_tests/dbPathTests.cc @@ -372,5 +372,10 @@ TEST(12) path.hull (pts, 4); EXPECT_EQ (to_string (pts), "(286,521;454,350;522,282;240,0;70,170;0,241)"); + + tl::Extractor ("(143,381;262,260;381,141) w=1000 bx=0 ex=0 r=false").read (path); + + path.hull (pts, 4); + EXPECT_EQ (to_string (pts), "(286,521;454,350;522,282;240,0;70,170;0,241;499,732;564,666;735,495;27,-213;-40,-146;-213,30)"); } From 593678e2288d194776194fe63f81593d77decaf5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Mar 2025 09:55:08 +0100 Subject: [PATCH 094/392] WIP: Polygon#delaunay and variants --- src/db/db/dbTriangles.cc | 40 ++++-- src/db/db/dbTriangles.h | 11 +- src/db/db/gsiDeclDbPolygon.cc | 230 ++++++++++++++++++++++++++++++++++ 3 files changed, 270 insertions(+), 11 deletions(-) diff --git a/src/db/db/dbTriangles.cc b/src/db/db/dbTriangles.cc index e6aff5ee5..9837f5104 100644 --- a/src/db/db/dbTriangles.cc +++ b/src/db/db/dbTriangles.cc @@ -1455,10 +1455,14 @@ Triangles::create_constrained_delaunay (const db::Region ®ion, const CplxTran } void -Triangles::create_constrained_delaunay (const db::Polygon &p, const CplxTrans &trans) +Triangles::create_constrained_delaunay (const db::Polygon &p, const std::vector &vertexes, const CplxTrans &trans) { clear (); + for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { + insert_point (trans * *v); + } + std::vector > edge_contours; make_contours (p, trans, edge_contours); @@ -1466,12 +1470,16 @@ Triangles::create_constrained_delaunay (const db::Polygon &p, const CplxTrans &t } void -Triangles::create_constrained_delaunay (const db::DPolygon &p) +Triangles::create_constrained_delaunay (const db::DPolygon &p, const std::vector &vertexes, const DCplxTrans &trans) { clear (); + for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { + insert_point (trans * *v); + } + std::vector > edge_contours; - make_contours (p, db::DUnitTrans (), edge_contours); + make_contours (p, trans, edge_contours); constrain (edge_contours); } @@ -1529,28 +1537,46 @@ Triangles::triangulate (const db::Region ®ion, const TriangulateParameters &p void Triangles::triangulate (const db::Polygon &poly, const TriangulateParameters ¶meters, double dbu) +{ + triangulate (poly, std::vector (), parameters, dbu); +} + +void +Triangles::triangulate (const db::Polygon &poly, const std::vector &vertexes, const TriangulateParameters ¶meters, double dbu) { tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate"); - create_constrained_delaunay (poly, db::CplxTrans (dbu)); + create_constrained_delaunay (poly, vertexes, db::CplxTrans (dbu)); refine (parameters); } void Triangles::triangulate (const db::Polygon &poly, const TriangulateParameters ¶meters, const db::CplxTrans &trans) +{ + triangulate (poly, std::vector (), parameters, trans); +} + +void +Triangles::triangulate (const db::Polygon &poly, const std::vector &vertexes, const TriangulateParameters ¶meters, const db::CplxTrans &trans) { tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate"); - create_constrained_delaunay (poly, trans); + create_constrained_delaunay (poly, vertexes, trans); refine (parameters); } void -Triangles::triangulate (const db::DPolygon &poly, const TriangulateParameters ¶meters) +Triangles::triangulate (const db::DPolygon &poly, const TriangulateParameters ¶meters, const DCplxTrans &trans) +{ + triangulate (poly, std::vector (), parameters, trans); +} + +void +Triangles::triangulate (const db::DPolygon &poly, const std::vector &vertexes, const TriangulateParameters ¶meters, const DCplxTrans &trans) { tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate"); - create_constrained_delaunay (poly); + create_constrained_delaunay (poly, vertexes, trans); refine (parameters); } diff --git a/src/db/db/dbTriangles.h b/src/db/db/dbTriangles.h index f86f77c1c..f026ce820 100644 --- a/src/db/db/dbTriangles.h +++ b/src/db/db/dbTriangles.h @@ -160,14 +160,17 @@ public: void triangulate (const db::Region ®ion, const TriangulateParameters ¶meters, double dbu = 1.0); // more versions - void triangulate (const db::Polygon &poly, const TriangulateParameters ¶meters, double dbu = 1.0); void triangulate (const db::Region ®ion, const TriangulateParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ()); + void triangulate (const db::Polygon &poly, const TriangulateParameters ¶meters, double dbu = 1.0); + void triangulate (const db::Polygon &poly, const std::vector &vertexes, const TriangulateParameters ¶meters, double dbu = 1.0); void triangulate (const db::Polygon &poly, const TriangulateParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ()); + void triangulate (const db::Polygon &poly, const std::vector &vertexes, const TriangulateParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ()); /** * @brief Triangulates a floating-point polygon */ - void triangulate (const db::DPolygon &poly, const TriangulateParameters ¶meters); + void triangulate (const db::DPolygon &poly, const TriangulateParameters ¶meters, const db::DCplxTrans &trans = db::DCplxTrans ()); + void triangulate (const db::DPolygon &poly, const std::vector &vertexes, const TriangulateParameters ¶meters, const db::DCplxTrans &trans = db::DCplxTrans ()); /** * @brief Statistics: number of flips (fixing) @@ -289,12 +292,12 @@ protected: /** * @brief Creates a constrained Delaunay triangulation from the given Polygon */ - void create_constrained_delaunay (const db::Polygon &poly, const db::CplxTrans &trans = db::CplxTrans ()); + void create_constrained_delaunay (const db::Polygon &poly, const std::vector &vertexes, const db::CplxTrans &trans = db::CplxTrans ()); /** * @brief Creates a constrained Delaunay triangulation from the given DPolygon */ - void create_constrained_delaunay (const db::DPolygon &poly); + void create_constrained_delaunay (const db::DPolygon &poly, const std::vector &vertexes, const DCplxTrans &trans); /** * @brief Returns a value indicating whether the edge is "illegal" (violates the Delaunay criterion) diff --git a/src/db/db/gsiDeclDbPolygon.cc b/src/db/db/gsiDeclDbPolygon.cc index 4ebb782a0..8282198ab 100644 --- a/src/db/db/gsiDeclDbPolygon.cc +++ b/src/db/db/gsiDeclDbPolygon.cc @@ -28,10 +28,124 @@ #include "dbPolygonTools.h" #include "dbPolygonGenerators.h" #include "dbHash.h" +#include "dbTriangles.h" namespace gsi { +template +static db::Region region_from_triangles (const db::Triangles &tri, const T &trans) +{ + db::Region result; + + db::Point pts [3]; + + for (auto t = tri.begin (); t != tri.end (); ++t) { + for (int i = 0; i < 3; ++i) { + pts [i] = trans * *t->vertex (i); + } + db::SimplePolygon poly; + poly.assign_hull (pts + 0, pts + 3); + result.insert (poly); + } + + return result; +} + +template +static std::vector

polygons_from_triangles (const db::Triangles &tri, const T &trans) +{ + std::vector

result; + result.reserve (tri.num_triangles ()); + + typename P::point_type pts [3]; + + for (auto t = tri.begin (); t != tri.end (); ++t) { + for (int i = 0; i < 3; ++i) { + pts [i] = trans * *t->vertex (i); + } + P poly; + poly.assign_hull (pts + 0, pts + 3); + result.push_back (poly); + } + + return result; +} + +template +static db::polygon to_polygon (const db::simple_polygon &sp) +{ + db::polygon p; + p.assign_hull (sp.begin_hull (), sp.end_hull ()); + return p; +} + +template +static db::polygon to_polygon (const db::polygon &p) +{ + return p; +} + +template +static db::Region triangulate_ipolygon (const P *p, double max_area = 0.0, double min_b = 0.0, double dbu = 0.001) +{ + db::Triangles tris; + db::Triangles::TriangulateParameters param; + param.min_b = min_b; + param.max_area = max_area * dbu * dbu; + + db::CplxTrans trans = db::CplxTrans (dbu) * db::ICplxTrans (db::Trans (db::Point () - p->box ().center ())); + + tris.triangulate (to_polygon (*p), param, trans); + + return region_from_triangles (tris, trans.inverted ()); +} + +template +static db::Region triangulate_ipolygon_v (const P *p, const std::vector &vertexes, double max_area = 0.0, double min_b = 0.0, double dbu = 0.001) +{ + db::Triangles tris; + db::Triangles::TriangulateParameters param; + param.min_b = min_b; + param.max_area = max_area * dbu * dbu; + + db::CplxTrans trans = db::CplxTrans (dbu) * db::ICplxTrans (db::Trans (db::Point () - p->box ().center ())); + + tris.triangulate (to_polygon (*p), vertexes, param, trans); + + return region_from_triangles (tris, trans.inverted ()); +} + +template +static std::vector

triangulate_dpolygon (const P *p, double max_area = 0.0, double min_b = 0.0) +{ + db::Triangles tris; + db::Triangles::TriangulateParameters param; + param.min_b = min_b; + param.max_area = max_area; + + db::DCplxTrans trans = db::DCplxTrans (db::DTrans (db::DPoint () - p->box ().center ())); + + tris.triangulate (to_polygon (*p), param, trans); + + return polygons_from_triangles (tris, trans.inverted ()); +} + +template +static std::vector

triangulate_dpolygon_v (const P *p, const std::vector &vertexes, double max_area = 0.0, double min_b = 0.0) +{ + db::Triangles tris; + db::Triangles::TriangulateParameters param; + param.min_b = min_b; + param.max_area = max_area; + + db::DCplxTrans trans = db::DCplxTrans (db::DTrans (db::DPoint () - p->box ().center ())); + + tris.triangulate (to_polygon (*p), vertexes, param, trans); + + return polygons_from_triangles (tris, trans.inverted ()); +} + template static std::vector split_poly (const C *p) { @@ -766,6 +880,37 @@ Class decl_SimplePolygon ("db", "SimplePolygon", "\n" "This method has been introduced in version 0.18.\n" ) + + method_ext ("delaunay", &triangulate_ipolygon, gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), gsi::arg ("dbu", 0.001), + "@brief Performs a Delaunay triangulation of the polygon.\n" + "\n" + "@return A \\Region holding the triangles of the refined, constrained Delaunay triangulation.\n" + "\n" + "Refinement is implemented by Chew's second algorithm. A maximum area can be given. Triangles " + "larger than this area will be split. In addition 'skinny' triangles will be resolved where " + "possible. 'skinny' is defined in terms of shortest edge to circumcircle radius ratio (b). " + "A minimum number for b can be given. A value of 1.0 corresponds to a minimum angle of 30 degree " + "and is usually a good choice. The algorithm is stable up to roughly 1.2 which corresponds to " + "a minimum angle of abouth 37 degree.\n" + "\n" + "The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t.\n" + "\n" + "The area value is given in terms of DBU units. Picking a value of 0.0 for area and min b will " + "make the implementation skip the refinement step. In that case, the results are identical to " + "the standard constrained Delaunay triangulation.\n" + "\n" + "The 'dbu' parameter a numerical scaling parameter. It should be choosen in a way that the polygon dimensions " + "are \"in the order of 1\" (very roughly) after multiplication with the dbu parameter. A value of 0.001 is suitable " + "for polygons with typical dimensions in the order to 1000 DBU. Usually the default value is good enough.\n" + "\n" + "This method has been introduced in version 0.30." + ) + + method_ext ("delaunay", &triangulate_ipolygon_v, gsi::arg ("vertexes"), gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), gsi::arg ("dbu", 0.001), + "@brief Performs a Delaunay triangulation of the polygon.\n" + "\n" + "This variant of the triangulation function accepts an array of additional vertexes for the triangulation.\n" + "\n" + "This method has been introduced in version 0.30." + ) + simple_polygon_defs::methods (), "@brief A simple polygon class\n" "\n" @@ -861,6 +1006,33 @@ Class decl_DSimplePolygon ("db", "DSimplePolygon", "\n" "This method has been introduced in version 0.25.\n" ) + + method_ext ("delaunay", &triangulate_dpolygon, gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), + "@brief Performs a Delaunay triangulation of the polygon.\n" + "\n" + "@return An array of triangular polygons of the refined, constrained Delaunay triangulation.\n" + "\n" + "Refinement is implemented by Chew's second algorithm. A maximum area can be given. Triangles " + "larger than this area will be split. In addition 'skinny' triangles will be resolved where " + "possible. 'skinny' is defined in terms of shortest edge to circumcircle radius ratio (b). " + "A minimum number for b can be given. A value of 1.0 corresponds to a minimum angle of 30 degree " + "and is usually a good choice. The algorithm is stable up to roughly 1.2 which corresponds to " + "a minimum angle of abouth 37 degree.\n" + "\n" + "The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t.\n" + "\n" + "Picking a value of 0.0 for max area and min b will " + "make the implementation skip the refinement step. In that case, the results are identical to " + "the standard constrained Delaunay triangulation.\n" + "\n" + "This method has been introduced in version 0.30." + ) + + method_ext ("delaunay", &triangulate_dpolygon_v, gsi::arg ("vertexes"), gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), + "@brief Performs a Delaunay triangulation of the polygon.\n" + "\n" + "This variant of the triangulation function accepts an array of additional vertexes for the triangulation.\n" + "\n" + "This method has been introduced in version 0.30." + ) + simple_polygon_defs::methods (), "@brief A simple polygon class\n" "\n" @@ -2035,6 +2207,37 @@ Class decl_Polygon ("db", "Polygon", "\n" "This method was introduced in version 0.18.\n" ) + + method_ext ("delaunay", &triangulate_ipolygon, gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), gsi::arg ("dbu", 0.001), + "@brief Performs a Delaunay triangulation of the polygon.\n" + "\n" + "@return A \\Region holding the triangles of the refined, constrained Delaunay triangulation.\n" + "\n" + "Refinement is implemented by Chew's second algorithm. A maximum area can be given. Triangles " + "larger than this area will be split. In addition 'skinny' triangles will be resolved where " + "possible. 'skinny' is defined in terms of shortest edge to circumcircle radius ratio (b). " + "A minimum number for b can be given. A value of 1.0 corresponds to a minimum angle of 30 degree " + "and is usually a good choice. The algorithm is stable up to roughly 1.2 which corresponds to " + "a minimum angle of abouth 37 degree.\n" + "\n" + "The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t.\n" + "\n" + "The area value is given in terms of DBU units. Picking a value of 0.0 for area and min b will " + "make the implementation skip the refinement step. In that case, the results are identical to " + "the standard constrained Delaunay triangulation.\n" + "\n" + "The 'dbu' parameter a numerical scaling parameter. It should be choosen in a way that the polygon dimensions " + "are \"in the order of 1\" (very roughly) after multiplication with the dbu parameter. A value of 0.001 is suitable " + "for polygons with typical dimensions in the order to 1000 DBU. Usually the default value is good enough.\n" + "\n" + "This method has been introduced in version 0.30." + ) + + method_ext ("delaunay", &triangulate_ipolygon_v, gsi::arg ("vertexes"), gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), gsi::arg ("dbu", 0.001), + "@brief Performs a Delaunay triangulation of the polygon.\n" + "\n" + "This variant of the triangulation function accepts an array of additional vertexes for the triangulation.\n" + "\n" + "This method has been introduced in version 0.30." + ) + polygon_defs::methods (), "@brief A polygon class\n" "\n" @@ -2157,6 +2360,33 @@ Class decl_DPolygon ("db", "DPolygon", "\n" "This method has been introduced in version 0.25.\n" ) + + method_ext ("delaunay", &triangulate_dpolygon, gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), + "@brief Performs a Delaunay triangulation of the polygon.\n" + "\n" + "@return An array of triangular polygons of the refined, constrained Delaunay triangulation.\n" + "\n" + "Refinement is implemented by Chew's second algorithm. A maximum area can be given. Triangles " + "larger than this area will be split. In addition 'skinny' triangles will be resolved where " + "possible. 'skinny' is defined in terms of shortest edge to circumcircle radius ratio (b). " + "A minimum number for b can be given. A value of 1.0 corresponds to a minimum angle of 30 degree " + "and is usually a good choice. The algorithm is stable up to roughly 1.2 which corresponds to " + "a minimum angle of abouth 37 degree.\n" + "\n" + "The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t.\n" + "\n" + "Picking a value of 0.0 for max area and min b will " + "make the implementation skip the refinement step. In that case, the results are identical to " + "the standard constrained Delaunay triangulation.\n" + "\n" + "This method has been introduced in version 0.30." + ) + + method_ext ("delaunay", &triangulate_dpolygon_v, gsi::arg ("vertexes"), gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), + "@brief Performs a Delaunay triangulation of the polygon.\n" + "\n" + "This variant of the triangulation function accepts an array of additional vertexes for the triangulation.\n" + "\n" + "This method has been introduced in version 0.30." + ) + polygon_defs::methods (), "@brief A polygon class\n" "\n" From 71644fa56c0a64e8a2ac296c48007163047197a3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Mar 2025 11:34:13 +0100 Subject: [PATCH 095/392] Implementing additional vertexes for triangulation --- src/db/db/dbTriangle.cc | 10 ++-- src/db/db/dbTriangle.h | 6 +-- src/db/db/dbTriangles.cc | 6 +-- src/db/unit_tests/dbTriangleTests.cc | 3 -- src/db/unit_tests/dbTrianglesTests.cc | 76 +++++++++++++++++++++++++++ 5 files changed, 87 insertions(+), 14 deletions(-) diff --git a/src/db/db/dbTriangle.cc b/src/db/db/dbTriangle.cc index 867187295..e9289a324 100644 --- a/src/db/db/dbTriangle.cc +++ b/src/db/db/dbTriangle.cc @@ -32,19 +32,19 @@ namespace db // Vertex implementation Vertex::Vertex () - : DPoint (), m_level (0) + : DPoint (), m_is_precious (false) { // .. nothing yet .. } Vertex::Vertex (const db::DPoint &p) - : DPoint (p), m_level (0) + : DPoint (p), m_is_precious (false) { // .. nothing yet .. } Vertex::Vertex (const Vertex &v) - : DPoint (), m_level (0) + : DPoint (), m_is_precious (false) { operator= (v); } @@ -54,13 +54,13 @@ Vertex &Vertex::operator= (const Vertex &v) if (this != &v) { // NOTE: edges are not copied! db::DPoint::operator= (v); - m_level = v.m_level; + m_is_precious = v.m_is_precious; } return *this; } Vertex::Vertex (db::DCoord x, db::DCoord y) - : DPoint (x, y), m_level (0) + : DPoint (x, y), m_is_precious (false) { // .. nothing yet .. } diff --git a/src/db/db/dbTriangle.h b/src/db/db/dbTriangle.h index 29ff09abb..332d1c695 100644 --- a/src/db/db/dbTriangle.h +++ b/src/db/db/dbTriangle.h @@ -74,8 +74,8 @@ public: bool has_edge (const TriangleEdge *edge) const; - size_t level () const { return m_level; } - void set_level (size_t l) { m_level = l; } + void set_is_precious (bool f) { m_is_precious = f; } + bool is_precious () const { return m_is_precious; } std::string to_string (bool with_id = false) const; @@ -102,7 +102,7 @@ private: } edges_type mp_edges; - size_t m_level; + bool m_is_precious; }; /** diff --git a/src/db/db/dbTriangles.cc b/src/db/db/dbTriangles.cc index 23710b681..e712abaa2 100644 --- a/src/db/db/dbTriangles.cc +++ b/src/db/db/dbTriangles.cc @@ -1484,7 +1484,7 @@ Triangles::create_constrained_delaunay (const db::Polygon &p, const std::vector< clear (); for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { - insert_point (trans * *v); + insert_point (trans * *v)->set_is_precious (true); } std::vector > edge_contours; @@ -1499,7 +1499,7 @@ Triangles::create_constrained_delaunay (const db::DPolygon &p, const std::vector clear (); for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { - insert_point (trans * *v); + insert_point (trans * *v)->set_is_precious (true); } std::vector > edge_contours; @@ -1724,7 +1724,7 @@ Triangles::refine (const TriangulateParameters ¶meters) for (auto e = (*v)->begin_edges (); e != (*v)->end_edges () && ! has_segment; ++e) { has_segment = (*e)->is_segment (); } - if (! has_segment) { + if (! has_segment && ! (*v)->is_precious ()) { to_delete.push_back (*v); } } diff --git a/src/db/unit_tests/dbTriangleTests.cc b/src/db/unit_tests/dbTriangleTests.cc index 439eb1c65..4d0eb9e78 100644 --- a/src/db/unit_tests/dbTriangleTests.cc +++ b/src/db/unit_tests/dbTriangleTests.cc @@ -54,9 +54,6 @@ TEST(Vertex_basic) v = db::Vertex (db::DPoint (2, 3)); EXPECT_EQ (v.to_string (), "(2, 3)"); - - v.set_level (42); - EXPECT_EQ (v.level (), size_t (42)); } static std::string edges_from_vertex (const db::Vertex &v) diff --git a/src/db/unit_tests/dbTrianglesTests.cc b/src/db/unit_tests/dbTrianglesTests.cc index c3855ce29..df7c5e357 100644 --- a/src/db/unit_tests/dbTrianglesTests.cc +++ b/src/db/unit_tests/dbTrianglesTests.cc @@ -1000,3 +1000,79 @@ TEST(triangulate_issue1996) EXPECT_GT (tri.num_triangles (), size_t (128000)); EXPECT_LT (tri.num_triangles (), size_t (132000)); } + +TEST(triangulate_with_vertexes) +{ + db::Point contour[] = { + db::Point (0, 0), + db::Point (0, 100), + db::Point (1000, 100), + db::Point (1000, 0) + }; + + db::Polygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + + double dbu = 0.001; + + db::Triangles::TriangulateParameters param; + param.min_b = 0.0; + param.max_area = 0.0; + + std::vector vertexes; + + TestableTriangles tri; + db::CplxTrans trans = db::DCplxTrans (dbu) * db::CplxTrans (db::Trans (db::Point () - poly.box ().center ())); + tri.triangulate (poly, param, trans); + + EXPECT_EQ (tri.to_string (), "((-0.5, -0.05), (-0.5, 0.05), (0.5, 0.05)), ((0.5, -0.05), (-0.5, -0.05), (0.5, 0.05))"); + + vertexes.clear (); + + // outside vertexes are ignored, but lead to a different triangulation + vertexes.push_back (db::Point (50, 150)); + tri.triangulate (poly, vertexes, param, trans); + + EXPECT_EQ (tri.to_string (), "((-0.5, -0.05), (-0.133333333333, 0.05), (0.5, -0.05)), ((0.5, 0.05), (0.5, -0.05), (-0.133333333333, 0.05)), ((-0.133333333333, 0.05), (-0.5, -0.05), (-0.5, 0.05))"); + + for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { + auto *vp = tri.find_vertex_for_point (trans * *v); + EXPECT_EQ (vp, 0); + } + + vertexes.clear (); + vertexes.push_back (db::Point (50, 50)); + tri.triangulate (poly, vertexes, param, trans); + + EXPECT_EQ (tri.to_string (), "((-0.45, 0), (-0.5, -0.05), (-0.5, 0.05)), ((0.5, 0.05), (-0.45, 0), (-0.5, 0.05)), ((-0.45, 0), (0.5, -0.05), (-0.5, -0.05)), ((-0.45, 0), (0.5, 0.05), (0.5, -0.05))"); + + for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { + auto *vp = tri.find_vertex_for_point (trans * *v); + if (! vp) { + tl::warn << "Vertex not present in output: " << v->to_string (); + EXPECT_EQ (1, 0); + } + } + + // aggressive triangulation + param.min_b = 1.0; + param.max_area = 20 * 20 * dbu * dbu; + + tri.triangulate (poly, vertexes, param, trans); + + EXPECT_GT (tri.num_triangles (), size_t (380)); + EXPECT_LT (tri.num_triangles (), size_t (400)); + + for (auto t = tri.begin (); t != tri.end (); ++t) { + EXPECT_LE (t->area (), param.max_area); + EXPECT_GE (t->b (), param.min_b); + } + + for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { + auto *vp = tri.find_vertex_for_point (trans * *v); + if (! vp) { + tl::warn << "Vertex not present in output: " << v->to_string (); + EXPECT_EQ (1, 0); + } + } +} From cf5e62a4ed34a72fe280274c9b559a305df675dc Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Mar 2025 11:41:26 +0100 Subject: [PATCH 096/392] Added RBA/pya tests for triangulation --- testdata/ruby/dbPolygonTest.rb | 20 ++++++++++++++++++++ testdata/ruby/dbSimplePolygonTest.rb | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/testdata/ruby/dbPolygonTest.rb b/testdata/ruby/dbPolygonTest.rb index 114faf54d..3ac9c74e4 100644 --- a/testdata/ruby/dbPolygonTest.rb +++ b/testdata/ruby/dbPolygonTest.rb @@ -986,6 +986,26 @@ class DBPolygon_TestClass < TestBase end + def test_triangulation + + p = RBA::Polygon::new(RBA::Box::new(0, 0, 1000, 100)) + assert_equal(p.delaunay.to_s, "(0,0;0,100;1000,100);(0,0;1000,100;1000,0)") + + assert_equal(p.delaunay(0.0, 0.5).to_s, "(0,0;0,100;250,0);(250,0;500,100;500,0);(250,0;0,100;500,100);(750,0;1000,100;1000,0);(500,0;500,100;750,0);(750,0;500,100;1000,100)") + assert_equal(p.delaunay(20000, 0.0).to_s, "(0,0;250,50;500,0);(500,0;250,50;500,100);(250,50;0,100;500,100);(0,0;0,100;250,50);(500,0;500,100;750,50);(500,0;750,50;1000,0);(1000,0;750,50;1000,100);(750,50;500,100;1000,100)") + + assert_equal(p.delaunay([ RBA::Point::new(50, 50) ]).to_s, "(0,0;0,100;50,50);(50,50;0,100;1000,100);(0,0;50,50;1000,0);(1000,0;50,50;1000,100)") + + p = RBA::DPolygon::new(RBA::DBox::new(0, 0, 1000, 100)) + assert_equal(p.delaunay.collect(&:to_s).join(";"), "(0,0;0,100;1000,100);(0,0;1000,100;1000,0)") + + assert_equal(p.delaunay(0.0, 0.5).collect(&:to_s).join(";"), "(0,0;0,100;250,0);(250,0;500,100;500,0);(250,0;0,100;500,100);(750,0;1000,100;1000,0);(500,0;500,100;750,0);(750,0;500,100;1000,100)") + assert_equal(p.delaunay(20000, 0.0).collect(&:to_s).join(";"), "(0,0;250,50;500,0);(500,0;250,50;500,100);(250,50;0,100;500,100);(0,0;0,100;250,50);(500,0;500,100;750,50);(500,0;750,50;1000,0);(1000,0;750,50;1000,100);(750,50;500,100;1000,100)") + + assert_equal(p.delaunay([ RBA::DPoint::new(50, 50) ]).collect(&:to_s).join(";"), "(0,0;0,100;50,50);(50,50;0,100;1000,100);(0,0;50,50;1000,0);(1000,0;50,50;1000,100)") + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/dbSimplePolygonTest.rb b/testdata/ruby/dbSimplePolygonTest.rb index e76b15e7c..268d675ca 100644 --- a/testdata/ruby/dbSimplePolygonTest.rb +++ b/testdata/ruby/dbSimplePolygonTest.rb @@ -418,6 +418,26 @@ class DBSimplePolygon_TestClass < TestBase end + def test_triangulation + + p = RBA::SimplePolygon::new(RBA::Box::new(0, 0, 1000, 100)) + assert_equal(p.delaunay.to_s, "(0,0;0,100;1000,100);(0,0;1000,100;1000,0)") + + assert_equal(p.delaunay(0.0, 0.5).to_s, "(0,0;0,100;250,0);(250,0;500,100;500,0);(250,0;0,100;500,100);(750,0;1000,100;1000,0);(500,0;500,100;750,0);(750,0;500,100;1000,100)") + assert_equal(p.delaunay(20000, 0.0).to_s, "(0,0;250,50;500,0);(500,0;250,50;500,100);(250,50;0,100;500,100);(0,0;0,100;250,50);(500,0;500,100;750,50);(500,0;750,50;1000,0);(1000,0;750,50;1000,100);(750,50;500,100;1000,100)") + + assert_equal(p.delaunay([ RBA::Point::new(50, 50) ]).to_s, "(0,0;0,100;50,50);(50,50;0,100;1000,100);(0,0;50,50;1000,0);(1000,0;50,50;1000,100)") + + p = RBA::DSimplePolygon::new(RBA::DBox::new(0, 0, 1000, 100)) + assert_equal(p.delaunay.collect(&:to_s).join(";"), "(0,0;0,100;1000,100);(0,0;1000,100;1000,0)") + + assert_equal(p.delaunay(0.0, 0.5).collect(&:to_s).join(";"), "(0,0;0,100;250,0);(250,0;500,100;500,0);(250,0;0,100;500,100);(750,0;1000,100;1000,0);(500,0;500,100;750,0);(750,0;500,100;1000,100)") + assert_equal(p.delaunay(20000, 0.0).collect(&:to_s).join(";"), "(0,0;250,50;500,0);(500,0;250,50;500,100);(250,50;0,100;500,100);(0,0;0,100;250,50);(500,0;500,100;750,50);(500,0;750,50;1000,0);(1000,0;750,50;1000,100);(750,50;500,100;1000,100)") + + assert_equal(p.delaunay([ RBA::DPoint::new(50, 50) ]).collect(&:to_s).join(";"), "(0,0;0,100;50,50);(50,50;0,100;1000,100);(0,0;50,50;1000,0);(1000,0;50,50;1000,100)") + + end + end load("test_epilogue.rb") From f977973b856fcac2aa452d367cb47629619be57a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Mar 2025 15:23:19 +0100 Subject: [PATCH 097/392] RecursiveShapeIterator#property and RecursiveShapeIterator#properties --- src/db/db/gsiDeclDbRecursiveShapeIterator.cc | 31 ++++++++++++++++++++ src/db/db/gsiDeclDbShape.cc | 4 +-- testdata/ruby/dbRecursiveShapeIterator.rb | 19 ++++++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc index ef7f39a3e..9987cdb6e 100644 --- a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc +++ b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc @@ -217,6 +217,22 @@ static void map_properties (db::RecursiveShapeIterator *c, const std::mapprop_id (); + + const db::PropertiesSet &props = db::properties (id); + return props.value (key); +} + +static tl::Variant get_properties (const db::RecursiveShapeIterator *s) +{ + db::properties_id_type id = s->prop_id (); + + const db::PropertiesSet &props = db::properties (id); + return props.to_dict_var (); +} + Class decl_RecursiveShapeIterator ("db", "RecursiveShapeIterator", gsi::constructor ("new", &new_si1, gsi::arg ("layout"), gsi::arg ("cell"), gsi::arg ("layer"), "@brief Creates a recursive, single-layer shape iterator.\n" @@ -622,6 +638,21 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS "\n" "This attribute has been introduced in version 0.28.4." ) + + gsi::method_ext ("property", &get_property, gsi::arg ("key"), + "@brief Gets the effective user property with the given key\n" + "See \\prop_id for the definition of 'effective user property'.\n\n" + "This method is a convenience method that gets the effective property of the current shape with the given key. " + "If no property with that key exists, it will return nil.\n" + "\n" + "This method has been introduced in version 0.30." + ) + + gsi::method_ext ("properties", &get_properties, + "@brief Gets the effective user properties\n" + "See \\prop_id for the definition of 'effective user properties'.\n\n" + "This method is a convenience method that gets the effective properties of the current shape as a single hash.\n" + "\n" + "This method has been introduced in version 0.30." + ) + gsi::method ("shape", &db::RecursiveShapeIterator::shape, "@brief Gets the current shape\n" "\n" diff --git a/src/db/db/gsiDeclDbShape.cc b/src/db/db/gsiDeclDbShape.cc index b4c5f999e..6c14f1759 100644 --- a/src/db/db/gsiDeclDbShape.cc +++ b/src/db/db/gsiDeclDbShape.cc @@ -1315,8 +1315,8 @@ Class decl_Shape ("db", "Shape", gsi::method_ext ("property", &get_property, gsi::arg ("key"), "@brief Gets the user property with the given key\n" "This method is a convenience method that gets the property with the given key. " - "If no property with that key does not exist, it will return nil. Using that method is more " - "convenient than using the layout object and the properties ID to retrieve the property value. " + "If no property with that key exists, it will return nil. Using that method is more " + "convenient than using the layout object and the properties ID to retrieve the property value.\n" "\n" "This method has been introduced in version 0.22." ) + diff --git a/testdata/ruby/dbRecursiveShapeIterator.rb b/testdata/ruby/dbRecursiveShapeIterator.rb index 7e47576e4..7962ffded 100644 --- a/testdata/ruby/dbRecursiveShapeIterator.rb +++ b/testdata/ruby/dbRecursiveShapeIterator.rb @@ -354,6 +354,25 @@ END end + def test_4 + + ly = RBA::Layout::new + top = ly.create_cell("TOP") + l1 = ly.layer(1, 0) + + shape = top.shapes(l1).insert(RBA::BoxWithProperties::new(RBA::Box::new(1000, 2000), { 1 => 'A', 'X' => 17 })) + + iter = top.begin_shapes_rec(l1) + iter.enable_properties + + assert_equal(iter.at_end, false) + assert_equal(iter.prop_id, shape.prop_id) + assert_equal(iter.property('Y'), nil) + assert_equal(iter.property('X'), 17) + assert_equal(iter.properties, { 1 => 'A', 'X' => 17 }) + + end + end load("test_epilogue.rb") From a22f48d87ad266e5810d6c9212e7edd5628471af Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Mar 2025 15:54:18 +0100 Subject: [PATCH 098/392] Texts#polygons now has an argument to specify a property key that receives the text string --- src/db/db/dbAsIfFlatTexts.cc | 22 ++++++++++++++++++++-- src/db/db/dbAsIfFlatTexts.h | 2 +- src/db/db/dbDeepTexts.cc | 22 ++++++++++++++++++++-- src/db/db/dbDeepTexts.h | 2 +- src/db/db/dbEmptyTexts.cc | 2 +- src/db/db/dbEmptyTexts.h | 2 +- src/db/db/dbTexts.cc | 4 ++-- src/db/db/dbTexts.h | 2 +- src/db/db/dbTextsDelegate.h | 2 +- src/db/db/gsiDeclDbTexts.cc | 12 ++++++++---- src/db/unit_tests/dbTextsTests.cc | 31 +++++++++++++++++++++++++++++++ testdata/ruby/dbTextsTest.rb | 13 +++++++++++++ 12 files changed, 100 insertions(+), 16 deletions(-) diff --git a/src/db/db/dbAsIfFlatTexts.cc b/src/db/db/dbAsIfFlatTexts.cc index 6b7f887e9..a9e8c0e91 100644 --- a/src/db/db/dbAsIfFlatTexts.cc +++ b/src/db/db/dbAsIfFlatTexts.cc @@ -233,14 +233,32 @@ AsIfFlatTexts::processed_to_polygons (const TextToPolygonProcessorBase &filter) } RegionDelegate * -AsIfFlatTexts::polygons (db::Coord e) const +AsIfFlatTexts::polygons (db::Coord e, const tl::Variant &text_prop) const { + db::property_names_id_type key_id = 0; + if (! text_prop.is_nil ()) { + key_id = db::property_names_id (text_prop); + } + + std::map value_ids; + std::unique_ptr output (new FlatRegion ()); for (TextsIterator tp (begin ()); ! tp.at_end (); ++tp) { db::Box box = tp->box (); box.enlarge (db::Vector (e, e)); - output->insert (db::Polygon (box)); + if (key_id == 0) { + output->insert (db::Polygon (box)); + } else { + std::string value (tp->string ()); + auto v = value_ids.find (value); + if (v == value_ids.end ()) { + db::PropertiesSet ps; + ps.insert_by_id (key_id, db::property_values_id (value)); + v = value_ids.insert (std::make_pair (value, db::properties_id (ps))).first; + } + output->insert (db::PolygonWithProperties (db::Polygon (box), v->second)); + } } return output.release (); diff --git a/src/db/db/dbAsIfFlatTexts.h b/src/db/db/dbAsIfFlatTexts.h index 79591cf33..bd255399c 100644 --- a/src/db/db/dbAsIfFlatTexts.h +++ b/src/db/db/dbAsIfFlatTexts.h @@ -71,7 +71,7 @@ public: virtual TextsDelegate *add (const Texts &other) const; - virtual RegionDelegate *polygons (db::Coord e) const; + virtual RegionDelegate *polygons (db::Coord e, const tl::Variant &text_prop) const; virtual EdgesDelegate *edges () const; virtual TextsDelegate *in (const Texts &, bool) const; diff --git a/src/db/db/dbDeepTexts.cc b/src/db/db/dbDeepTexts.cc index 107a3f6ed..e272e25ca 100644 --- a/src/db/db/dbDeepTexts.cc +++ b/src/db/db/dbDeepTexts.cc @@ -528,8 +528,15 @@ DeepTexts::processed_to_polygons (const TextToPolygonProcessorBase &filter) cons return shape_collection_processed_impl (deep_layer (), filter); } -RegionDelegate *DeepTexts::polygons (db::Coord e) const +RegionDelegate *DeepTexts::polygons (db::Coord e, const tl::Variant &text_prop) const { + db::property_names_id_type key_id = 0; + if (! text_prop.is_nil ()) { + key_id = db::property_names_id (text_prop); + } + + std::map value_ids; + db::DeepLayer new_layer = deep_layer ().derived (); db::Layout &layout = const_cast (deep_layer ().layout ()); @@ -539,7 +546,18 @@ RegionDelegate *DeepTexts::polygons (db::Coord e) const db::Box box = s->bbox (); box.enlarge (db::Vector (e, e)); db::Polygon poly (box); - output.insert (db::PolygonRef (poly, layout.shape_repository ())); + if (key_id == 0) { + output.insert (db::PolygonRef (poly, layout.shape_repository ())); + } else { + std::string value (s->text_string ()); + auto v = value_ids.find (value); + if (v == value_ids.end ()) { + db::PropertiesSet ps; + ps.insert_by_id (key_id, db::property_values_id (value)); + v = value_ids.insert (std::make_pair (value, db::properties_id (ps))).first; + } + output.insert (db::PolygonRefWithProperties (db::PolygonRef (poly, layout.shape_repository ()), v->second)); + } } } diff --git a/src/db/db/dbDeepTexts.h b/src/db/db/dbDeepTexts.h index 2f2b37aff..7425df7cd 100644 --- a/src/db/db/dbDeepTexts.h +++ b/src/db/db/dbDeepTexts.h @@ -86,7 +86,7 @@ public: virtual TextsDelegate *add_in_place (const Texts &other); virtual TextsDelegate *add (const Texts &other) const; - virtual RegionDelegate *polygons (db::Coord e) const; + virtual RegionDelegate *polygons (db::Coord e, const tl::Variant &text_prop) const; virtual EdgesDelegate *edges () const; virtual TextsDelegate *in (const Texts &, bool) const; diff --git a/src/db/db/dbEmptyTexts.cc b/src/db/db/dbEmptyTexts.cc index 4e4a80b60..2a22100d5 100644 --- a/src/db/db/dbEmptyTexts.cc +++ b/src/db/db/dbEmptyTexts.cc @@ -49,7 +49,7 @@ EmptyTexts::clone () const } RegionDelegate * -EmptyTexts::polygons (db::Coord) const +EmptyTexts::polygons (db::Coord, const tl::Variant &) const { return new EmptyRegion (); } diff --git a/src/db/db/dbEmptyTexts.h b/src/db/db/dbEmptyTexts.h index 2b7b6cc8e..37cdd6725 100644 --- a/src/db/db/dbEmptyTexts.h +++ b/src/db/db/dbEmptyTexts.h @@ -62,7 +62,7 @@ public: virtual TextsDelegate *processed (const TextProcessorBase &) const { return new EmptyTexts (); } virtual RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &) const; - virtual RegionDelegate *polygons (db::Coord e) const; + virtual RegionDelegate *polygons (db::Coord e, const tl::Variant &text_prop) const; virtual EdgesDelegate *edges () const; virtual TextsDelegate *add_in_place (const Texts &other); diff --git a/src/db/db/dbTexts.cc b/src/db/db/dbTexts.cc index 37af51e35..9e071fb1a 100644 --- a/src/db/db/dbTexts.cc +++ b/src/db/db/dbTexts.cc @@ -177,9 +177,9 @@ Texts::iter () const return *(i ? i : &def_iter); } -void Texts::polygons (Region &output, db::Coord e) const +void Texts::polygons (Region &output, db::Coord e, const tl::Variant &text_prop) const { - output.set_delegate (mp_delegate->polygons (e)); + output.set_delegate (mp_delegate->polygons (e, text_prop)); } void Texts::edges (Edges &output) const diff --git a/src/db/db/dbTexts.h b/src/db/db/dbTexts.h index 87b471804..a948f3366 100644 --- a/src/db/db/dbTexts.h +++ b/src/db/db/dbTexts.h @@ -590,7 +590,7 @@ public: * The given extension is applied in all directions rendering a square of 2*e * width and height. The center of the boxes will be the position of the texts. */ - void polygons (Region &output, db::Coord e = 1) const; + void polygons (Region &output, db::Coord e = 1, const tl::Variant &text_prop = tl::Variant ()) const; /** * @brief Returns individual, dot-like edges diff --git a/src/db/db/dbTextsDelegate.h b/src/db/db/dbTextsDelegate.h index 0376c69cc..4808e9679 100644 --- a/src/db/db/dbTextsDelegate.h +++ b/src/db/db/dbTextsDelegate.h @@ -100,7 +100,7 @@ public: virtual TextsDelegate *processed (const TextProcessorBase &proc) const = 0; virtual RegionDelegate *processed_to_polygons (const TextToPolygonProcessorBase &proc) const = 0; - virtual RegionDelegate *polygons (db::Coord e) const = 0; + virtual RegionDelegate *polygons (db::Coord e, const tl::Variant &text_prop) const = 0; virtual EdgesDelegate *edges () const = 0; virtual TextsDelegate *add_in_place (const Texts &other) = 0; diff --git a/src/db/db/gsiDeclDbTexts.cc b/src/db/db/gsiDeclDbTexts.cc index 099fa65aa..66e1d34a6 100644 --- a/src/db/db/gsiDeclDbTexts.cc +++ b/src/db/db/gsiDeclDbTexts.cc @@ -349,10 +349,10 @@ static db::Texts moved_xy (const db::Texts *r, db::Coord x, db::Coord y) return r->transformed (db::Disp (db::Vector (x, y))); } -static db::Region polygons0 (const db::Texts *e, db::Coord d) +static db::Region polygons0 (const db::Texts *e, db::Coord d, const tl::Variant &text_prop) { db::Region r; - e->polygons (r, d); + e->polygons (r, d, text_prop); return r; } @@ -710,9 +710,13 @@ Class decl_Texts (decl_dbShapeCollection, "db", "Texts", "@brief Returns a region with the enlarged bounding boxes of the texts\n" "This method acts like the other version of \\extents, but allows giving different enlargements for x and y direction.\n" ) + - method_ext ("polygons", &polygons0, gsi::arg ("e", db::Coord (1)), + method_ext ("polygons", &polygons0, gsi::arg ("e", db::Coord (1)), gsi::arg ("text_prop", tl::Variant (), "nil"), "@brief Converts the edge pairs to polygons\n" - "This method creates polygons from the texts. This is equivalent to calling \\extents." + "This method creates polygons from the texts. This is basically equivalent to calling \\extents. " + "In addition, a user property with the key given by 'text_prop' can be attached. The value of that " + "user property will be the text string. If 'text_prop' is nil, no user property is attached.\n" + "\n" + "The 'text_prop' argument has been added in version 0.30." ) + method_ext ("filter", &filter, gsi::arg ("filter"), "@brief Applies a generic filter in place (replacing the texts from the Texts collection)\n" diff --git a/src/db/unit_tests/dbTextsTests.cc b/src/db/unit_tests/dbTextsTests.cc index 5bddc26fc..2fc3e652b 100644 --- a/src/db/unit_tests/dbTextsTests.cc +++ b/src/db/unit_tests/dbTextsTests.cc @@ -276,3 +276,34 @@ TEST(8_add_with_properties) EXPECT_EQ (r.to_string (), "('uvw',r0 -10,20){net=>17};('abc',r0 10,20){net=>17}"); EXPECT_EQ ((ro1 + rf2).to_string (), "('uvw',r0 -10,20){net=>17};('abc',r0 10,20){net=>17}"); } + +TEST(9_polygons) +{ + db::DeepShapeStore dss ("TOP", 0.001); + db::Texts rf; + db::Texts rd (dss); + + rf.insert (db::Text ("ABC", db::Trans (db::Vector (10, 20)))); + rf.insert (db::Text ("XZY", db::Trans (db::Vector (-10, -20)))); + + rd.insert (db::Text ("ABC", db::Trans (db::Vector (10, 20)))); + rd.insert (db::Text ("XZY", db::Trans (db::Vector (-10, -20)))); + + db::Region r; + + rf.polygons (r, 1); + EXPECT_EQ (r.to_string (), "(9,19;9,21;11,21;11,19);(-11,-21;-11,-19;-9,-19;-9,-21)"); + + rf.polygons (r, 2); + EXPECT_EQ (r.to_string (), "(8,18;8,22;12,22;12,18);(-12,-22;-12,-18;-8,-18;-8,-22)"); + + rd.polygons (r, 1); + EXPECT_EQ (r.to_string (), "(9,19;9,21;11,21;11,19);(-11,-21;-11,-19;-9,-19;-9,-21)"); + + rf.polygons (r, 1, tl::Variant (17)); + EXPECT_EQ (r.to_string (), "(9,19;9,21;11,21;11,19){17=>ABC};(-11,-21;-11,-19;-9,-19;-9,-21){17=>XZY}"); + + rd.polygons (r, 1, tl::Variant (17)); + EXPECT_EQ (r.to_string (), "(9,19;9,21;11,21;11,19){17=>ABC};(-11,-21;-11,-19;-9,-19;-9,-21){17=>XZY}"); +} + diff --git a/testdata/ruby/dbTextsTest.rb b/testdata/ruby/dbTextsTest.rb index c4e7351ca..145379068 100644 --- a/testdata/ruby/dbTextsTest.rb +++ b/testdata/ruby/dbTextsTest.rb @@ -529,6 +529,19 @@ class DBTexts_TestClass < TestBase end + # polygons + def test_polygons + + r = RBA::Texts::new + r.insert(RBA::Text::new("abc", RBA::Trans::new(10, 20))) + r.insert(RBA::Text::new("uvw", RBA::Trans::new(-10, -20))) + + assert_equal(r.polygons.to_s, "(9,19;9,21;11,21;11,19);(-11,-21;-11,-19;-9,-19;-9,-21)") + assert_equal(r.polygons(2).to_s, "(8,18;8,22;12,22;12,18);(-12,-22;-12,-18;-8,-18;-8,-22)") + assert_equal(r.polygons(1, 17).to_s, "(9,19;9,21;11,21;11,19){17=>abc};(-11,-21;-11,-19;-9,-19;-9,-21){17=>uvw}") + + end + end From efeb2c061bcae334eaf68536008c81ad79c367e0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Mar 2025 16:51:47 +0100 Subject: [PATCH 099/392] Layout queries support diff as placeholder for the current cell --- src/db/db/dbLayoutQuery.cc | 14 +++++-- src/db/db/dbLayoutQuery.h | 6 +-- src/db/db/gsiDeclDbLayoutQuery.cc | 41 +++++++++++++++++--- src/db/unit_tests/dbLayoutQueryTests.cc | 8 ++++ src/doc/doc/about/custom_queries.xml | 8 ++++ src/lay/lay/laySearchReplaceDialog.cc | 16 ++++---- src/layui/layui/layLayoutStatisticsForm.cc | 2 +- src/tl/tl/tlExpression.cc | 44 +++++++++++++++++----- src/tl/unit_tests/tlExpressionTests.cc | 9 ++++- 9 files changed, 114 insertions(+), 34 deletions(-) diff --git a/src/db/db/dbLayoutQuery.cc b/src/db/db/dbLayoutQuery.cc index c5989b5bb..40835f186 100644 --- a/src/db/db/dbLayoutQuery.cc +++ b/src/db/db/dbLayoutQuery.cc @@ -2282,7 +2282,7 @@ private: // -------------------------------------------------------------------------------- // LayoutQueryIterator implementation -LayoutQueryIterator::LayoutQueryIterator (const LayoutQuery &q, db::Layout *layout, tl::Eval *parent_eval, tl::AbsoluteProgress *progress) +LayoutQueryIterator::LayoutQueryIterator (const LayoutQuery &q, db::Layout *layout, db::Cell *cell, tl::Eval *parent_eval, tl::AbsoluteProgress *progress) : mp_q (const_cast (&q)), mp_layout (layout), m_eval (parent_eval), m_layout_ctx (layout, true /*can modify*/), mp_progress (progress), m_initialized (false) { m_eval.set_ctx_handler (&m_layout_ctx); @@ -2290,13 +2290,16 @@ LayoutQueryIterator::LayoutQueryIterator (const LayoutQuery &q, db::Layout *layo for (unsigned int i = 0; i < mp_q->properties (); ++i) { m_eval.define_function (mp_q->property_name (i), new FilterStateFunction (i, &m_state)); } + if (cell && cell->layout ()) { + m_eval.set_var ("_", cell->layout ()->cell_name (cell->cell_index ())); + } // Avoid update() calls while iterating in modifying mode mp_layout->update (); mp_layout->start_changes (); } -LayoutQueryIterator::LayoutQueryIterator (const LayoutQuery &q, const db::Layout *layout, tl::Eval *parent_eval, tl::AbsoluteProgress *progress) +LayoutQueryIterator::LayoutQueryIterator (const LayoutQuery &q, const db::Layout *layout, const Cell *cell, tl::Eval *parent_eval, tl::AbsoluteProgress *progress) : mp_q (const_cast (&q)), mp_layout (const_cast (layout)), m_eval (parent_eval), m_layout_ctx (layout), mp_progress (progress), m_initialized (false) { // TODO: check whether the query is a modifying one (with .. do, delete) @@ -2306,6 +2309,9 @@ LayoutQueryIterator::LayoutQueryIterator (const LayoutQuery &q, const db::Layout for (unsigned int i = 0; i < mp_q->properties (); ++i) { m_eval.define_function (mp_q->property_name (i), new FilterStateFunction (i, &m_state)); } + if (cell && cell->layout ()) { + m_eval.set_var ("_", cell->layout ()->cell_name (cell->cell_index ())); + } // Avoid update() calls while iterating in modifying mode mp_layout->start_changes (); @@ -2850,9 +2856,9 @@ LayoutQuery::dump () const } void -LayoutQuery::execute (db::Layout &layout, tl::Eval *context) +LayoutQuery::execute (db::Layout &layout, db::Cell *cell, tl::Eval *context) { - LayoutQueryIterator iq (*this, &layout, context); + LayoutQueryIterator iq (*this, &layout, cell, context); while (! iq.at_end ()) { ++iq; } diff --git a/src/db/db/dbLayoutQuery.h b/src/db/db/dbLayoutQuery.h index a7ad97258..d67513e8c 100644 --- a/src/db/db/dbLayoutQuery.h +++ b/src/db/db/dbLayoutQuery.h @@ -536,7 +536,7 @@ public: * * The context provides a way to define variables and functions. */ - void execute (db::Layout &layout, tl::Eval *context = 0); + void execute (db::Layout &layout, db::Cell *cell = 0, tl::Eval *context = 0); /** * @brief A dump method (for debugging) @@ -578,7 +578,7 @@ public: * @param q The query that this iterator walks over * @param layout The layout to which the query is applied */ - LayoutQueryIterator (const LayoutQuery &q, db::Layout *layout, tl::Eval *parent_eval = 0, tl::AbsoluteProgress *progress = 0); + LayoutQueryIterator (const LayoutQuery &q, db::Layout *layout, db::Cell *cell = 0, tl::Eval *parent_eval = 0, tl::AbsoluteProgress *progress = 0); /** * @brief Constructor @@ -586,7 +586,7 @@ public: * @param q The query that this iterator walks over * @param layout The layout to which the query is applied */ - LayoutQueryIterator (const LayoutQuery &q, const db::Layout *layout, tl::Eval *parent_eval = 0, tl::AbsoluteProgress *progress = 0); + LayoutQueryIterator (const LayoutQuery &q, const db::Layout *layout, const db::Cell *cell = 0, tl::Eval *parent_eval = 0, tl::AbsoluteProgress *progress = 0); /** * @brief Destructor diff --git a/src/db/db/gsiDeclDbLayoutQuery.cc b/src/db/db/gsiDeclDbLayoutQuery.cc index cf4f00d7d..d760b0f31 100644 --- a/src/db/db/gsiDeclDbLayoutQuery.cc +++ b/src/db/db/gsiDeclDbLayoutQuery.cc @@ -52,8 +52,8 @@ struct LayoutQueryIteratorWrapper typedef void difference_type; typedef void pointer; - LayoutQueryIteratorWrapper (const db::LayoutQuery &q, const db::Layout *layout, tl::Eval *eval) - : mp_iter (new db::LayoutQueryIterator (q, layout, eval)) + LayoutQueryIteratorWrapper (const db::LayoutQuery &q, const db::Layout *layout, const db::Cell *cell, tl::Eval *eval) + : mp_iter (new db::LayoutQueryIterator (q, layout, cell, eval)) { // .. nothing yet .. } @@ -77,9 +77,14 @@ private: tl::shared_ptr mp_iter; }; -static LayoutQueryIteratorWrapper iterate (const db::LayoutQuery *q, const db::Layout *layout, tl::Eval *eval) +static LayoutQueryIteratorWrapper iterate1 (const db::LayoutQuery *q, const db::Layout *layout, tl::Eval *eval) { - return LayoutQueryIteratorWrapper (*q, layout, eval); + return LayoutQueryIteratorWrapper (*q, layout, 0, eval); +} + +static LayoutQueryIteratorWrapper iterate2 (const db::LayoutQuery *q, const db::Layout *layout, const db::Cell *cell, tl::Eval *eval) +{ + return LayoutQueryIteratorWrapper (*q, layout, cell, eval); } static tl::Variant iter_get (db::LayoutQueryIterator *iter, const std::string &name) @@ -158,6 +163,16 @@ Class decl_LayoutQueryIterator ("db", "LayoutQueryItera "The LayoutQueryIterator class has been introduced in version 0.25." ); +static void execute1 (db::LayoutQuery *q, db::Layout &layout, tl::Eval *context) +{ + q->execute (layout, 0, context); +} + +static void execute2 (db::LayoutQuery *q, db::Layout &layout, db::Cell *cell, tl::Eval *context) +{ + q->execute (layout, cell, context); +} + Class decl_LayoutQuery ("db", "LayoutQuery", gsi::constructor ("new", &new_query, gsi::arg ("query"), "@brief Creates a new query object from the given query string\n" @@ -168,7 +183,7 @@ Class decl_LayoutQuery ("db", "LayoutQuery", "This method allows detection of the properties available. Within the query, all of these " "properties can be obtained from the query iterator using \\LayoutQueryIterator#get.\n" ) + - gsi::method ("execute", &db::LayoutQuery::execute, gsi::arg("layout"), gsi::arg ("context", (tl::Eval *) 0, "nil"), + gsi::method_ext ("execute", &execute1, gsi::arg("layout"), gsi::arg ("context", (tl::Eval *) 0, "nil"), "@brief Executes the query\n" "\n" "This method can be used to execute \"active\" queries such\n" @@ -179,13 +194,27 @@ Class decl_LayoutQuery ("db", "LayoutQuery", "The context argument allows supplying an expression execution context. This context can be used for " "example to supply variables for the execution. It has been added in version 0.26.\n" ) + - gsi::iterator_ext ("each", &iterate, gsi::arg ("layout"), gsi::arg ("context", (tl::Eval *) 0, "nil"), + gsi::method_ext ("execute", &execute2, gsi::arg("layout"), gsi::arg("cell"), gsi::arg ("context", (tl::Eval *) 0, "nil"), + "@brief Executes the query\n" + "\n" + "This version allows specifying a context cell. This cell can be used as a default cell for cell expressions.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + + gsi::iterator_ext ("each", &iterate1, gsi::arg ("layout"), gsi::arg ("context", (tl::Eval *) 0, "nil"), "@brief Executes the query and delivered the results iteratively.\n" "The argument to the block is a \\LayoutQueryIterator object which can be " "asked for specific results.\n" "\n" "The context argument allows supplying an expression execution context. This context can be used for " "example to supply variables for the execution. It has been added in version 0.26.\n" + ) + + gsi::iterator_ext ("each", &iterate2, gsi::arg ("layout"), gsi::arg("cell"), gsi::arg ("context", (tl::Eval *) 0, "nil"), + "@brief Executes the query and delivered the results iteratively.\n" + "\n" + "This version allows specifying a context cell. This cell can be used as a default cell for cell expressions.\n" + "\n" + "This variant has been introduced in version 0.30." ), "@brief A layout query\n" "Layout queries are the backbone of the \"Search & replace\" feature. Layout queries allow retrieval of " diff --git a/src/db/unit_tests/dbLayoutQueryTests.cc b/src/db/unit_tests/dbLayoutQueryTests.cc index 79b059a57..a9ddb1102 100644 --- a/src/db/unit_tests/dbLayoutQueryTests.cc +++ b/src/db/unit_tests/dbLayoutQueryTests.cc @@ -515,6 +515,14 @@ TEST(1) EXPECT_EQ (s, "c1,c4,c5x"); } + { + // $_ is a placeholder for the current cell + db::LayoutQuery q ("$_.*"); + db::LayoutQueryIterator iq (q, &g, &g.cell (g.cell_by_name ("c4").second)); + std::string s = q2s_var (iq, "cell_name"); + EXPECT_EQ (s, "c1,c3"); // child cells of "c4" + } + { // Another way of saying "c2x.*" db::LayoutQuery q ("*.$(cell_name=='c2x'?'*':'')"); diff --git a/src/doc/doc/about/custom_queries.xml b/src/doc/doc/about/custom_queries.xml index 605c16457..62eec312e 100644 --- a/src/doc/doc/about/custom_queries.xml +++ b/src/doc/doc/about/custom_queries.xml @@ -281,6 +281,14 @@ select cell_name of cells TOP.. sorted by cell_name unique cells *.$("A"+cell_name) +

+ The "$_" placeholder is the name of the cell selected in the view as the current cell. + The following selects all child cells of the current cell: +

+ +
+cells $_.*
+

Building queries: instances

diff --git a/src/lay/lay/laySearchReplaceDialog.cc b/src/lay/lay/laySearchReplaceDialog.cc index 2e764a753..351b9aebd 100644 --- a/src/lay/lay/laySearchReplaceDialog.cc +++ b/src/lay/lay/laySearchReplaceDialog.cc @@ -1136,7 +1136,7 @@ BEGIN_PROTECTED progress.set_unit (100000); progress.set_format ("Processing .."); - db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress); + db::LayoutQueryIterator iq (lq, &cv->layout (), cv.cell (), 0, &progress); if (tl::verbosity () >= 10) { tl::log << tl::to_string (QObject::tr ("Running query: ")) << m_last_query; @@ -1198,7 +1198,7 @@ BEGIN_PROTECTED progress.set_unit (100000); progress.set_format ("Processing .."); - db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress); + db::LayoutQueryIterator iq (lq, &cv->layout (), cv.cell (), 0, &progress); if (tl::verbosity () >= 10) { tl::log << tl::to_string (QObject::tr ("Running query: ")) << m_last_query; @@ -1246,7 +1246,7 @@ BEGIN_PROTECTED progress.set_unit (100000); progress.set_format ("Processing .."); - db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress); + db::LayoutQueryIterator iq (lq, &cv->layout (), cv.cell (), 0, &progress); if (tl::verbosity () >= 10) { tl::log << tl::to_string (QObject::tr ("Running query: ")) << m_last_query; @@ -1317,7 +1317,7 @@ BEGIN_PROTECTED progress.set_unit (100000); progress.set_format ("Processing .."); - db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress); + db::LayoutQueryIterator iq (lq, &cv->layout (), cv.cell (), 0, &progress); if (tl::verbosity () >= 10) { tl::log << tl::to_string (QObject::tr ("Running query: ")) << m_last_query; @@ -1371,7 +1371,7 @@ BEGIN_PROTECTED progress.set_unit (100000); progress.set_format ("Processing .."); - db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress); + db::LayoutQueryIterator iq (lq, &cv->layout (), cv.cell (), 0, &progress); if (tl::verbosity () >= 10) { tl::log << tl::to_string (QObject::tr ("Running query: ")) << m_last_query; @@ -1668,7 +1668,7 @@ SearchReplaceDialog::issue_query (const std::string &q, const std::set * progress.set_unit (100000); progress.set_format ("Processing .."); - db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress); + db::LayoutQueryIterator iq (lq, &cv->layout (), cv.cell (), 0, &progress); while (! iq.at_end ()) { ++iq; } @@ -1690,7 +1690,7 @@ SearchReplaceDialog::issue_query (const std::string &q, const std::set * progress.set_format ("Processing .."); size_t n = 0; - for (db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress); ! iq.at_end (); ++n) { + for (db::LayoutQueryIterator iq (lq, &cv->layout (), cv.cell (), 0, &progress); ! iq.at_end (); ++n) { iq.next (selected_items->find (n) == selected_items->end ()); } @@ -1767,7 +1767,7 @@ SearchReplaceDialog::update_results (const std::string &q) progress.set_unit (100000); progress.set_format ("Processing .."); - db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress); + db::LayoutQueryIterator iq (lq, &cv->layout (), cv.cell (), 0, &progress); if (tl::verbosity () >= 10) { tl::log << tl::to_string (QObject::tr ("Running query: ")) << q; diff --git a/src/layui/layui/layLayoutStatisticsForm.cc b/src/layui/layui/layLayoutStatisticsForm.cc index 583634840..6a4ca1d04 100644 --- a/src/layui/layui/layLayoutStatisticsForm.cc +++ b/src/layui/layui/layLayoutStatisticsForm.cc @@ -340,7 +340,7 @@ StatisticsTemplateProcessor::process (const QDomElement &element, tl::Eval &eval db::LayoutQuery q (tl::to_string (element.attribute (template_name_expr, template_value_empty_query))); - db::LayoutQueryIterator qi (q, mp_layout, &eval); + db::LayoutQueryIterator qi (q, mp_layout, 0, &eval); process_child_nodes (begin_node, qi.eval (), writer); diff --git a/src/tl/tl/tlExpression.cc b/src/tl/tl/tlExpression.cc index 679a85aaf..bb42ad65b 100644 --- a/src/tl/tl/tlExpression.cc +++ b/src/tl/tl/tlExpression.cc @@ -3692,10 +3692,41 @@ scan_angle_bracket (tl::Extractor &ex, const char *term, std::string &s) ex.expect (term); } +static bool +get_match_group (tl::Extractor &ex, int &group) +{ + tl::Extractor ex1 = ex; + group = 0; + if (ex1.test ("$") && isdigit (*ex1)) { + ex1.read (group); + ex = ex1; + return true; + } else { + return false; + } +} + +static bool +get_variable_name (tl::Extractor &ex, std::string &name) +{ + tl::Extractor ex1 = ex; + if (ex1.try_read_word (name, "_")) { + ex = ex1; + return true; + } else if (ex1.test ("$")) { + name = "$"; + ex = ex1; + return true; + } else { + return false; + } +} + void Eval::eval_atomic (ExpressionParserContext &ex, std::unique_ptr &n, int am) { double g = 0.0; + int match_group = 0; std::string t; ExpressionParserContext ex1 = ex; @@ -3792,12 +3823,10 @@ Eval::eval_atomic (ExpressionParserContext &ex, std::unique_ptr } - } else if (ex.test ("$")) { + } else if (get_match_group (ex, match_group)) { // match substring - int i = 0; - ex.read (i); - n.reset (new MatchSubstringReferenceNode (ex1, this, i - 1)); + n.reset (new MatchSubstringReferenceNode (ex1, this, match_group - 1)); } else if (ex.test ("{")) { @@ -3934,12 +3963,7 @@ Eval::eval_atomic (ExpressionParserContext &ex, std::unique_ptr n.reset (new ConstantExpressionNode (ex1, tl::Variant (t))); - } else if (ex.try_read_word (t, "_")) { - - ExpressionParserContext ex2 = ex; - - // for a function: collect the parameter or check if it's an assignment - std::vector vv; + } else if (get_variable_name (ex, t)) { const EvalFunction *function = 0; const tl::Variant *value = 0; diff --git a/src/tl/unit_tests/tlExpressionTests.cc b/src/tl/unit_tests/tlExpressionTests.cc index 8009071df..96083dc85 100644 --- a/src/tl/unit_tests/tlExpressionTests.cc +++ b/src/tl/unit_tests/tlExpressionTests.cc @@ -1084,9 +1084,14 @@ TEST(13) tl::Eval e, ee; e.set_var ("L", tl::Variant((long) 89)); ee.set_var ("L", tl::Variant((long) 123)); + ee.set_var ("$", tl::Variant("dollar")); + ee.set_var ("_", tl::Variant("underscore")); - EXPECT_EQ (e.interpolate("A$L B$(L+100)C"), std::string ("A89 B189C")); - EXPECT_EQ (ee.interpolate("123*11=$(L*11)."), std::string ("123*11=1353.")); + EXPECT_EQ (e.interpolate ("A$L B$(L+100)C"), std::string ("A89 B189C")); + EXPECT_EQ (ee.interpolate ("123*11=$(L*11)."), std::string ("123*11=1353.")); + EXPECT_EQ (ee.interpolate ("A$$C"), std::string ("A$C")); + EXPECT_EQ (ee.interpolate ("A$_ C"), std::string ("Aunderscore C")); + EXPECT_EQ (ee.interpolate ("A$($)C"), std::string ("AdollarC")); } // assignment From a3593efa18092cb6b589d7a4836473165373e621 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Mar 2025 17:36:07 +0100 Subject: [PATCH 100/392] Layer settings are now better preserved between calls of boolean, merge and size dialogs --- src/layui/layui/layWidgets.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/layui/layui/layWidgets.cc b/src/layui/layui/layWidgets.cc index 1efdbfa82..d0f1eda6b 100644 --- a/src/layui/layui/layWidgets.cc +++ b/src/layui/layui/layWidgets.cc @@ -781,6 +781,18 @@ LayerSelectionComboBox::set_current_layer (const db::LayerProperties &props) void LayerSelectionComboBox::set_current_layer (int l) { + const db::Layout *layout = mp_private->layout; + if (! layout && mp_private->view) { + const CellView &cv = mp_private->view->cellview (mp_private->cv_index); + if (cv.is_valid ()) { + layout = & cv->layout (); + } + } + + if (l >= 0 && layout && layout->is_valid_layer ((unsigned int) l)) { + mp_private->last_props = layout->get_properties ((unsigned int) l); + } + if (l < 0) { setCurrentIndex (-1); } else { From 740f36a2c265d2042b5dc3303894cef3d86944e2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Mar 2025 18:03:09 +0100 Subject: [PATCH 101/392] Macro editor won't jump back after leaving find mode --- src/lay/lay/layMacroEditorPage.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lay/lay/layMacroEditorPage.cc b/src/lay/lay/layMacroEditorPage.cc index 4f03a58d8..e90c42fff 100644 --- a/src/lay/lay/layMacroEditorPage.cc +++ b/src/lay/lay/layMacroEditorPage.cc @@ -1007,6 +1007,8 @@ void MacroEditorPage::cursor_position_changed () } } + + emit edit_trace (false); } void MacroEditorPage::text_changed () @@ -1116,9 +1118,13 @@ void MacroEditorPage::connect_macro (lym::Macro *macro) void MacroEditorPage::find_reset () { + /* + Editor gets too jumpy if we try to reset after search: + m_ignore_cursor_changed_event = true; mp_text->setTextCursor (m_edit_cursor); m_ignore_cursor_changed_event = false; + */ } bool @@ -1199,7 +1205,6 @@ MacroEditorPage::find_next () m_ignore_cursor_changed_event = true; mp_text->setTextCursor (newc); m_ignore_cursor_changed_event = false; - emit edit_trace (false); return true; } @@ -1242,7 +1247,6 @@ MacroEditorPage::select_match_here () m_ignore_cursor_changed_event = true; mp_text->setTextCursor (newc); m_ignore_cursor_changed_event = false; - emit edit_trace (false); return true; } else { return false; From 8760ac4bcb8f266c05ec56433ac76dfb97982173 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Mar 2025 18:09:40 +0100 Subject: [PATCH 102/392] Updating Changelog. --- Changelog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Changelog b/Changelog index 7d6541701..c4450b33d 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,6 @@ 0.30.0 (2025-03-25): +* Bug: %GITHUB%/issues/1996 More robust triangulation +* Bug: %GITHUB%/issues/2002 Path to polygon conversion issue * Enhancement: Better support for user properties The property management has been overhauled. Properties are not globally scoped. The "Layout" class now provides class methods to convert property IDs @@ -23,6 +25,7 @@ This is a visitor to obtain all "neighboring" polygons for each polygon in a Region. Currently, neighborhood is defined by the bounding box of the primary polygons. +* Enhancement: Layout queries can use "$_" to refer to the current cell now * Enhancement: New methods: - "LayoutToNetlist#build_net", "build_nets" and "build_all_nets" do not - "LayoutToNetlist#layer_index" from a name @@ -30,6 +33,10 @@ - "LayoutToNetlist#original_layout" and "LayoutToNetlist#original_top_cell" - "LayoutToNetlist#polygons_of_net" and "LayoutToNetlist#texts_of_net" - "Device#terminal_ref" + - New "text_prop" argument in Texts#polygons (texts can transfer their + string to the generated polygons as user properties) + - New methods "Polygon#delaunay", "DPolygon#delaunay", "SimplePolygon#delaunay" + and "DSimplePolygon#delaunay" 0.29.12 (2025-03-02): * Bug: %GITHUB%/issues/1976 Crash on cross mode, lw > 1 and oversampling From b493be2dfa0cd9bde0dc862657e421c8adc096cd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Mar 2025 18:12:13 +0100 Subject: [PATCH 103/392] New golden testdata for MSVC2017 --- testdata/drc/drcSimpleTests_au70.gds.4 | Bin 0 -> 72696 bytes testdata/drc/drcSimpleTests_au70d.gds.4 | Bin 0 -> 53058 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 testdata/drc/drcSimpleTests_au70.gds.4 create mode 100644 testdata/drc/drcSimpleTests_au70d.gds.4 diff --git a/testdata/drc/drcSimpleTests_au70.gds.4 b/testdata/drc/drcSimpleTests_au70.gds.4 new file mode 100644 index 0000000000000000000000000000000000000000..d6b609d4f9d01c14503fea228a8e423b2ebeade0 GIT binary patch literal 72696 zcmcItO{`_dQ9keQzB|s?FRs#zV&r=RdscBpYxul&2)TxYkKq1@%dM#lj%*;%KbTn=5JD#TL`u!)< zbpAtM``Z7$3Y?~+PfyeKF(TUop1!^!fA*31y|a%*xy?-?+uQOdACKQV`FNB&eTK;P zw*1kj@zK!zdx1)SUwr|_=+y9R8upj16WV>LP|K+^?--z|UdDD?(nad#y$jD;l z&DU>a`EuP$F`O7hV|sE_4|EF04EHU07`3yPIz~68l|#^Vw7H7sS6{ z#RtER(oM@$r0@SrvFoS1wrWUA?&w*%A-#Uy&|LDy9%D<^P|}xToF1orYV$Cnb$dm2 zE6KLk5U+?UtP&tH4&s3*?ID7jEEgf_B{IMQuMbIuvG4oQ;HT;GzsxAMo6s&eI5xhG zTniM<^_r7;FV`p5Mzql)vI^e~+55J(=jCiq#aoF4%qqrFbEJwiW+NG2`BF@bn&U** z9N44p`=aa{f>)lOrq?|?BijXoO=|Mc6Y}@|MwBPAU9c&Sn1%f7e<8{f*)FK%PqqH= zHgpEQ;g4m4H)mwKU{xM|$M=Ac-##`Y+gtL8bbJp8`FkFjk?jlld$j(8_TM}jYQ^gt zi3I#Cv37m)RLc3uFFU-nKm9>uyI_&`+dI7U3;jf7yP)RPwtwB8b8m8SUYsY9?Th&~ zxi}ZjjmY-K94{~DNEvbv*)FJc*^%cuY^=|0??p`PaM2dD4Uz5IMvFMOcR8;Cz4W|x zpY>Y!&TtLp|ID?Rqv0Cd3o_SY zju*>0!bg}Ze1?6E$acZZwV30BOD@$UKs;kxtzIYfxPfUpzxbRyfey%v6a%Th1Q1><9G z(2L0S#vBcoS1<7+<{N5-mX6?sEGvdGC5$tQ9jC=s>?-dA`3^T3pZzF%3ja z!>&ZO>-Dk7w?;467&1VvF(TU=Tn{h(4VjK!+TKQfP?xGaY>e@Nm_uc)SLIj4o6&zT zUaI3mp6g<8y_oT#KVl4s7^~DB)$w7sff#MdJJ;JI%lH|Xzur%@E{1C`7BpAhOBd_K z@D9d^=B?@oF0L`IIU?HyHP?`inEQYqG;dW$#3Xd-tZB_Pq~qXPDe^9I(G53tW5T8>1KI=#0CnUMpg)$+br= zc-t~o2kfPFG4g|PP?aCdYgKt~>@4!k&RjhHZgF}2-QvO=J^5s@*X_&^T<{zBB_i7e zbIxmVdGpDF>(ON#4$M)KhesC^T`4#KH=sy_8)$s@OUR56Y55#^| zelXsf@&h?d%fEfk_%ZS;;>XCZh#w=r!v98oh5wBF3jZ1T74c`}SF9f+5B;6-^Nu~^ z$H=dUA0xjaevJHz_%ZS;;>XCZh#w=rB7Thgiuf_|E8^#|J>$p7uZSNbzaoB&{EGN7 z@+;!U$ghYWBflbkjQooDG4dXCZh#w=rB7Thg ziuf_|E8^#aI(`PtmF-N88c9{S99?d{Rox_UOgK7BF1*$u=EGgbppy;<&H9DLVYF}|at z!>Wpg`50Y^W<3Td+_pi*jN7WkFFgh~sAfqGW4l+72?jWNh;|?-9GVUOw zBjZNKQjKeT?Z~);jE;<(7z;J7(b17{2N@k1H!&7!T%#ix2Q#jNaWLa1#zKv|qa!mb zGcA$rDdP?@Ix=ozEY!G0M@Pm@jD;H4Fgh}BVl33SKd>U>>T{)$AIz^+`N3>jl^@KV zRr$e8S(P8mi&go-ELfEv%z0J$!3M9S7rJ9&#`aW~Mgdj~v|BhVdZ2 zHjD@HwP8GnuMOitd~FyH;_FwmuaTE!c+eaB9FckmR`u8Rf*kf~ zBIHwlRURt?^JIO1pCJciyI@rw`r>=spYd!5@_fdV`m6GroAEF1Y;Hy!U=QfRr%%{y)Cokk zUvH0(TXc+r`U-qRwhK1L$89|1@O_M*k?mLO@?qT9;`pe~z(d4etH~pBYOxFotn( z)z6Gw9S7H;gE6g~Vq~k8H8Wl-+?SYHgAuU-Y~(7%Sh(Hza~(Z8Tfrlh-BU3ZqQ@}m z^^@aRbPjPn$FpMW>9~xZN`_O6h5Hhtqo1+x)Y~u)?t-(9rFb-q*aM&^Q04K^#&*G( zv9x9kqg%&PrZ9{tH->o3IV|~D~2kSefV=ux7D=x*@i^t_Egr~3? zf$)9mSByO!u?lHFR-;6=3o6E*XZ*A7t(mZ6bBDfD-MN*Hg{M87 zK;?8ne{CUZ`=6ui=?<(Fv$d={L3tDT^o_d$aaOcrTmzXFbB`i^ma0Up$U5`gVmI zyT6lQeC^K^BaFP^$htOfJ_?NTcSPs^3kj-b#3b)|P*0+gVqKZ~^|KQr&-*nXFr{|kk&A8(j|$Sec@7Y-wjIr4*fBcBl2-kU$x5%fpyg#H=XZvQVBW_-vY69S<> zaUIu8GaCZ`ong?syu9s?*Spp zXZAq0x8(VZ9_vlV{`Rr>>^k*V$KTwH;{YK~9w5d~{Z;wFH)g8xd}o0F_aLl4=mEs| zslO^e_w+49E^i|jbbd+Oh!kw!{l8Do?%s+`W0iLnl+4=^}v3l7z=009Y*d% z+?j}M7j!ZX>{*JjP=y&CbruTIT;9cI##^!UH2aca1Peb{FZ#oFK?mcU*&4(^5yMxCFbBlp=wes(Yp?wAfny$3)??!}8ez`;1Ut2!8UghR*5KI>rA z(SwMd;2xyGj2II z)`_%c>aWTN-^rc}#P~dSaYs%4Rr#<_-c9GFi*#N}$dj#LRh}_TOR~7{;>v(3nU{zlG3%fYa%|OmWuqwaQf4P2o z`ltBL@ef%4J;RUIGxA9#DCDL-%%S(S(W1MkdK<)MF`_njEO;6wc8_N#UO z>zKjM+Mlq0>eu6cUgsa^k9-4E>m}d&r2eY>iu`NFUy=WeJoICp%J;w9w2!=&_7$7I zjJ%H5!Te?9b-sf9GJh2IUn8&mHMsta{EGZ(p zg%$a8uINk8yba@EMp2A~8#u$L>{#66Q@>&?+?N@~L95SAQG+SQUW6~#^|C5V{fe=t<6xbp7<=p5KkHtE57q)o$HMajQ&e{B{>+OW8SCboQ3l$ z)^otLZ%e;}5jqZ3q7Fv?n(4)(-!mhOK0A&uW*^^<9>ED8p2_Xz&AcAtV2!Rk)60*3 zU*Fh$x5`8~Jd;=7ojBXVJ&Mw?$LL?zJw|uk)2#s9dtsN+oge#*hj^#l_}U$hT^$c{ zR`}b4SN4Vx5uVpC*E4aOo*N;&EYuW+(Oub>B78r_(pkYU!pE%Ob6lo4WP7)=H;f0l`7n&`{MenfziRHS z89!q$di;A~p#m^=JV+gG7~T6!cU}AU>q6CRbo5tyJx2eU=`lh_U;Wr)gpPaH;d|P# z7mt`V_m{>~U$xJSF#7B`!kF`-?1`cNCKmVW9;3VNxz@`)emnN|Yk&0I*sbh$qG#{Q z-Z1(zMNdcfKGWT=-M;QJx-)K{@eq~0@r=9L>*{!r%Kq@9H4Y znO5atw}CgitMbs1ALxzxkI43x{@6z_J`j7~z;6f{|3QE3oj|_#Ok}%YRe#7K9|yb> z;|BEt;wQJ;H&)E}o14+cK-iyt0K)gFzdAm?!w=;3hwXw@dDt4?145Q>N&(s4lINR- ze5)4m2Oc2gQ-5`QzL`il+JWcN)L)h7n}vuQj7vTEbTai<<>!16<;eqt{;9tzuk#Pa z$M++~cUXJ*N3TC+Z}EqDPq#Y0wjbnEya<}Na;x&%e#&2BNAnJ@mj8R5{|ED*syz$O zb_^p{(!h?O7zim`B#HjIONje~J8`^=18J0f}p_M@4x^yY+_wc3U9&IG-YAyl7YESxh84o0jA ze!!?dY!`Ge&iOd9RrYrWqt5uy4fD%6d9A}+B$ww@7G~>y&daSWCql!M4Y=)zuxbSJnI5# z!a9$0PyJPS#6Pko5EkdTiFHKkugVACi#!nWJoj*KO8r&&us__5^UOC~7ts#t{e-GK z>ni#d`oc4erqo}RXNABO!a9>{F!gKsf6)GezXtBns`8jWpGRb#ZJa;b1&8DrH^dQn z#0&fng#UPN!1^ckSI5Wq;A4JY#}nHHtMWSDu|x9Q2;@8jtMW_zm+{-vAF%>Gfv^L9 zCbGSyzy3b#2U#L{1grW(UvO}}@t%U`zSLipXVn8Nt-sV?mB%{8_*}2ro@^Ja%Io!; z??JCN*YD2y8F-thIzHkj=Z)85{2=ZTKe^rB7n$*)|G@Kv=J%= zXn(@~slO_}BLAEDugIT9entM2_bKPU=yUuB5`RA@^AtS(L-EBYW@Niydq*{lXFnF% zfZN9&?5QHvED_$-88RpN-KIk?jFz#_p`$ zcI@hS`de|vK+N>)qcg%1so$aF>CMz=>H6BAAA7TQuflFNyYGrb z%0J4fK=AyM*z=P!vR!bF$I_er#*Sb*`j5zX@^EA%vRzOy7VaDkBYb@HX!P~be@0(t zWc#BYM(B9*uTjUd2cwQewhKCR^xJV{)@KjIS)Y9=&N?I8AMof1JD#6M#`C|4j2YQ} zt{Dq=WyUl99A3B~G>jn*KNmf-{h#O=;(2NpRG#VSh*@v{E6)1TpT=1e*)Hf{RJ&8i z;oVBATLJhZyqC@VvmRme#p4L0FCIr2eY?Vq-MibJcw|Okh9I)tW{MF;=0WB~BHL|# z)Qpm%H}@V-UrLqz=HcV%c-me$ksVq>`+h;5b8x?vbB zIA7tuVjq2Y-;wlMtq;%d>HMd7bZ+X;3;N(5AnB!ebame6;k^iKw2yswzme`UW__G~ zhaQ+0P-1E3G4)P=Uc48CePoU^&kB7I)9K!5=5gx7`xo51>|}-h5Nv8PbhonqsJ20 zm#=RgG0&O%JV`$C?4b{M&TmJ3_VE{T4?lK&`SyztH`J&4yDww*QBSy)oqchb_CL-| z@%ZM2e8Kn7Y&`q&26*rs0x0uS`uKSe&)|`G+=~a#iNGUqw3-KTsNRcHJifXP@soK> ze)jPMeI(BZzN>m(1)eY0>&$BWl=k-qPd(2U@^gcyS|2*~L4AqZ2YjYZDAm0UebWBN z=d{Z2h4t0o;qxk>%|AZ-q~j zA8^1%=`-_`>db~d)%wS&58pFL&z)v{oIHHqo}M4A=D~VLJ(kw}%;U6AJs%eGe8bLI zU+@*|Ex1~r`Z?DZht>0!GjG)EsKWl^k+& z3-2jme)hcM%Nvj{*Ux^QeBLqV6hBY-oZ{nwzsl$L8|scToi7^e9rh`o2YozowJ^K$ak z>y|?PY3Sps=Nmk*5AuAnlgiJ&_;H;J8v0c0UT6HIeQWG|(a@(_M>+Lzol_e6U|!{Y zxlkWAc#y~V5kBR2ACK!^y`him+|=Ny`n_~6Xz*0y(8*Ih7dYd`b&hMy%k{jV!Q(m) zHh5gm9U45Yb7h04o>zVAqMqk{JoP%NP>(g{g*qzV*O_}ZA5S$754A3wJoUQ8XXkny z<>RU5d1swa-FKWk@O$}uS~xc~;s^C}K3|x7Y(G!A4lkT@8~TKJF5Vmad9pse=T;4U zuug~XB^o^S^Nw#`@K;{%=6>GKQ}(-$ryhrXo~(~K=lXcyukv#lA5WOq?ZM}I4ZlO5 z;d`3~4{JK0t?PW}i+Y{u<3Swe`vmjc%9l4_=iz&#hJCX`R-UFDo z^wK>^g9rYG&%h_B>Up2P>UqppC&13-yz1kr+u6rc_q&e=`jqpAj|cjc^O%nZ`jqpA zj|ckXb!y%x@$=+;$JCiOcJKWf@r-#5-yb%3>ieKipWv^Hc#kdqSL@5qgLwtL_OK7+ z^ZKgZXKv*|q`aT`&-yp7j-HDre;{uXh#`x_el@goaLs?Z$@SyJd^tW zUm5l7X=%;)XWe7evxcw3jV`e`>k&ry8qDx97&Efn_`1h<{=LxyK=^ub`#hz7<(VGi z;@)}M{z_y9!q=&PB_r$@PV~gpj3L{~J2S8!7&D3;!&%>D$B_NM8g~)33&f|9Z2zjx zX8Kf9ipR7c#eL~*@jP=vEbdo5#s_{O_V+uMV?gRgWV@-FdUk{_Rh=Dtjd~fr&h0KnWCN_ifU*kH zNhu=R^(r(s^d--LDSsqGgtHuR?wjq{Wek~CbQ#XLLyXWd><*$^cUR~#x+|$6M(8+P z?R6PbMva+uB;yV-LdUS5h#fCU4$r#-+jT|SWn4Tto6bKREpYx}`F-YB;i-V0=y@Va zZk~uti)U)nCiXiR-PK-q*6xb5%eZ)=HJyDd&i?FU(Hg1WGwY|KG>v>Eu(9`Q%e^q}1@(ucNLr&&2QqA z@*{QwNCp%poT1g-Kx}t0`YQl^dr{NTSEckAp`*GP4EDo~Bkag_L8l!d2|M!clE`)+ zqknhp%{uJ--05G7zE<~Ju`)3Gb&nA`s++A~zw_JCj=9}bDLqE$2+4rLw7Bz%?QTY2 zgx^rNV5NA(tn+HSlU5(GxC(oWp|bz7ry?C;^i|CxjF@%Mm8T9O+fAkzVLbY3WZXG@ zu-%-HMi>!2p*m)@Ok}%>9{of?zOZu>2d1;nN5;HXxOUiXD*GOzKf-&A{#Dpx4EB=> zD&TSzvi)V9A4eFG2f=u06W2A7?KVI57-2_90-0%vZ1*w3j*tX`3A+K2?LJ2TZJ~Y= zREkGko8fk*f1}xB%%`K5WcI>{ikYbT)Lz(Q%qO_#A`|XWzz76m>US_IJ%WDSQ=EA+ zSF)qhBk0vVX%-lzCW(J7?OX9z6p$T|#ej$wRQ5zve)l{gvrowH2`Ve>-lOFap1}zB zey8~RnS?J&UD`k8sS_#nrYw>%6$}bHoU58&V z0vPEJFkzhn*)FIUcdMXmmCT5lgYll9jZ8$g3pyBKJAR-O>`Y|4wd?LGKl;W4#Jl=y ziIz{Szb4kId@CcX%C|C(N_>Z0%_aU2L&~e!(*=)L*NnSWD%RbI~@H%eH|#LE1Q{>ylV4*Wo`)L$JR^TVzga4~*v zf6Un5jIZa9j0zbd?IKtmAK&2za?qW~_Lh8WKZJbhua4hZzmQM;Re3#r&|T(F92tM< zzh(UN{HOKDxQH3zi2f$u)c2|C{1G=8FW^Exx8JJ!Kdf8XCBiy*A;!P1CQ^TM{9JXs z80Ali!VA5mzbX%Y=o!YLTtc=BHsulXkZ0Z^vR$w$4lpkjYlB#ho}P}A{DUO^ zm3)nM>7RAH6))Dznlm~k2|!>3D?c2`Fq+>)*os^4J+{8qs}M}^FA4D9VXL>rSl@``B*r78}^(h@k)(kyBj}X}|s2F>@LRu4! zHM4uaZL=8|F~fk1nkcuM2=6h%$39;d7;osh2fD#t0Y4MhHFwwC-m{}&)HMb2BUp&= z^-xXmPOYPgkX-Myb9kXPGmLt71rxFc5u**&W`@zfEA({Kc7)GY)n-OV_!u({xLDV@ z{bpy)7)Hb+B!M| znlX$?2SCsfdh$cvjgFLQIV1fmRVBFQQuooIetUlHbV>Lz3j_?d- z8t|o+^`p@dOz=j)7gpAfh7q%d?LrP(FtS}xd8Rk(z$7{X$FfIKbVcH@+$iBzEId7s`7)oYE@qA5BtFuM75h#<@NZGL+leFpZcrvll;T? z@DFlJ|Dj#_hvmV+4|!Yi@cTeMtd2jpYOC@XA8RqhVSN{>%7?Wr@|`tal@D>>-(i~a zuy>e8J^NSX;Uj*)16vT;zVP3-Eb|9q4)b5G-`sBR{f)fNA6R#gA!7X0AM}_11B+SpkQUYv{5)ep835W$Gb_jUD14)PjSs-vE7KjWhP|W$d>gzhEyHEA0 ze$A8Kd^4x+`Re@Dsp`J>_H>7Cx4YFnaI!moy*ur`r91B)>7LMk@6LCJr{8(A>#m)i z{m_%Y{I;h)`}_a-_8g(_*D(dp}!^2Nrzxjxbo#D$_PcahT z;17HR;wO)X`Hr`f9YK8zt#l}U(lZ#-;3Hl z?5O>ZAi662B~mV^{WJO}chk9maY9$d&+apn_vq^H^KJ#iyzq|*o;>dAm-&(&>J9p= zyFBjdV^_itcPHcv`asGBU45R9(yQgqA)d+qJnrhBy_#OfycT;XugCB7we&jro8u*M zqr9$vyq#W0f6t#!p76Yv$F==0)#_&){~V7}kJD3);Vb@^Jj~>y@J&1H=WF&k^1Mg1^o7E${{Fz1X+lZ?OIg--3N(f3Yrf{pIn( zw_smzeTDf-e@S*#>)G4kO+`0VOo%bKCnTRe#=l;@ibISW^cid6#ruNc+n}xu-}X7# zq;8CjO|?CLg>3kmEDZ3Cd&6R{p7t7J-v+(G@HN{v?Old%gI@gwvxlC3)P{=NXwj8}*6# zS*Lki>;KJ)_?5k6T7QNw7|$?YFusLvT7SmAV7$Y8!T2XWZx{Jnlab-HpEAkHWMp$? z>x>$GRt_tPmB~ovajpMyZGKi3J)N1~>c@61cYanbD|zgH9(VQkvV!pg#7f}GV&?O> zs~_x8u6}Trb@j*gA9H5`(l5BD?th#32X_EhKe(%F{lC9p{TTff^=tH3Ts<5874>KI z$N3A?pV41Ye@1^r{Tclg^<(r`)Xyum`WdhP+}#(&s&6|d(DS-3&pvKC&v<XOT|4_+tqw27H(hsz zuc->>uhb3S27SixZO~^7-v)ib@HNFRRMDHP;(yeunAariS+IHG>euL}6LtC%dgz5j zHKxzHMxWPYJfmPuK%Z+PkGuNV3-AMkUf}M5nV;7*Tzlvl;9>8`<9hxt>iDJE)1UO4 zsK&|~o~}8=7mP=kFBqS~H{CmpeZhEz`GWPA_`EgpC6kf%tAC@<%(D_$S&U>J*A?;Q z+WcHu^mOLx-|EM98F&7@tRVaVQHiWIb{Ls~_y@uKw8nW9|$<#s%R~ zSN|sd!Ck=B5B6xS|Ej5<&%G``V&Uf`cOR7Z{{Ndud5`rKVHgh|Pnw61C#K`y5h+)U z*vF?mK=I+V^6vC2_*1Mrk>8hylmolc1ExJs@yVwG{=I*_VB*>TA(8SnzLRexy_0Vw zp531jDOY@6PcatmKApVXeL6+%^y@^*du+%_*OKM=v(~W|ZxSh2Ia#bhS-!3IRcl&&`%1Fo>?_Hx!|xI)_wBnR#|8P^@>laJ zKD>3;?Yi^((x;cN;S;s&{dq5N>SURRJuLUMvci;i%Phn7s<<>;#RUC!q~xcq*t==vqPvElkKLO;pritCWSa-N0UCDE4aeL6JEAztu;Kay(k?*?&8dSQheDKo_hA_6Jje_c=pJj^Ghr% zA~RdRD|lx6-e|X2j?L`aZf0>Sc-r+UHN(?k{v=+>({vqFSlpG}#KM#I;S;0Eo3i|i zznW%q;kEHQ1-|mkR9}qmFbU+Z zQDt^#|CV@x_{rm8zR~X99seou0r8W^3!kdpx%?h}E9n8@=V;&W@_2>s)#gs4GC-&3MOV1|09w~o9@lE2Z?Gt;hXM3(Ee@{L}q`b$VznM?$An)mi6Hkwn z)4s_Lo<07q|A&mJ`>`kFZ^@9OUp$q#_7&ws(ADoV$;?S*I2m>I`$#jgqVGgp>pxyP zvSeqd!kqE>3U&0u84u{s#E*VGq3`FPDEqOJ;e)fHdwDhG^^y3zj$|w>6;sW4v2fah9X?fgl1XMD&llz^6_xBWdt6x&g>Slt8T*>}LJ=&C z)tV1m*qo6CkArKf*td}3(xU@4?L|;_&GOuoVqs!*R&C9~w--Ki5cMmyx8j_l_=c|8 zKSu=J#OaDb`!wI`HDc^58A*Jrx`M+P$nogPI-h$CUs)roSMhp2uLd2)6hmj{Dmk96 zI>WcYDmHxEt`X_Sv7NT~JM9X_zEXpMit#e?11nasJ_{&)z4umEML@smk*i-Spt{Ok z{otB$^~?Opey3}>`epv=ig5LV6{hunu;gD?XtLkg6?*-J=VYB94$u7PyXA&LdLEWM zGwVwK?5G#}dm=r9 zxx&2=T#c7!m!*B6Gf$ifMk#e2bctKnC2^04JL(fqhA|ko2F73{8yJJJZD8!*>yhdO zB1@N^EvUvC7_a?tpT7=x=`XwP%|BNVcLVZxV!Ss$8*7>y^K_?8vTYbQ$+ltKB-@5@ zlWZGCmC^OtHjEo(+dPS?A=~N6QFCkCFg9d6$k>qWAY((egNzN?4l*`m`=s;Nws|U7 zFx$Fs8~tEzUHxDtUHxF*T>W5{T>W59w7z+YS1|td9l$UKGt$5q%uxelFl!Br!93O( z&GXFiNwMqkZr(YGvXjo7B#k~S!k!MqJ9hmkEF3|J5(I!kM!94EHk9T%44^Y43$dhdz_x17I*E{J1i+Q1!kn(a8Jx?c5|ADD(0{s`b7AJzt`f2{GR`B$}>()ufMiPQf$C?GtlN{Go>@@%DH->EY@C`h)!3%x|CHmaljY zH&Wi5`m3)3j5jvoH*bgl+Q_>HT-9`B|#j8C;-Ee~?J>hC{C z+)$N6A7Ni1Udw&w+9IcU&eSF~WEeN8A;TElSL@j^jGL@Q%NW~x8*JR9QyLpLsdB@( z$x1Yg8?BoOCwBtw3`EKUJH&)@gB@bRx%K+qsQ;R0!Rc#>e(%ZWMOJJc*XJ~&4;}tu zpVGgS3%dHaLyXt)PVx92ULJS#gI9pAKF?P`*v>oGJa6T3S3h{Y=<4GRHT>tDYF;1k zyr0Ki{ovK5tIz8L-s$F@b6zjxaaUj058_9jX#ME?u?`ZZ>y$k1&adMSy^_zQpT}K& z9Y5wFVbagzu0GEIK=dK!j-2*L zMqS~s(diHkjJkSI(WO4uR*%j#7)v#+&9%-97)$OGV|!+TohHT6zY4L>&5Ye;(A~@E zv(D%pcP+*1)HG0^$RVR#(A5uKU%C3h zGrOw~|9NWWDVj*RU{ybGa<1wJPRXvmj(O!;-_;MEsI|U%?&FI3v-+_sO?Unk^=sx| zQGZ5%Mg1H7;N1Y%{}uT+^RK9XqrYPP8T}Rg&*;Pc#`QDNZ(P3<{o!u*?a*Cr`<&9) z{!>327{=g^)W8_bQJv8|D>7L14UEBc+`t%I*$s@rwc5ZKTzw6U!G4e!{WF%y_9hi+ z89$Xy7ubCeRh+trICbP*slo5bO}9yfbM&-oRKYsk*Wo z7Q6Fc`bq<1u+t<)|GLvY6+3osMRzZwPj9&TxY`)cJnrgqmtfU#*J1SXxYqx& z?w-Mux~m_&c5wBBCuUb4_6JU$u72=}&(#lJ-?{pzY^-UZjs;|t3%dF%>c`}N#l3Z- zzeW88PUNot!K<07e&BA0);G_W#?#18epT`(i05ga{8~ZE1?yePFrNQLVg!Qm(vKAc zV;*l{JiJ3X<}1>Z-ywFsBzJdyib#2n4UBmDHEaRGMm&$5NO_yr`=^pE`=^p^V1%t_ zAEdn1#@&08=I%X->GT(fls7ZVQ_kTXo^}pz!APXM&1*b)9-2TfAu~kE+ZeC@qA)%x zpKJxn6Y{V9i9*UBUHrtWsYr00|4-t)^jC?KNV(wBzB%DMeK2w2sqwILK*}F%=RCWb zIPqL|a1KcM)ppL~F>&Jm2LR`QlplT0OOo9BGtjvMm(J(<)jT{o<@+70dGx7PJ};kmW=^lm6$B=H9swwy zPq-ix-j@KAd>`n7OgJBaNxm6$K_=|l5kk4$%rpXfoa=a@%{I(oc zuJEnq8C`3w=D`YsUs$;pv4__(`E0t{9$wSrGv;a@tW@}we^>KhrGf)1_agD>uUYQO zo|3Lzl0E*4tyB;g@JS+|tlSGS;hTG4l9hWwCVYnvOtNw>$RsPZXOfk>#8i3eZqpuH z;b~g=6`rQOy28`cK`J~=dwzwdsS{Orn$8Xto~Djg<&kTJJ5tZ-UTCUBuj5yEnmS{J zr|B$H;c4oyH6H1TSR0`;QH7_eQ&)JJ&Q=wkrjB3X!I=Yhsd`*Kqg9yDRq?IJ9xLC5 z%%_hE6KrWX?NyjyOT#WyVS-H!=f(;XY--rgDon7c;RIS?f=vxOV}%JeHJoKDOt2}k z$DXI_*stk($NjGQk#r8uSFh#WU&=p{_w7ly^cZo%>QC06O+95tq})9z&oTBpt=KZ! zann<*C~vYcpVreeFT}Qf448zTNTtDFVm$r(GwHH!vO^Pnw6v6Vqs4 z?(%p8W4|Lyyhb~7dWIb3O>qPha^0iEuivYQlsDyibf=&@`%v=!>_gnWdu;LgX42f> zOiZKwyW6Mn2FB6O-JO0kF`s@k&6LL*7P~Fy_kN<-n8zDzM3+{(es@j& zPmTVG20)$$Dtq``uekd2Nw>$l`5ZtL&ODy1oVli)ws8Kd0f3mXZ5fChrHrBu|*JF=-RsrQZ884#|{d8?C#JfQZ884 z$0sbHfB9FEK9O?4sy=oe=%Wi^ej??9Rei(@`q*uuPo!K>>zn7;Lyxbc9+7gvg`RU_ zICIVbLoUHJ%4xORT1HsOY*w<|WMv=m8;hB5=495GMIz;b&77(sT(CKs%r3J{q&$%IiJiWTNg&~PPh3r!|3Q{iU>f_Xn*8!n_{#ZfEYx=kv$LoO5$9_Vje58L_+aHWS*8WL%xPEw2 zT$I1>@b{aNa`o)+2`}T%d=M!YoaO!D1zzTbc_LCSsCl)GZ@8fERWABPKZ%r&{H=1) z7y3q|yyD}Pv5(Xt2a$3?ZOcTT>yYb`NV(t;?@>)mxEKq@hDf=N(WnmY8T-ZgX%CTd zLG9N>pL5a%BISZrea4URBvL+#>%A-NAL1(OPTMl!qRsSyNV#B`i}uqWBISY}*L`Cj zsY4DT<$~I;i9U15+!84l9BdiY-h_*>U~Gt#>llq{?|$u9u;&kUjdEwWg8hHs8u@6r zF2(kOfotTWTuqn%Hwr!?M?mBZ`x=pQ!GUY!-vAm!S}Q63E!@(5pm@C|!9k#g_z+#dmwUwI1(wZ0+D0*OQc-qTJzT82%nL6;C(M7--(p#depo%d%*|0fUHR(lksBwh?I}wSmk1j7&9W}qu9Az@C`K#mAN0%B5BgHoXI`0SBIP>YqqSmuK@5=BXpbDN=Lr|)2N&!? zT#1xN?Lv%U7ZCNg)N@95y>*;#&QE)Ylnd&3PxJ#Za`oW@<^f_}XWy;r2V&vsUwSsw zAFBG8A3W>@M9KwSebfWjRdBtT_#uLGqI6t$dAjl zVl7s=U>7(Mv!y*oYTM;P{Fz7Ql}Pz0zf~^gj=3aKE~sn8et2>*ur{a{ShhGbvWuR zh6^==`J?N$W*0axKM?jDZWN?EY8UbayQ1s1W*0axKM-~~XS1OWNAZ$xZQA=$^rhmX zu7inwAl|Ni(3h%yAg-?drC9A%^)WwqSpP)I1zmmAR%0J9@kjii4@55toHLC+?1ny& zy^Kh?ptk>eFIYbleZ*;Dy}I+Ss2?*w^f5mWd2{wtcm5UiW9ElG>x6Yfq+HOQe?|S6 z`QdkC{TTff_49ofte=TK;?z(-Mt?>9O!P5-L;V>274c{A> zs2`)hqJHG_M;F?^Ci;j|L;V>274hEad}1Wbn;iIGp3%HufQ%h5spSNpmDliV=s?}~sC lpI=MZ@LdtZxXF{`3?sg&#b4NlybviDbi9@asO Date: Sun, 23 Mar 2025 18:14:44 +0100 Subject: [PATCH 104/392] New golden testdata for MSYS2 --- testdata/drc/drcSimpleTests_au70.gds.5 | Bin 0 -> 72600 bytes testdata/drc/drcSimpleTests_au70d.gds.5 | Bin 0 -> 53058 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 testdata/drc/drcSimpleTests_au70.gds.5 create mode 100644 testdata/drc/drcSimpleTests_au70d.gds.5 diff --git a/testdata/drc/drcSimpleTests_au70.gds.5 b/testdata/drc/drcSimpleTests_au70.gds.5 new file mode 100644 index 0000000000000000000000000000000000000000..45c44d0224e20e59a94647fab6812b475cb1dd59 GIT binary patch literal 72600 zcmcItP0VgdQGUO__xtWJGk0LV0W}2fBzG||*OnqsX<*RxG@OE zxIjcn433G3W86#>s;jH}J@?+-es_HQ^zPM1$LC+!o$OwY}N4x#~$GhF` z)&nQI-T4oH?Q8${1>kOX^vrH|@feZvgr_fe^v^z+zjyY*thc{SqE zE55oVNiBHoPj|bAequt(1zr97AISO%U)=2;{8{<`MBy#ttNJH5^7l^eX8nqk-&lYC z*=5#0dm-yrr2KMS|NJ8BpWn&)6)C@1*T49mtPlTT{zS?J)BNwP>;IKl|NB=RNtL-b zRRMXjczOTzds#l6?_#$3yKn1$7ZH?<9O6Y3M!yRiM!yU1j(!&&8~E=28;-<(_rCh< z$@d82f60pveI2z|J>zIU@K@s3XS%*x(U#iL^Yn`L_Ibl{sT)^#wp0zJeJ$tdaoMN# z4Y!Bu_aEc9Rgi&>vY zxuDiR)%GLWuo?J@KamOEo{(}uS08c5_khs9cx*z-*YuI;_#P1Y_dGHoODHmMTr~l|rBIPr`-oL^A>Q`3avep({w3%}tQZ6{mMf*7)BISY} z*Y)`vsY4DT<$~JB3;lr@S6gPex8R~L=o=#C+DEh8dqB@?u;y30X0bC|gZ001&E{yh z2KR!>HJjt*e2$0_=8BkMUn5d3Sh;3%eCVa*LQcRg)FaqMq+D>=F2o*O$OUi_Dc3nM zo8v3>9N~ND0KtKMm`J(in)%*vrCw0x2*lhjq$eO^j%B>gah4OWxr$vfNARZoeP*NK zg$Q1i9|5mF zHxz5V2N}D-f#(BZPtae6YeTNA+J$ujyI5z4l+V_m%LTij1LPVeQoh1bHAIN%3q^F#%uW-Fy=jvciXdfBgvOOtX&}1@<5-tupj!E2kQ!va>3Q-5A64@KAsDGAp1)q<$|t0{0BRMtYt*X zXZF8goBDY(FW^7({0;Fl`Wy1c=)-R0Kk&M&MW+wc{x|xtKe&F3KI})F zf$W=zl+WUC^kFx?2V~w5DPPlv{rDabdd@wLp8p%Km_J4zb_esv=)-R41Ch4_>)+^a z$RDE*`-Ayo^kH`}e~kWy{4x3)^2g}I{$Tz*cE$WL`Wy1c=x@j$qrV}4jQ)oFG5QA%BeihWs)58}i5KZ^$2`zaf8&{)YT9`Wy1+ zy*hse-|a?UFB{FR{UcA`k{88aXWzszjOvZ(^p-U&0og-EQQN39nU>tncTrs|}W8>i4=K;pS z_sJFGV;vj8IPiveka6IhamDzi0UPhT6i|$B z?l2-UCpV-=1>SYv{a1`nEE&K5;1$;}s;XKmZMfnZ#!jW>d)3pdTf2f`JV?bgj0dT> zhVdX3*DxNW;u^+-R9wS&kcw*<4^nXr<3TF!g7L}~cfok&io0NBWo4x$Qr@k&hLP14 zxVhpQ#)DK`!+4O2`vV&*?vdN$8pc4y9b^nt+(E`b#T{e}RNO(vK*b$o3{>3A*y?dj ztOFHykTFnk3uB|lH8ut+?jU2J;ugk6k85lM<6y-NFb-DS!r16>7dEo8veFVMFBNx? zF;H;}W2474HU=tgVQlochA~ia3uB|l{lN_tSD!15{$PD|^#`k|t3OyfUH!pI>FN*G zMOS~Y3cC7(HP6)_tZ=UWU_Ep72dmer{y?o-)gP!#tNN@rtUpA`1zr8Ys^jVp))-fR zu%c-F_iv~l>*r6yI9O2vjDs~Mz&KcS0*r%oD8M*anbsM9-_xZRVhn>792M_EgUm17xPoK(m;%~r# z-S~b+%Gd3Ozj2$H@NWJaRdB0`dSB%?eEU;fm+z-IU1lEaljJMouKk!h`~<`Y{wGqt z=0EV`Z*ED?;XDdbF6cgA=Nr~E=3Vik=A)|*U&BtIo(p7@ui201WB&$XKgaWll+X5c zlYf{u>;@bDr*RXCGqy zz?b|LvW&aW*ZG2VbNYNQQQ zVbm)U`RMGBim{Q8hHQ*jU$wvE*BCH&={38&wU~`$6h6l>HN9t5O&nJt2{DL1klap9yDA*N<4&u~pcs6k$JhGhD}Z ztOMx9I=p6DzX_U(tKvDf&)n?AI-KEMKVn^TKdHOJW$q;?xBE=5;v#!0)6hQC+ppub zr@{D_^B$K~YjZU-jPVYjvCkOBcw~CBK1ii7HXfv!8OCs>XjL=A7~iiOtI#ka)&ut& zs)je}UXFV^A6v`!yp*6~%?IJ+9ggWO&lMtz>fDQGL-_ zL=O7oxp*x9rdP(@=kqr|AoR%t#PiFzt3UY0gsacr%=xd!U=_g*Af8{wUH!rLD_s34 z-ub!MRq%Wuo?pgYeVsqh!}l|0|M&7o#}Ch&@?k7~+W#tE61S{h#$Efh|CGPQ?^(Z$ zyZXsTa`#8uF?Z%5_R})%>Z1Z*ptB)N3-{Vez zx&wV6<+J(!^@jSdc9cecykH#I5fo#idsr|I>=6M*z3Nkj%c{?4R{g}->Sm3N(B=ng zNPj373@{>!&;)|b*;xXNx>o6RKHF;ojQiW!Mj#lS-6z1P>meA)JgbKR#=#vbz^H33 zY~c9jeOb<1FV=II?iY+cDl{U?K7)M& zEiL1&eySy+4@73L?_*Y%aaW(`GFCkH>0G&G+|_4|<;vzcgSDiLyZYQmSZgp}tZi1e zGVbcLPh{VReh=9OJik->NUi@_FMjoY_r_}c7W(|1aUOrl1>NT(&M9`KZ)Bgy?xKvl z`tUdMhkgD$f6BP4k37NinJ=1;azR%g-@)^N$Upoyk#a#-A8Q=CT?q@|{aU~NhEtwPUO`u%u>#_Q`a@K)boKT3TlKGuyZSoc zp^rQy!j9hhf&XjWxSRcsbr1hn`3*Dw&t0+pEc97tRKBG6Q9i3bMt?*7H~Jgux6$8F z|BSw_7l_-u{x$YbqmLLf&zN^a%4hY%=x?ZBMjz{k`_z2>Q7)+S*XVDke@1^p{afhQ zb)+^~KA+yhd&IwtyY{Obsrw%C5M20_aaVss|77gn zP``}+RqGe@Yu$OF>|4akyf4YPJAdd0_s`FJ^|RVhp3A;mP<8Z;+bzR5SkWfN)?Jig z9E@>*ajOv30ICvmUH$im`FRG>n6_UNJV#gJ1BnNA1>W_iSfR z}R|STmf^iw$79{Iaj=)3X5G#3!R|7^IM^>wjIB2&Ol0D*9$7QB=G=A9bjrqF#noLd zvxulp$%?U;JqPIkj32|<(~XRtb*+;Zr6-ggv5W_1J@0nsr@b<+7<(N+>F;Zwqo07x zD&vZ=w+iFC>qy0o&PCq(SB%gFTn7-#Io`XHUXSu^4K*y@@MV|>l@ z_QHdlK8%gwPO9}@r(q1Q!d5>1-N8Hi1tUB7&7J)f85{eIVT{+FM)oWiuUvZ!<3VbV zVT^ZDjSj#t!jD((nhj%oT{k)a!+4NBe8Cu-buWA3y;);dSlGzUY4b_bFd{NnK4}`p zcw`#;wP6fb+@bn-ekb=}^e2&WLDlz++GrbIt*YIAY2+z8^X2RWGmoeVs_GTStY;?^g)(5$DHH-)8!wqA&e{A*P zhA|xLE@Qj{=Fmv{!~0CDvp0<4$h6kX-_JXH^xyPnszq{}z?zig_7|sxJ}Nf8 zhkhP<6)CsB*c*MmnS^zLEWs%^qj~RA|Kerz`DPSWD)jcZiRLX!SAXzLQdggEYVj>u z*nxeK_LOl~pKprs4OUcc=mRmIGVbc5E+fx)cY`;b;6uLoRK{I>bOq1{LJx5uQZDG~ zBks73N_Z#79~}?!pvvtp)+T;<20wT%=1ru0?fE)>%3tEQ9RD)z+K)F&(4_;>XAS%b z4P!s%4gG{K=6R$4#r&(>{t2;#KKeZ95e!)ku6FBK+4$9Z;hmO|en~p%r1-1SEFt7hLPnoww=JhRoa%t?N3&w#R zMV*CujF<;M@U8w(E;uo^c0FU`VAcf~2jdW699&re#%XP5A3A$gfDu(|VE3OGZ$7d7 zm(EON2KU2>u^a2b9dcr9oqLUqgL`Ix5q@N~XZ0siE~pqAr+s7N;J!F9cKrw&>yCb> z?C`{k(g9pVY(#zI2kXb#`zIS){jr(#;BKxM8z*VQsQrk#!#-`+UrdZ$KMw9V0Y=n7 z?iDM0$i&z>U71-A?y3PsU2$RWRNYLUJ~4LvIJol$7iyp6v#+B6dG4a6W!%+gUqs*VoWuB*aaW(~hIO8OCw*VW zUHw#rr0TF@qF+XZOy`Xvm-C0KpVqL{9U!8?zL6PU#$A2*3u}$%B%Zst%an1g|Bqh$ z>P_d3a$YELoyBjVKk&Pm`}_^@H_zV?KckO)f&bX2&;3`%UHcIy#E9|Ie3T2i`X#&N&Hd0zA8uKPBP`+V&$%$w(a_B1{Jr*(EQ ztvBqr=XbIl>a7&_`FIZe$LHzwM7f}=k1C4q0ad(`jPf;oT`$mK@!St&9T9Y&pX%-l z?fRqMJ#zK&{93n#2hW9G!prPe8DG`Mc?~?M_t2|IxxH^N`J?Su@k)Fn_M=|I@9fpE z4p~Pj7j*5{F;m}9e28Be*ZMD*`gvXa^-tn|!Sie4hmTE2x!|;;w*H2D!}#FO2*bHB z0smG!`g;l~KaViNwv&I$Hl96{Z6s1IsBCPUfQ^k8za&xqdGWO1@gK_O+h4oFD?V=#%f{L;6+nr(D|5|1P!p8IWO^BzJamCoP@#LqnjX>CT`a2WC z#xkxLdp07*M~~)MAN^O3bw$b_)w6D#56rB=c=6wv@!FqdMk3{c%ErdYZNYf$V#%kcP`d185!u<#=zz=#kjw~-m~v-+y*{IfTT7@S+omU}Ht@+8u);Vk8$CKCQd_4I|&c}+BzgEw> zYva4+^z(VK>)o=Ce@51^*p`v;g7{fbP*N@>@cNydROyj))!fhZb$#|B%D1fR*nZu! zQPsed;qc{p-_whAI5IE6D(rPq`?ux@?BALrg5C^~Ie#DJ(a4rz)hopF60-WQbn17(knVV?)*B!1-lrFe7?+~?u>yPU@rPs|>k2g~_T@x<&&=UF+& zmUFG*(OpG*&u5e!;we;%_ytSiE2l?lCYgLB$hWU$6uB8VSuCw=sLlJp}J1 zCVR}jI_4kT*TEj~_rw#-AKs5+eaYR|#1pg!{;hGpp?J(Mv;BGT9uM_H?rpASkN79$ z;}z`T-BGz0oa`~XZGT?4|HOS(xd+|M!+YX#FFf%C{<^(@qWnRb|+*_8qsO*W=4gP)wd#<{^Y8`Xmo9X8ve1kj$N-a|J(*0W&zJo^)6bvAzP!TY?>jM`R0r?o ze75^VBcE67aqCa34`1Q&^RThrS9sk0B503a$71%BJ|p)VW}P|FZwGuCXKW*&qD?ILfLNH#X&*1OsoBfP@#=czgdg2NC$31_Uo6^$%toX<6 ze}eu&-KcLrn)M#58+>o7yazJ*Cuk4W7v2L%X!1E`5ATqGwy%rjA@*hD(4wCY#vS^| zpAxspKfyj>e>?S;@}8mGY<@xQgF!!+dpiF4nss~r+3z#`{AujVD?Di*ZJu{xJjovR z2_~OoJjlQHeM^i7_6*;zcKlQK!MiH%=3hiFbz_Cct*fm(T;U1z;VV4;I&Jj5D?Fio zeuc-~mxKNZoeNg%@%NpWe^AHZYoM)%F&@7@H~O)ad0}1DzNL3g>F~7s=h!;MyoS#^ zEB1h=eZGkMr{4dV^F^Fz_ts#y!a6_r0-r`F(ht2l-q+DxYJ0IP{zQPU{|NC4b-_@GR~(f_^Ud8tHst z)@hIjb))vZz5DrsvylugcG{b)nY8?0g#Iaq}>^U%2Zc$m7@f#_zc+e)jjB7*FVT>J@uJ_mC?*=v$CK zrGHWR6PwrAeWnxyyr20m`cGb^O8U`UkyS;_9X;hARTackG>@_7^QX#h2E8+k7QXXYYoviq4*3ss_IfwF)ANATf7XPIj zA&MW%_>?nUKc;R;Y?*aU`CsX!JY=KJJJ^`|D5BU{#sfB{4oYm9byBIq9#I~$5lqO( zgkoE&)kMm5Z{77{s*YmItV-rCg7T1!V1kVa#kN$(iIj(IobBAZvkzwvoP9WFco`2w z=Jd&|dHQ5#n(gVkG9F-@RpZ_H2Qu^d2lA83cz|*Lc-GuMo|$HKdRN8+jJ1+U6i}&= z|WX9Ux zQ!fA3*!2E+kMY5u%r+*xQ{MZdj9%HR!5(Uf}5g}sMWjx@=T5UxeO#Pj!XQTEb*t%WHP-HMOFk{AGtVnq%AHyA~ z`DMG6kEvTmRG!VYp~r=d2`M)=>a$ymQD->(*zN4QHi8j;OenU25jG~Ie6x*XtFSlg zc%|quo_}Ai2te3~*%FccWn8V99;414FkSp=W(0z`pApLyWLd{c4|VLze;@tK9rrt$zjcY=kCkto@EK!qzGeF~&Q95gW17 zk9R3OM%WnZqhRfus#%5Uf{rN;;xz?e`N zVOxDyi}Da->Y62c622xgo)IZG*`sfNFQg34Z@Iv9^5x7}ZKJJ}%loENAKqicY-=Vn zvx#D384obVS7Faa%=+l7dDivT{Kf3gD*tL^*2#|&(}b@tu_jV(Vm)Hx8Bap=ACYo% zLKF{TQ^ia^_ll$-47F~+M|k1_d?nas>4QXaAqjCHk1jLm-i z)v`LxDqPwqxw60ic(>bqYx-Lh{#`oer-VrmH(d99r$XrjFW)KuzaYOSxSCD%2~Xh( zV8lBo2|t;ig#HIl1S8}XpU+Q35058Ty{GjPSLSq!bHZHkgnNE2GZ85lRC8GFOLY#3 z5p^C_4~Tsp_7W)k7YzB15F^9U>A|{7$da!0TX;jq&&tbIg%KGV7up$3Bic} z8OEn`8PI>XpPe-#vU~5811ZLz%n_noQ2B9L{f@rz0Qsx__Q1+_$ZT!#M{?8EAIuw9 zUu8ngjz*Ta`X%OowVZJE2Q$FcA6$uA|8e~r`!A5^Rf2<3B)S7UgukD8( z);$sWW!%+=|L}bvt0a+fL02CgKb`}`_vdT6jJx{jxl;Xr+!u)OUm17xH{`GJKjMee zbi!wI<-kcAm88la^XIRhkNHD~2)~qZ*Zz6@=l(0>u0H&OX9Bq=5h)jR^zN>xv-gt=lAkQ=RfqEoz%5I<(=4{{K&W$g#BgQ)mJ`B zamVu!f1nz3_2D;sKVjLq(9O)c6;~hj$qtw7ta?SCazU;CW?jFakLLqVpU=-FQZA@= z&_=hkVC2fJnNTu=azVw|*pm&T%rN;92%DTWGBLLLhE+ze@`I@1e}WTZ)tHKPqxKla z_?qd>`XD=(u@U*mGsVhTL(RHTDHe?EW!c*jDHl|Xja6tE<9_U|LiiE!O*lItRk^7> zy;*|^*^;oF)v7-!H@iZQQO_F8@EsBPSjLqf4?pXX+7rv3UhRSZ&^;xb?Fvk&r86wIt*M9KvNHl_-}iZE0udaF=pI3fez5m711xU#XA;XC;sH1R($=?~?C z0mgW&d%FT`qb;y+o{wdGvhnH0FHhgBdox6)R)2q2(D!aee{PZEHCKOdg}M5J+3)HP z?l?pG=ogWPL`16ayZUqgIT3aBF@LT}u2CZ8g0BAHI&$?fM}F8Z6Db#T_0ygwK1=wC za<4j&}-cFgy)D!2J(^!56O-=IT;|H^oJ{<*mKboc)o-YLWY literal 0 HcmV?d00001 diff --git a/testdata/drc/drcSimpleTests_au70d.gds.5 b/testdata/drc/drcSimpleTests_au70d.gds.5 new file mode 100644 index 0000000000000000000000000000000000000000..d553ddd7f759d259ae81529d696b8b325685ed06 GIT binary patch literal 53058 zcmcg#U94?aRbFSGbM`*B-2TA5U^I;_i4QHk#;CQlm`GcFA#IR{Cfx7@N!1tpc_NS) z9|)o*1~D-ae;!CceKDaLO-w-JgF=gFs}upfC=W(T;)9X;q@j*)jrr|4$69O5HRe34 z?&REYI_LhrHRkx6W3IFJ>aZVnyPLz^C%eNphtuIZhKu1%!&}PVhl}0*$+tZ|4A)Q3 zf8?=WdGiyW{e%B}uU0{M8Zv^f2r`Gwi=}e^cIpcO8Z`U1SAkw+Ywlpp~-d zrAEA+IeB@UT9`R~$(uR9<;@&k$TQ20~Jeb0)qk|a*#694E^yce91@a4sW zS#n(Dh)=5!{jyIb3WxnC%T+AB>+s%D%fj<%txtEv*Zu)S)NMER_dRyw@%ydq3}5oP z+DO)h^ky+rT4+ZxfIFWT!_?oE$NH4IT;cHF!k% z<~d^YYw$>jufZcHgO?*e)!~t{0vmkL11>&jaTg!7u!|2`w8Z~RX}_TUwJK=%g6paA zJ!|#TeVuw93@^!R#KdDFa$KTEq6B&s^rM+u%Z@8VBi z((5qJG9Sn5`k&v@_tCD{rSRH+lH+xJe!uE{NV_Mb@{GdOKif<3h&QaCahK*@{K?Cz zC*O}<5wG26I9}I3zhAElJL3AZXPPgs|6CbAQ~js-G@s*f-qjespgl9+yuyCIrk`Wq z%s1E1@a6bYJ2~+h<_pGem~X1zG#=A@=3B8}%71o$QWrG7CiNlA7pxbVZ)<<7tT$K> zvVJRk!Tid6E9;%+TQ#1&9o|HAlZGM2U`^6BdyIcmX7L7DV)&Y@xO^5feABG!s0xN} zgRC=r8)Ti~YhLd>OAOxzS!eh**hLIqQ;r1Ur78YG8M1xOHLcmx@e5@Z>&~J7qQ`(0 zD2utW*D!eYB<@$3$Jng6_8JVX-O`DC4KY{|4XTe}nJvzrlC--{3p)&)_@a$KX5S$KX5S z$KX5S=Zj_hO!c3}Z<@~)dVabwd_jL?zWEOFVyRy+J~H22Kf^a&FL!e8EP?eJzF_=^ z^$X^6=3BAfRKG*&oFKXHhz#=u^Cip|tUsA=#lO?_I{Q)PTd`j-zm(7GI84L@Gyc_L zTt3?v4V!YrTl1GT&SJyYq~Y?44PSHA%%jloZIH!=Z-cBed>d40!?!^e8@^_L%(K|= zZIZ=?ag!|WXAH!2Q;v99{EuZ8>yDxS;;e!zP!@A#uWIl-Q?bW@mw~6Z)4Yp6q&*7~ zxWMV(T%TtW?g_{O@Gu+FynB822<#aU1HdyD(tL^ka)%(t@Mp#7E4i|DTykF=lu4W5z1NMdBtGiknzh_9B{5AG8#{!B6mFGxh<^!jPu z#Rt1<7a!c0TzqifaPiaiGqV}VYwHj03-0xq`K$!2Y>fOg@8W|sy2RfqxmV;R9G{bT`hZaWHzmhM8Rk3tl;X}lr99_fqvUuS z-{E;(*Wr0x=f!VPa(uGhdBrQ%i)-Wk_*Z#`=O{To;hR^ef=}$gnDzNMDi{W&Ga3k!xAo7OyIydv#}d~W$`DU0`S+zq?o;!gea@^yTomNhNaknD@X zcIzyY-mH@ax?o>PI(4$}69)(B+vJs5is*{1XvFJ3nWf1t+!@kNky)B_;l7{PF2Pdt zpPek+Ytr76S(@xJ>8+HdBYL?bCcaDY6uotw=U@D6Vuy3~jO4l8Y9s_4R=F@1g~uV@~@6_#V?rJwT4EKRz&^`S`@c9H25ds&wzmQ-EX19!5p zlS^@&S(@y!Sbe>0s(6Y0Y5FXnYSY29SR*{1fAzB|?F0O5Va+-fmWG_Huq@hmIVUSD z4VhhGar0mvH5HbI`cPqM*ljB;4OOPXay;Lx@7Xn$^bMwAy;fCNT9@Kc<&6~8eY+5u`+bj%*8s@!PC@PRo69kLi~Jr zeNUFed)kKr%Uc|at3#fhbdG@m27sK!ft6ORuk-&S5oywZG_ z@9ggy?uC1%omm~T?ShVy?{UP!#se3)-y`Qh}xln)ZGG#}>M{juU8*}GF>Ce4TW z_P?!skl>kAsv*s1zM^6+`FB#IhQmK8FC<=RzQT8MBgNnN-PhB5O zh*HLXSGccJ>iD<@dEfc_i5q3U-Yh8e+7&o?GyHUj>g9 z_hV1U-{K(`AM{8azixjAU)r>=Gwq)3UE&|g75TDw$-oP6Kx*9Jd)dgW!+SYCO6U41 zGnfgY_lTq9_`~J5%})yb%j?T^1UJpFq+j6rs`oVGQwF>EYDE$K)4Yq%@x#2MRQsiQ z7oV@M>mMtX*kMw!TztO1;+=|8;@_O(=eWM*ikjD7s~?6h$5YjxnQvY(4PRa#?Mi9v zoB8JT$nZ@ox)ZONuc{w(D1O9m9re=DPuJSszB1p6{jxul&xtqX^LDVy ztvIY9BZU&43huE+!smek9VE5vuFH4MmIg8bP9a&=da#S-`C>LTTV85)N+OMO^82z$;m9Ha<4PRcb z@^xjgVazKIdSsr(hA(SKPf#v+#LGFum}6R*{$F#>)9@`hi?Oca$Ip^-FE8+2y_msg z3u#n1Ss%E5H3nRKFv?wgjv3Y8iRu8Ju|^5M9Cr>EpY>Oqvrm-xAI|p5jx^m zo|AR4-#_)^?~ofF`aCRoX4cj5*-_7qpPd^Sjx+o3lFzXWSMNT5{9PjP8sB^Em$Blq zbGOg_LQTU{uAn7Oop?@^N{)}RSgpmQh5?V-olmV;ImhdGco%qB8Thj&DLG!pqf-MO zSp49*RVq22@mEDHefC*?&K2%m;4Ge>U8XzfV4gS?v{LSuZr%{b-7m{2p8sVSgLZ3R z3|g{*F=*Qc#_{hO61|Y{(v^2-SCTV}rMY zj1ArnGB$YoWbkv_Je4cx?Xqqge9*TpKIlmoAM~4x4|>VP2YpiF%~QOB_OI^%hB4@o z2F9R|8W@9KYhVoevCe3pXO{M4Fyn8OV;7Yqe#f}T(z6C%V(>dUI?95}#gETTl}-*F zAg+auQl)lsY2L-}ZtDBc0Ro5XtK@j@1TVA`H2UN7AJ7vLdZRa?UGgk z!FY$7}Y(b@61|QQp$)<1e$&<2Y`gcx%=V+{OPY9-Sa?lpGgXT|fSQ z0v?6toXm4k%lpt5eXaf}hhgdZ!)=yxW@l4IPj$A2>?PC?+tr!WxHN6B$H{f?jR4eFWd$XHL{7pKlN zSd-p`#E-LcG%#Yl%z4%TN{)+cS~oZY^>K~UJY6feXVh127&plt!?;O?8^%rY(J+qj z&3NvQ^BL!H-?^3cAxh@y_0f(Z%z8!aLQx^Ud>0ns@QR)1`~&`G$A8dFPzxoiy*_%lH{9 zhs2LcJtwWjPw79#fl9@vdH4FI{ejE=RD7Cu@umHM&;C$+ns@PI47)!51FJ}M6gU0I(jM?wiJZ@ktBMUmR zLa@t9^9_tSwp2$a*Bcni=!K4~BHVk@d;?>SKh@Ex2Mvs6ghNMGn#m5+z*t5PB09%s zZ}gPD24jvUWo-9#=@~F)-z#IgXM&YR?dYGr>~k|?cNxm+W$=tMu8zAF?V09XJY#@S z$6bfkOYrE5qCKY2ALxHNirlol<)OI*HdZk zfw}nd`iP$#59U6fi!b93*LChZy7Y$qiZtW3zz~9_9~xQ~q#VWCLT4H)UfU@-9i5Z(z(Bq>N5Q zZD1_p2P?tu@Z9BS zE$$lhc$#Kxztz9}6T}7|Jn^~u zGXq&gSTz{C3-?yd_0f?Ax?K1A!E=#|51vn5Jg(0UpB+CX$3<50fsWk82hT??9{LA9 zAz0$gGne;omFgx>CEveQa^N{Rqy97{$46POD25TwBR}~yVG)TZIG_A_mK+z^z=$Vc z0|^O6=t#-&HpYwJR7OZJU3pKIU`+E3I$rxFVSGS7^$ICZV!!^Uh2;1HtM{>tCBb!m zO*!#2c5ogg$FH?-Mv%&eEN%&9B=hEo+l2QLBf_`q~v&;ckl#yAR)nor`uC< zyp0i-JiSLPiRYffl1It$d)h1s&Wrz1&MSYdoRl0FsWIf=S2i9$dqTZ%_Jn!`D@RtI ze}Lny-hq~fgKCK<$YbS4$?=1)h_se8%%oIv;i02zb~i{dDsn zQ^A4Ey{xVAjXzjOGWU|UM&}MD$=pjY@$8z;vfVa4Jn40>c3QtqDuJEA3B1b~~RfVUi>Q{K0`m_p9)9z8>K@2tYdle?c zP{Te{VS-K#onwUwIyLNQ6{e;hw8GQ0Ggf$DqlR9#!UP*d_Sg$O$9}!Mcf5Z{PSY>S z#6#M{PIg%tALpI@e~EFjzYd4@Df8iddZjeqz&P%-A6AxhGR}W9;`oPCZ{huQ(5yOj z>)N${R_n`-QtC~;n_pV>xciDOlU-Q9CCBk59n(obPXwadxGz(Rj%mKZj^mCjx=eOv zeHs|Yn{-U43_Wd#ZsT4}DLSV41|5+V#V$Qum;YD7_|17p?%tI>cgZ)`enGLLd^zpg zl){gfqA^klz4$?;asv&S`}&mPwZpL7sIns11rN$)V6 ze^8mvKd4tq^9_u!Wzj{9wmisr8+G$IUO9JrwgeWo1lOd$(D7AH`+q99_8$`MQhOi~ z@z>vxCC5b;f1{h@(VGB|T?Kebj*G0~@ks&TZ~IlnQ*vCSix)qQ`w%4bz3sj%u|MG@ z(#4PKep0WGog4a7BCgVW6+b+!??cr9o|5AtOT2jsd(v;zv!Z`B@=gpRH0G752_?rx zDxF#vPI{Ogr{uWEW=@H=wPxOY7ug^F)@cN9C z-lexGIUex(jB^98&p0>mx_J_L&_`vhF!*59xcF4d#_XtMaTQM=(Pxw#7g@yz{8YsU zd{W?t(=zLTz2hsoKiv88VR+3`!$8S#kyZTFe^dPJf2ep$j*G0~@A`t`U;7)1r{uWE z5`R|ek4_XiL%!}WWuJO9OOA_l@i?jCeUQLkJeVcNYj~U@@jgi4vDZ>^e1gBN)IVr{ zRML}Se`EicEKz>n;dg2E%5}|nX@B~IlH(#5d4F_;mwut2C^;^&I=y zPD+kX)?4M`TDUe!j#t)rb-G4k$U(_*k)zd(tP3l0&agkL#?MHi3a(rUfJ390~+Ewzd)Mds+ow)`|j*AR) zQGc$7lH(#ht~;k|B!(Q692Z%xYlf#U>03&Ui#+NwiM<&YZ9&^ma=f(BB=+tq*A=Y! zN4+M#GhD&?f8?61(QsXf?FC1!$r|NuS^obAzW&wZg72Xh zVgY(la=eU*HND__a3L1JMal6pCMH~ODAx$v0|N;T?8B5CFS#bRH(ZDbtOXKlyLd25 zj+bkk#Kd=BMlV?-d^+y$6CG!~&;{#-j#w`x$4ebc-kQGf1?<(}laRixHf_-t>urd~ zqrNpeKwsDa5^;nWpyYTNPo=(VenbAkZ^&o(jgsS)yf!fa-GG4v2Xv(5c!lepX`BMn zsh8o}S}(>klQ=K;vzcDNKtFJ-<+b?K<$|3M2av2YlpLSLhsy`N#)F0$0ojniPA@wo1v#{4DLXt*x9 zM#HuB8XNPkw7t;_YYf!bknOR?#(Z$~3fA6`URWdQASCK%px<}(>ZnmA*PA+G)x`(n z)5Qm4(#5y>Xi}SuEn0n4SGrd~!qsG;mA8TCf0oW5za$MvwKPL8)Z@=dI zQLtB_W|nba@Evhz@Evhz@Iil9^$+;i#RvWB;)8kQ;)A*4;-NodhA~9Ragind2QC>u z2Hz1s2Hz1s2Hz1s1`qukX}S;>X}S;>X}S;^zl189xT!5kCgs5kCgs5kCgs z5kCgs5kCgs5kCgs5kCgs5kK-dsY~r&2Hz1s2Hz1s2Hz1s2Hz1s2Hz1s2Hz1s2Hz1s z2Hz1sKXl3XG5C)7G5C)7G5C)7G5C)7G5C)7G5C)7G5C)7G5C)7`Pnjl%5NIsdqFr~ zs?<+YhrVfK7|YLRg9&$$RW^J++c1{jmH-nz=crN{L*JG#jODWu!GupcsZ_=^j}wTr dm(H^i4I{qo#2+vrE+{!J(qWVbsGOY*{|7Z*D+vGq literal 0 HcmV?d00001 From b748579bd7b3b9833e0c9c674d69a183db8bbecb Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Mar 2025 18:35:50 +0100 Subject: [PATCH 105/392] Updating Python stubs --- src/pymod/distutils_src/klayout/dbcore.pyi | 448 +++++++++++++++++++-- 1 file changed, 409 insertions(+), 39 deletions(-) diff --git a/src/pymod/distutils_src/klayout/dbcore.pyi b/src/pymod/distutils_src/klayout/dbcore.pyi index 11f6de2f2..a9d4ed50c 100644 --- a/src/pymod/distutils_src/klayout/dbcore.pyi +++ b/src/pymod/distutils_src/klayout/dbcore.pyi @@ -13802,6 +13802,32 @@ class DPolygon: Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. """ ... + @overload + def delaunay(self, max_area: Optional[float] = ..., min_b: Optional[float] = ...) -> List[DPolygon]: + r""" + @brief Performs a Delaunay triangulation of the polygon. + + @return An array of triangular polygons of the refined, constrained Delaunay triangulation. + + Refinement is implemented by Chew's second algorithm. A maximum area can be given. Triangles larger than this area will be split. In addition 'skinny' triangles will be resolved where possible. 'skinny' is defined in terms of shortest edge to circumcircle radius ratio (b). A minimum number for b can be given. A value of 1.0 corresponds to a minimum angle of 30 degree and is usually a good choice. The algorithm is stable up to roughly 1.2 which corresponds to a minimum angle of abouth 37 degree. + + The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t. + + Picking a value of 0.0 for max area and min b will make the implementation skip the refinement step. In that case, the results are identical to the standard constrained Delaunay triangulation. + + This method has been introduced in version 0.30. + """ + ... + @overload + def delaunay(self, vertexes: Sequence[DPoint], max_area: Optional[float] = ..., min_b: Optional[float] = ...) -> List[DPolygon]: + r""" + @brief Performs a Delaunay triangulation of the polygon. + + This variant of the triangulation function accepts an array of additional vertexes for the triangulation. + + This method has been introduced in version 0.30. + """ + ... def destroy(self) -> None: r""" @brief Explicitly destroys the object @@ -14889,6 +14915,32 @@ class DSimplePolygon: Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. """ ... + @overload + def delaunay(self, max_area: Optional[float] = ..., min_b: Optional[float] = ...) -> List[DSimplePolygon]: + r""" + @brief Performs a Delaunay triangulation of the polygon. + + @return An array of triangular polygons of the refined, constrained Delaunay triangulation. + + Refinement is implemented by Chew's second algorithm. A maximum area can be given. Triangles larger than this area will be split. In addition 'skinny' triangles will be resolved where possible. 'skinny' is defined in terms of shortest edge to circumcircle radius ratio (b). A minimum number for b can be given. A value of 1.0 corresponds to a minimum angle of 30 degree and is usually a good choice. The algorithm is stable up to roughly 1.2 which corresponds to a minimum angle of abouth 37 degree. + + The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t. + + Picking a value of 0.0 for max area and min b will make the implementation skip the refinement step. In that case, the results are identical to the standard constrained Delaunay triangulation. + + This method has been introduced in version 0.30. + """ + ... + @overload + def delaunay(self, vertexes: Sequence[DPoint], max_area: Optional[float] = ..., min_b: Optional[float] = ...) -> List[DSimplePolygon]: + r""" + @brief Performs a Delaunay triangulation of the polygon. + + This variant of the triangulation function accepts an array of additional vertexes for the triangulation. + + This method has been introduced in version 0.30. + """ + ... def destroy(self) -> None: r""" @brief Explicitly destroys the object @@ -34855,11 +34907,11 @@ class Instance: Starting with version 0.25 the displacement is of vector type. Setter: - @brief Sets the displacement vector for the 'a' axis + @brief Sets the displacement vector for the 'a' axis in micrometer units - If the instance was not an array instance before it is made one. + Like \a= with an integer displacement, this method will set the displacement vector but it accepts a vector in micrometer units that is of \DVector type. The vector will be translated to database units internally. - This method has been introduced in version 0.23. Starting with version 0.25 the displacement is of vector type. + This method has been introduced in version 0.25. """ b: Vector r""" @@ -34904,10 +34956,10 @@ class Instance: Getter: @brief Gets the basic \CellInstArray object associated with this instance reference. Setter: - @brief Returns the basic cell instance array object by giving a micrometer unit object. - This method replaces the instance by the given CellInstArray object and it internally transformed into database units. + @brief Changes the \CellInstArray object to the given one. + This method replaces the instance by the given CellInstArray object. - This method has been introduced in version 0.25 + This method has been introduced in version 0.22 """ cplx_trans: ICplxTrans r""" @@ -35403,7 +35455,7 @@ class Instance: r""" @brief Gets the layout this instance is contained in - This const version of the method has been introduced in version 0.25. + This method has been introduced in version 0.22. """ ... @overload @@ -35411,7 +35463,7 @@ class Instance: r""" @brief Gets the layout this instance is contained in - This method has been introduced in version 0.22. + This const version of the method has been introduced in version 0.25. """ ... def pcell_declaration(self) -> PCellDeclaration_Native: @@ -40839,6 +40891,17 @@ class LayoutQuery: The latter may happen, if the object is owned by a C++ object which got destroyed itself. """ ... + @overload + def each(self, layout: Layout, cell: Cell, context: Optional[tl.ExpressionContext] = ...) -> Iterator[LayoutQueryIterator]: + r""" + @brief Executes the query and delivered the results iteratively. + + This version allows specifying a context cell. This cell can be used as a default cell for cell expressions. + + This variant has been introduced in version 0.30. + """ + ... + @overload def each(self, layout: Layout, context: Optional[tl.ExpressionContext] = ...) -> Iterator[LayoutQueryIterator]: r""" @brief Executes the query and delivered the results iteratively. @@ -40847,6 +40910,17 @@ class LayoutQuery: The context argument allows supplying an expression execution context. This context can be used for example to supply variables for the execution. It has been added in version 0.26. """ ... + @overload + def execute(self, layout: Layout, cell: Cell, context: Optional[tl.ExpressionContext] = ...) -> None: + r""" + @brief Executes the query + + This version allows specifying a context cell. This cell can be used as a default cell for cell expressions. + + This variant has been introduced in version 0.30. + """ + ... + @overload def execute(self, layout: Layout, context: Optional[tl.ExpressionContext] = ...) -> None: r""" @brief Executes the query @@ -43676,6 +43750,204 @@ class LoadLayoutOptions: This method has been added in version 0.26.2. """ + mebes_boundary_datatype: int + r""" + Getter: + @brief Gets the datatype number of the boundary layer to produce + See \mebes_produce_boundary= for a description of this attribute. + + This property has been added in version 0.23.10. + + Setter: + @brief Sets the datatype number of the boundary layer to produce + See \mebes_produce_boundary= for a description of this attribute. + + This property has been added in version 0.23.10. + """ + mebes_boundary_layer: int + r""" + Getter: + @brief Gets the layer number of the boundary layer to produce + See \mebes_produce_boundary= for a description of this attribute. + + This property has been added in version 0.23.10. + + Setter: + @brief Sets the layer number of the boundary layer to produce + See \mebes_produce_boundary= for a description of this attribute. + + This property has been added in version 0.23.10. + """ + mebes_boundary_name: str + r""" + Getter: + @brief Gets the name of the boundary layer to produce + See \mebes_produce_boundary= for a description of this attribute. + + This property has been added in version 0.23.10. + + Setter: + @brief Sets the name of the boundary layer to produce + See \mebes_produce_boundary= for a description of this attribute. + + This property has been added in version 0.23.10. + """ + mebes_create_other_layers: bool + r""" + Getter: + @brief Gets a value indicating whether other layers shall be created + @return True, if other layers will be created. + This attribute acts together with a layer map (see \mebes_layer_map=). Layers not listed in this map are created as well when \mebes_create_other_layers? is true. Otherwise they are ignored. + + This method has been added in version 0.25 and replaces the respective global option in \LoadLayoutOptions in a format-specific fashion. + Setter: + @brief Specifies whether other layers shall be created + @param create True, if other layers will be created. + See \mebes_create_other_layers? for a description of this attribute. + + This method has been added in version 0.25 and replaces the respective global option in \LoadLayoutOptions in a format-specific fashion. + """ + mebes_data_datatype: int + r""" + Getter: + @brief Gets the datatype number of the data layer to produce + + This property has been added in version 0.23.10. + + Setter: + @brief Sets the datatype number of the data layer to produce + + This property has been added in version 0.23.10. + """ + mebes_data_layer: int + r""" + Getter: + @brief Gets the layer number of the data layer to produce + + This property has been added in version 0.23.10. + + Setter: + @brief Sets the layer number of the data layer to produce + + This property has been added in version 0.23.10. + """ + mebes_data_name: str + r""" + Getter: + @brief Gets the name of the data layer to produce + + This property has been added in version 0.23.10. + + Setter: + @brief Sets the name of the data layer to produce + + This property has been added in version 0.23.10. + """ + mebes_invert: bool + r""" + Getter: + @brief Gets a value indicating whether to invert the MEBES pattern + If this property is set to true, the pattern will be inverted. + + This property has been added in version 0.22. + + Setter: + @brief Specify whether to invert the MEBES pattern + If this property is set to true, the pattern will be inverted. + + This property has been added in version 0.22. + """ + mebes_layer_map: LayerMap + r""" + Getter: + @brief Gets the layer map + @return The layer map. + + This method has been added in version 0.25 and replaces the respective global option in \LoadLayoutOptions in a format-specific fashion. + Setter: + @brief Sets the layer map + This sets a layer mapping for the reader. Unlike \mebes_set_layer_map, the 'create_other_layers' flag is not changed. + @param map The layer map to set. + + This convenience method has been added in version 0.26.2. + """ + mebes_num_shapes_per_cell: int + r""" + Getter: + @brief Gets the number of stripes collected per cell + See \mebes_num_stripes_per_cell= for details about this property. + + This property has been added in version 0.24.5. + + Setter: + @brief Specify the number of stripes collected per cell + See \mebes_num_stripes_per_cell= for details about this property. + + This property has been added in version 0.24.5. + """ + mebes_num_stripes_per_cell: int + r""" + Getter: + @brief Gets the number of stripes collected per cell + See \mebes_num_stripes_per_cell= for details about this property. + + This property has been added in version 0.23.10. + + Setter: + @brief Specify the number of stripes collected per cell + This property specifies how many stripes will be collected into one cell. + A smaller value means less but bigger cells. The default value is 64. + New cells will be formed whenever more than this number of stripes has been read + or a new segment is started and the number of shapes given by \mebes_num_shapes_per_cell + is exceeded. + + This property has been added in version 0.23.10. + """ + mebes_produce_boundary: bool + r""" + Getter: + @brief Gets a value indicating whether a boundary layer will be produced + See \mebes_produce_boundary= for details about this property. + + This property has been added in version 0.23.10. + + Setter: + @brief Specify whether to produce a boundary layer + If this property is set to true, the pattern boundary will be written to the layer and datatype specified with \mebes_boundary_name, \mebes_boundary_layer and \mebes_boundary_datatype. + By default, the boundary layer is produced. + + This property has been added in version 0.23.10. + """ + mebes_subresolution: bool + r""" + Getter: + @brief Gets a value indicating whether to invert the MEBES pattern + See \subresolution= for details about this property. + + This property has been added in version 0.23.10. + + Setter: + @brief Specify whether subresolution trapezoids are supported + If this property is set to true, subresolution trapezoid vertices are supported. + In order to implement support, the reader will create magnified instances with a magnification of 1/16. + By default this property is enabled. + + This property has been added in version 0.23.10. + """ + mebes_top_cell_index: int + r""" + Getter: + @brief Gets the cell index for the top cell to use + See \mebes_top_cell_index= for a description of this property. + + This property has been added in version 0.23.10. + + Setter: + @brief Specify the cell index for the top cell to use + If this property is set to a valid cell index, the MEBES reader will put the subcells and shapes into this cell. + + This property has been added in version 0.23.10. + """ oasis_expect_strict_mode: int r""" Getter: @@ -43915,6 +44187,26 @@ class LoadLayoutOptions: This method has been added in version 0.26.2. """ ... + def mebes_select_all_layers(self) -> None: + r""" + @brief Selects all layers and disables the layer map + + This disables any layer map and enables reading of all layers. + New layers will be created when required. + + This method has been added in version 0.25 and replaces the respective global option in \LoadLayoutOptions in a format-specific fashion. + """ + ... + def mebes_set_layer_map(self, map: LayerMap, create_other_layers: bool) -> None: + r""" + @brief Sets the layer map + This sets a layer mapping for the reader. The layer map allows selection and translation of the original layers. + @param map The layer map to set. + @param create_other_layers The flag indicating whether other layers will be created as well. Set to false to read only the layers in the layer map. + + This method has been added in version 0.25 and replaces the respective global option in \LoadLayoutOptions in a format-specific fashion. + """ + ... def select_all_layers(self) -> None: r""" @brief Selects all layers and disables the layer map @@ -46110,15 +46402,15 @@ class NetPinRef: @overload def net(self) -> Net: r""" - @brief Gets the net this pin reference is attached to. + @brief Gets the net this pin reference is attached to (non-const version). + + This constness variant has been introduced in version 0.26.8 """ ... @overload def net(self) -> Net: r""" - @brief Gets the net this pin reference is attached to (non-const version). - - This constness variant has been introduced in version 0.26.8 + @brief Gets the net this pin reference is attached to. """ ... def pin(self) -> Pin: @@ -46441,15 +46733,15 @@ class NetTerminalRef: @overload def net(self) -> Net: r""" - @brief Gets the net this terminal reference is attached to (non-const version). - - This constness variant has been introduced in version 0.26.8 + @brief Gets the net this terminal reference is attached to. """ ... @overload def net(self) -> Net: r""" - @brief Gets the net this terminal reference is attached to. + @brief Gets the net this terminal reference is attached to (non-const version). + + This constness variant has been introduced in version 0.26.8 """ ... def terminal_def(self) -> DeviceTerminalDefinition: @@ -47444,15 +47736,6 @@ class Netlist: """ ... @overload - def circuits_by_name(self, name_pattern: str) -> List[Circuit]: - r""" - @brief Gets the circuit objects for a given name filter. - The name filter is a glob pattern. This method will return all \Circuit objects matching the glob pattern. - - This method has been introduced in version 0.26.4. - """ - ... - @overload def circuits_by_name(self, name_pattern: str) -> List[Circuit]: r""" @brief Gets the circuit objects for a given name filter (const version). @@ -47462,6 +47745,15 @@ class Netlist: This constness variant has been introduced in version 0.26.8. """ ... + @overload + def circuits_by_name(self, name_pattern: str) -> List[Circuit]: + r""" + @brief Gets the circuit objects for a given name filter. + The name filter is a glob pattern. This method will return all \Circuit objects matching the glob pattern. + + This method has been introduced in version 0.26.4. + """ + ... def combine_devices(self) -> None: r""" @brief Combines devices where possible @@ -53278,6 +53570,34 @@ class Polygon: This method was introduced in version 0.25. """ ... + @overload + def delaunay(self, max_area: Optional[float] = ..., min_b: Optional[float] = ..., dbu: Optional[float] = ...) -> Region: + r""" + @brief Performs a Delaunay triangulation of the polygon. + + @return A \Region holding the triangles of the refined, constrained Delaunay triangulation. + + Refinement is implemented by Chew's second algorithm. A maximum area can be given. Triangles larger than this area will be split. In addition 'skinny' triangles will be resolved where possible. 'skinny' is defined in terms of shortest edge to circumcircle radius ratio (b). A minimum number for b can be given. A value of 1.0 corresponds to a minimum angle of 30 degree and is usually a good choice. The algorithm is stable up to roughly 1.2 which corresponds to a minimum angle of abouth 37 degree. + + The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t. + + The area value is given in terms of DBU units. Picking a value of 0.0 for area and min b will make the implementation skip the refinement step. In that case, the results are identical to the standard constrained Delaunay triangulation. + + The 'dbu' parameter a numerical scaling parameter. It should be choosen in a way that the polygon dimensions are "in the order of 1" (very roughly) after multiplication with the dbu parameter. A value of 0.001 is suitable for polygons with typical dimensions in the order to 1000 DBU. Usually the default value is good enough. + + This method has been introduced in version 0.30. + """ + ... + @overload + def delaunay(self, vertexes: Sequence[Point], max_area: Optional[float] = ..., min_b: Optional[float] = ..., dbu: Optional[float] = ...) -> Region: + r""" + @brief Performs a Delaunay triangulation of the polygon. + + This variant of the triangulation function accepts an array of additional vertexes for the triangulation. + + This method has been introduced in version 0.30. + """ + ... def destroy(self) -> None: r""" @brief Explicitly destroys the object @@ -57105,6 +57425,26 @@ class RecursiveShapeIterator: This attribute has been introduced in version 0.28.4. """ ... + def properties(self) -> Any: + r""" + @brief Gets the effective user properties + See \prop_id for the definition of 'effective user properties'. + + This method is a convenience method that gets the effective properties of the current shape as a single hash. + + This method has been introduced in version 0.30. + """ + ... + def property(self, key: Any) -> Any: + r""" + @brief Gets the effective user property with the given key + See \prop_id for the definition of 'effective user property'. + + This method is a convenience method that gets the effective property of the current shape with the given key. If no property with that key exists, it will return nil. + + This method has been introduced in version 0.30. + """ + ... def remove_properties(self) -> None: r""" @brief Removes properties for the given container. @@ -63037,11 +63377,10 @@ class Shape: Starting with version 0.23, this method returns nil, if the shape does not represent a geometrical primitive that can be converted to a simple polygon. Setter: - @brief Replaces the shape by the given simple polygon object - This method replaces the shape by the given simple polygon object. This method can only be called for editable layouts. It does not change the user properties of the shape. - Calling this method will invalidate any iterators. It should not be called inside a loop iterating over shapes. + @brief Replaces the shape by the given simple polygon (in micrometer units) + This method replaces the shape by the given text, like \simple_polygon= with a \SimplePolygon argument does. This version translates the polygon from micrometer units to database units internally. - This method has been introduced in version 0.22. + This method has been introduced in version 0.25. """ text: Any r""" @@ -63830,7 +64169,8 @@ class Shape: def property(self, key: Any) -> Any: r""" @brief Gets the user property with the given key - This method is a convenience method that gets the property with the given key. If no property with that key does not exist, it will return nil. Using that method is more convenient than using the layout object and the properties ID to retrieve the property value. + This method is a convenience method that gets the property with the given key. If no property with that key exists, it will return nil. Using that method is more convenient than using the layout object and the properties ID to retrieve the property value. + This method has been introduced in version 0.22. """ ... @@ -66367,6 +66707,34 @@ class SimplePolygon: Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. """ ... + @overload + def delaunay(self, max_area: Optional[float] = ..., min_b: Optional[float] = ..., dbu: Optional[float] = ...) -> Region: + r""" + @brief Performs a Delaunay triangulation of the polygon. + + @return A \Region holding the triangles of the refined, constrained Delaunay triangulation. + + Refinement is implemented by Chew's second algorithm. A maximum area can be given. Triangles larger than this area will be split. In addition 'skinny' triangles will be resolved where possible. 'skinny' is defined in terms of shortest edge to circumcircle radius ratio (b). A minimum number for b can be given. A value of 1.0 corresponds to a minimum angle of 30 degree and is usually a good choice. The algorithm is stable up to roughly 1.2 which corresponds to a minimum angle of abouth 37 degree. + + The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t. + + The area value is given in terms of DBU units. Picking a value of 0.0 for area and min b will make the implementation skip the refinement step. In that case, the results are identical to the standard constrained Delaunay triangulation. + + The 'dbu' parameter a numerical scaling parameter. It should be choosen in a way that the polygon dimensions are "in the order of 1" (very roughly) after multiplication with the dbu parameter. A value of 0.001 is suitable for polygons with typical dimensions in the order to 1000 DBU. Usually the default value is good enough. + + This method has been introduced in version 0.30. + """ + ... + @overload + def delaunay(self, vertexes: Sequence[Point], max_area: Optional[float] = ..., min_b: Optional[float] = ..., dbu: Optional[float] = ...) -> Region: + r""" + @brief Performs a Delaunay triangulation of the polygon. + + This variant of the triangulation function accepts an array of additional vertexes for the triangulation. + + This method has been introduced in version 0.30. + """ + ... def destroy(self) -> None: r""" @brief Explicitly destroys the object @@ -67184,17 +67552,17 @@ class SubCircuit(NetlistObject): @overload def circuit(self) -> Circuit: r""" - @brief Gets the circuit the subcircuit lives in (non-const version). + @brief Gets the circuit the subcircuit lives in. This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \circuit_ref. - - This constness variant has been introduced in version 0.26.8 """ ... @overload def circuit(self) -> Circuit: r""" - @brief Gets the circuit the subcircuit lives in. + @brief Gets the circuit the subcircuit lives in (non-const version). This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \circuit_ref. + + This constness variant has been introduced in version 0.26.8 """ ... @overload @@ -67879,7 +68247,8 @@ class Text: Setter: @brief Sets the horizontal alignment - This is the version accepting integer values. It's provided for backward compatibility. + This property specifies how the text is aligned relative to the anchor point. + This property has been introduced in version 0.22 and extended to enums in 0.28. """ size: int r""" @@ -67915,8 +68284,7 @@ class Text: Setter: @brief Sets the vertical alignment - This property specifies how the text is aligned relative to the anchor point. - This property has been introduced in version 0.22 and extended to enums in 0.28. + This is the version accepting integer values. It's provided for backward compatibility. """ x: int r""" @@ -70197,10 +70565,12 @@ class Texts(ShapeCollection): @return A new text collection containing the texts not inside or on the edge of polygons from the region """ ... - def polygons(self, e: Optional[int] = ...) -> Region: + def polygons(self, e: Optional[int] = ..., text_prop: Optional[Any] = ...) -> Region: r""" @brief Converts the edge pairs to polygons - This method creates polygons from the texts. This is equivalent to calling \extents. + This method creates polygons from the texts. This is basically equivalent to calling \extents. In addition, a user property with the key given by 'text_prop' can be attached. The value of that user property will be the text string. If 'text_prop' is nil, no user property is attached. + + The 'text_prop' argument has been added in version 0.30. """ ... def process(self, process: TextOperator) -> None: From e6ff30adeed7aee3a274f0c4ef1a0c0d69a44632 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 23 Mar 2025 18:46:15 +0100 Subject: [PATCH 106/392] Modifying definition of Timer#memory_size on Linux to become resident size. --- src/gsi/gsi/gsiDeclTl.cc | 6 +++++- src/tl/tl/tlTimer.cc | 7 ++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/gsi/gsi/gsiDeclTl.cc b/src/gsi/gsi/gsiDeclTl.cc index e3a05295b..eae6d0629 100644 --- a/src/gsi/gsi/gsiDeclTl.cc +++ b/src/gsi/gsi/gsiDeclTl.cc @@ -147,7 +147,11 @@ Class decl_Timer ("tl", "Timer", gsi::method ("memory_size", &tl::Timer::memory_size, "@brief Gets the current memory usage of the process in Bytes\n" "\n" - "This method has been introduced in version 0.27." + "The returned value is the resident memory size on Linux and MacOS and " + "the working set size on Windows.\n" + "\n" + "This method has been introduced in version 0.27. The value has been changed " + "to be resident size (instead of virtual size) on Linux in version 0.30." ) + gsi::method ("user", &tl::Timer::sec_user, "@brief Returns the elapsed CPU time in user mode from start to stop in seconds\n" diff --git a/src/tl/tl/tlTimer.cc b/src/tl/tl/tlTimer.cc index c7cb1b11c..76c71ceff 100644 --- a/src/tl/tl/tlTimer.cc +++ b/src/tl/tl/tlTimer.cc @@ -245,8 +245,8 @@ Timer::memory_size () "%*d " // 0 "%*d " // itrealvalue "%*u " // starttime - "%lu " // vsize - "%*d " // rss + "%*u " // vsize + "%lu " // rss "%*u " // rlim "%*u " // startcode "%*u " // endcode @@ -270,7 +270,8 @@ Timer::memory_size () memsize = 0; } } - return size_t (memsize); + + return size_t (memsize) * size_t (getpagesize ()); #else # error Unsupported platform From db66a6ee74b74f0c40197fce1089f592146551ae Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 25 Mar 2025 23:43:14 +0100 Subject: [PATCH 107/392] Base class of DEdgePairWithProperties was EdgePair, not DEdgePair --- src/db/db/gsiDeclDbEdgePair.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/db/gsiDeclDbEdgePair.cc b/src/db/db/gsiDeclDbEdgePair.cc index 8c34e5507..0ba631bde 100644 --- a/src/db/db/gsiDeclDbEdgePair.cc +++ b/src/db/db/gsiDeclDbEdgePair.cc @@ -370,7 +370,7 @@ static db::DEdgePairWithProperties *new_dedge_pair_with_properties2 (const db::D return new db::DEdgePairWithProperties (edge_pair, db::properties_id (db::PropertiesSet (properties.begin (), properties.end ()))); } -Class decl_DEdgePairWithProperties (decl_EdgePair, "db", "DEdgePairWithProperties", +Class decl_DEdgePairWithProperties (decl_DEdgePair, "db", "DEdgePairWithProperties", gsi::properties_support_methods () + constructor ("new", &new_dedge_pair_with_properties, gsi::arg ("edge_pair"), gsi::arg ("properties_id", db::properties_id_type (0)), "@brief Creates a new object from a property-less object and a properties ID." From 227203cdd10eac485b959de13d17f084007e008a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 26 Mar 2025 00:43:31 +0100 Subject: [PATCH 108/392] Providing a less strict overload resolution Downcast now has precedence over conversion constructors, hence less ambiguities Solution is implemented for - Ruby - Python - Expressions For Expressions: - The overload resolution is less evolved anyway - There was an additional bug preventing to pass arrays (hashes) in expressions --- src/gsi/gsi/gsiExpression.cc | 6 +- src/gsi/gsi/gsiVariantArgs.cc | 44 +++++++------- src/gsi/gsi/gsiVariantArgs.h | 3 +- src/gsi/unit_tests/gsiExpressionTests.cc | 36 ++++++++++++ src/pya/pya/pyaCallables.cc | 2 + src/pya/pya/pyaMarshal.cc | 2 +- src/rba/rba/rba.cc | 2 + src/rba/rba/rbaMarshal.cc | 2 +- src/rdb/rdb/gsiDeclRdb.cc | 5 ++ testdata/python/rdbTest.py | 72 +++++++++++++++++++++++ testdata/ruby/rdbTest.rb | 74 ++++++++++++++++++++++++ 11 files changed, 223 insertions(+), 25 deletions(-) diff --git a/src/gsi/gsi/gsiExpression.cc b/src/gsi/gsi/gsiExpression.cc index 4ac9fbc10..1727c2bc6 100644 --- a/src/gsi/gsi/gsiExpression.cc +++ b/src/gsi/gsi/gsiExpression.cc @@ -952,9 +952,11 @@ VariantUserClassImpl::execute_gsi (const tl::ExpressionParserContext & /*context const tl::Variant *arg = i >= int (args.size ()) ? get_kwarg (*a, kwargs) : &args[i]; if (! arg) { is_valid = a->spec ()->has_default (); - } else if (gsi::test_arg (*a, *arg, false /*strict*/)) { + } else if (gsi::test_arg (*a, *arg, false /*strict*/, false /*no object substitution*/)) { + sc += 100; + } else if (gsi::test_arg (*a, *arg, true /*loose*/, false /*no object substitution*/)) { ++sc; - } else if (test_arg (*a, *arg, true /*loose*/)) { + } else if (gsi::test_arg (*a, *arg, true /*loose*/, true /*with object substitution*/)) { // non-scoring match } else { is_valid = false; diff --git a/src/gsi/gsi/gsiVariantArgs.cc b/src/gsi/gsi/gsiVariantArgs.cc index 39dc0725e..5e1b4ffb7 100644 --- a/src/gsi/gsi/gsiVariantArgs.cc +++ b/src/gsi/gsi/gsiVariantArgs.cc @@ -46,12 +46,12 @@ inline void *get_object (tl::Variant &var) // ------------------------------------------------------------------- // Test if an argument can be converted to the given type -bool test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose); +bool test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose, bool object_substitution); template struct test_arg_func { - void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType & /*atype*/, bool /*loose*/) + void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType & /*atype*/, bool /*loose*/, bool /*object_substitution*/) { *ret = arg.can_convert_to (); } @@ -60,7 +60,16 @@ struct test_arg_func template <> struct test_arg_func { - void operator () (bool *ret, const tl::Variant & /*arg*/, const gsi::ArgType & /*atype*/, bool /*loose*/) + void operator () (bool *ret, const tl::Variant & /*arg*/, const gsi::ArgType & /*atype*/, bool /*loose*/, bool /*object_substitution*/) + { + *ret = true; + } +}; + +template <> +struct test_arg_func +{ + void operator () (bool *ret, const tl::Variant & /*arg*/, const gsi::ArgType & /*atype*/, bool /*loose*/, bool /*object_substitution*/) { *ret = true; } @@ -69,7 +78,7 @@ struct test_arg_func template <> struct test_arg_func { - void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose) + void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose, bool object_substitution) { // allow nil of pointers if ((atype.is_ptr () || atype.is_cptr ()) && arg.is_nil ()) { @@ -77,7 +86,7 @@ struct test_arg_func return; } - if (arg.is_list ()) { + if (object_substitution && arg.is_list ()) { // we may implicitly convert an array into a constructor call of a target object - // for now we only check whether the number of arguments is compatible with the array given. @@ -104,9 +113,9 @@ struct test_arg_func const tl::VariantUserClassBase *cls = arg.user_cls (); if (! cls) { *ret = false; - } else if (! cls->gsi_cls ()->is_derived_from (atype.cls ()) && (! loose || ! cls->gsi_cls ()->can_convert_to(atype.cls ()))) { - *ret = false; - } else if ((atype.is_ref () || atype.is_ptr ()) && cls->is_const ()) { + } else if (! (cls->gsi_cls () == atype.cls () || + (loose && (cls->gsi_cls ()->is_derived_from (atype.cls ()) || + (object_substitution && cls->gsi_cls ()->can_convert_to (atype.cls ())))))) { *ret = false; } else { *ret = true; @@ -117,7 +126,7 @@ struct test_arg_func template <> struct test_arg_func { - void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose) + void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose, bool /*object_substitution*/) { if (! arg.is_list ()) { *ret = false; @@ -129,7 +138,7 @@ struct test_arg_func *ret = true; for (tl::Variant::const_iterator v = arg.begin (); v != arg.end () && *ret; ++v) { - if (! test_arg (ainner, *v, loose)) { + if (! test_arg (ainner, *v, loose, true)) { *ret = false; } } @@ -139,7 +148,7 @@ struct test_arg_func template <> struct test_arg_func { - void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose) + void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose, bool /*object_substitution*/) { // Note: delegating that to the function avoids "injected class name used as template template expression" warning if (! arg.is_array ()) { @@ -152,16 +161,11 @@ struct test_arg_func const ArgType &ainner = *atype.inner (); const ArgType &ainner_k = *atype.inner_k (); - if (! arg.is_list ()) { - *ret = false; - return; - } - *ret = true; for (tl::Variant::const_array_iterator a = arg.begin_array (); a != arg.end_array () && *ret; ++a) { - if (! test_arg (ainner_k, a->first, loose)) { + if (! test_arg (ainner_k, a->first, loose, true)) { *ret = false; - } else if (! test_arg (ainner, a->second, loose)) { + } else if (! test_arg (ainner, a->second, loose, true)) { *ret = false; } } @@ -169,7 +173,7 @@ struct test_arg_func }; bool -test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose) +test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose, bool object_substitution) { // for const X * or X *, nil is an allowed value if ((atype.is_cptr () || atype.is_ptr ()) && arg.is_nil ()) { @@ -177,7 +181,7 @@ test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose) } bool ret = false; - gsi::do_on_type () (atype.type (), &ret, arg, atype, loose); + gsi::do_on_type () (atype.type (), &ret, arg, atype, loose, object_substitution); return ret; } diff --git a/src/gsi/gsi/gsiVariantArgs.h b/src/gsi/gsi/gsiVariantArgs.h index 75f312819..187132289 100644 --- a/src/gsi/gsi/gsiVariantArgs.h +++ b/src/gsi/gsi/gsiVariantArgs.h @@ -70,10 +70,11 @@ GSI_PUBLIC void pull_arg (gsi::SerialArgs &retlist, const gsi::ArgType &atype, t * @param atype The argument type * @param arg The value to pass to it * @param loose true for loose checking + * @param object_substitution true to substitute object arguments by lists (using constructor) or employing conversion constructors * * @return True, if the argument can be passed */ -GSI_PUBLIC bool test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose); +GSI_PUBLIC bool test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose, bool object_substitution); } diff --git a/src/gsi/unit_tests/gsiExpressionTests.cc b/src/gsi/unit_tests/gsiExpressionTests.cc index 0449850d5..105383e92 100644 --- a/src/gsi/unit_tests/gsiExpressionTests.cc +++ b/src/gsi/unit_tests/gsiExpressionTests.cc @@ -863,3 +863,39 @@ TEST(16) } } +// implicit conversions +TEST(17) +{ + tl::Eval e; + tl::Variant v; + + // smoke test + v = e.parse ("var rdb=ReportDatabase.new();" + "var cat=rdb.create_category('name');" + "var cell=rdb.create_cell('TOP');" + "var it=rdb.create_item(cell,cat);" + "var bwp=BoxWithProperties.new(Box.new(0,0,1,2), {1=>'value'});" + "it.add_value(bwp)").execute (); + + v = e.parse ("var rdb=ReportDatabase.new();" + "var cat=rdb.create_category('name');" + "var cell=rdb.create_cell('TOP');" + "var it=rdb.create_item(cell,cat);" + "var bwp=DBoxWithProperties.new(DBox.new(0,0,1,2), {1=>'value'});" + "it.add_value(bwp)").execute (); + + v = e.parse ("var rdb=ReportDatabase.new();" + "var cat=rdb.create_category('name');" + "var cell=rdb.create_cell('TOP');" + "var it=rdb.create_item(cell,cat);" + "var b=DBox.new(0,0,1,2);" + "it.add_value(b)").execute (); + + v = e.parse ("var rdb=ReportDatabase.new();" + "var cat=rdb.create_category('name');" + "var cell=rdb.create_cell('TOP');" + "var it=rdb.create_item(cell,cat);" + "it.add_value(17.5)").execute (); +} + + diff --git a/src/pya/pya/pyaCallables.cc b/src/pya/pya/pyaCallables.cc index f9fd2c75f..829f7076f 100644 --- a/src/pya/pya/pyaCallables.cc +++ b/src/pya/pya/pyaCallables.cc @@ -413,6 +413,8 @@ match_method (int mid, PyObject *self, PyObject *args, PyObject *kwargs, bool st if (! arg) { is_valid = a->spec ()->has_default (); } else if (test_arg (*a, arg.get (), false /*strict*/, false /*object substitution*/)) { + sc += 100; + } else if (test_arg (*a, arg.get (), true /*loose*/, false /*object substitution*/)) { ++sc; } else if (test_arg (*a, arg.get (), true /*loose*/, true /*object substitution*/)) { // non-scoring match diff --git a/src/pya/pya/pyaMarshal.cc b/src/pya/pya/pyaMarshal.cc index 7a50c85e7..0326f2e18 100644 --- a/src/pya/pya/pyaMarshal.cc +++ b/src/pya/pya/pyaMarshal.cc @@ -1232,7 +1232,7 @@ struct test_arg_func return; } - if (! (cls_decl == acls || (loose && (cls_decl->is_derived_from (atype.cls ()) || cls_decl->can_convert_to (atype.cls ()))))) { + if (! (cls_decl == acls || (loose && (cls_decl->is_derived_from (atype.cls ()) || (object_substitution && cls_decl->can_convert_to (atype.cls ())))))) { *ret = false; return; } diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index 7979bc6cc..77faa3d88 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -539,6 +539,8 @@ private: if (arg == Qundef) { is_valid = a->spec ()->has_default (); } else if (test_arg (*a, arg, false /*strict*/, false /*with object substitution*/)) { + sc += 100; + } else if (test_arg (*a, arg, true /*loose*/, false /*with object substitution*/)) { ++sc; } else if (test_arg (*a, arg, true /*loose*/, true /*with object substitution*/)) { // non-scoring match diff --git a/src/rba/rba/rbaMarshal.cc b/src/rba/rba/rbaMarshal.cc index b55f82111..0781f5e7d 100644 --- a/src/rba/rba/rbaMarshal.cc +++ b/src/rba/rba/rbaMarshal.cc @@ -1217,7 +1217,7 @@ struct test_arg_func // in loose mode (second pass) try to match the types via implicit constructors, // in strict mode (first pass) require direct type match - if (p->cls_decl () == atype.cls () || (loose && (p->cls_decl ()->is_derived_from (atype.cls ()) || p->cls_decl ()->can_convert_to (atype.cls ())))) { + if (p->cls_decl () == atype.cls () || (loose && (p->cls_decl ()->is_derived_from (atype.cls ()) || (object_substitution && p->cls_decl ()->can_convert_to (atype.cls ()))))) { // type matches: check constness if ((atype.is_ref () || atype.is_ptr ()) && p->const_ref ()) { *ret = false; diff --git a/src/rdb/rdb/gsiDeclRdb.cc b/src/rdb/rdb/gsiDeclRdb.cc index 10307e96f..d2db8c0d1 100644 --- a/src/rdb/rdb/gsiDeclRdb.cc +++ b/src/rdb/rdb/gsiDeclRdb.cc @@ -999,6 +999,11 @@ Class decl_RdbItem ("rdb", "RdbItem", "@param value The box to add.\n" "This method has been introduced in version 0.25 as a convenience method." ) + + gsi::method_ext ("add_value", &add_value_t, gsi::arg ("value"), + "@brief Adds a text object to the values of this item\n" + "@param value The text to add.\n" + "This method has been introduced in version 0.30.1 to support text objects with properties." + ) + gsi::method_ext ("add_value", &add_value_t, gsi::arg ("value"), "@brief Adds an edge object to the values of this item\n" "@param value The edge to add.\n" diff --git a/testdata/python/rdbTest.py b/testdata/python/rdbTest.py index dfa2194d6..a7c34925f 100644 --- a/testdata/python/rdbTest.py +++ b/testdata/python/rdbTest.py @@ -972,6 +972,78 @@ class RDB_TestClass(unittest.TestCase): _cat_same = None self.assertEqual(_subcat.rdb_id(), _subcat_same.rdb_id()) + def test_15(self): + + p = pya.DPolygon(pya.DBox(0.5, 1, 2, 3)) + pwp = pya.DPolygonWithProperties(p, { 1: "value" }) + e = pya.DEdge(pya.DPoint(0, 0), pya.DPoint(1, 2)) + ewp = pya.DEdgeWithProperties(e, { 1: "value" }) + ep = pya.DEdgePair(e, e.moved(10, 10)) + epwp = pya.DEdgePairWithProperties(ep, { 1: "value" }) + t = pya.DText("text", pya.DTrans.R0) + twp = pya.DTextWithProperties(t, { 1: "value" }) + b = pya.DBox(0, 0, 1, 2) + bwp = pya.DBoxWithProperties(b, { 1: "value" }) + + ip = pya.Polygon(pya.Box(0, 1, 2, 3)) + ipwp = pya.PolygonWithProperties(ip, { 1: "value" }) + ie = pya.Edge(pya.Point(0, 0), pya.Point(1, 2)) + iewp = pya.EdgeWithProperties(ie, { 1: "value" }) + iep = pya.EdgePair(ie, ie.moved(10, 10)) + iepwp = pya.EdgePairWithProperties(iep, { 1: "value" }) + it = pya.Text("text", pya.Trans.R0) + itwp = pya.TextWithProperties(it, { 1: "value" }) + ib = pya.Box(0, 0, 1, 2) + ibwp = pya.BoxWithProperties(ib, { 1: "value" }) + + rdb = pya.ReportDatabase() + + cat = rdb.create_category("name") + cell = rdb.create_cell("TOP") + item = rdb.create_item(cell, cat) + + item.add_value(p) + item.add_value(pwp) + item.add_value(b) + item.add_value(bwp) + item.add_value(t) + item.add_value(twp) + item.add_value(e) + item.add_value(ewp) + item.add_value(ep) + item.add_value(epwp) + + item.add_value(ip) + item.add_value(ipwp) + item.add_value(ib) + item.add_value(ibwp) + item.add_value(it) + item.add_value(itwp) + item.add_value(ie) + item.add_value(iewp) + item.add_value(iep) + item.add_value(iepwp) + + item.add_value("string") + item.add_value(17.5) + + values = [ str(v) for v in item.each_value() ] + + self.assertEqual(values, [ + 'polygon: (0.5,1;0.5,3;2,3;2,1)', 'polygon: (0.5,1;0.5,3;2,3;2,1)', + 'box: (0,0;1,2)', 'box: (0,0;1,2)', + "label: ('text',r0 0,0)", "label: ('text',r0 0,0)", + 'edge: (0,0;1,2)', 'edge: (0,0;1,2)', + 'edge-pair: (0,0;1,2)/(10,10;11,12)', 'edge-pair: (0,0;1,2)/(10,10;11,12)', + 'polygon: (0,1;0,3;2,3;2,1)', 'polygon: (0,1;0,3;2,3;2,1)', + 'box: (0,0;1,2)', 'box: (0,0;1,2)', + "label: ('text',r0 0,0)", "label: ('text',r0 0,0)", + 'edge: (0,0;1,2)', 'edge: (0,0;1,2)', + 'edge-pair: (0,0;1,2)/(10,10;11,12)', 'edge-pair: (0,0;1,2)/(10,10;11,12)', + 'text: string', + 'float: 17.5' + ]) + # run unit tests if __name__ == '__main__': suite = unittest.TestLoader().loadTestsFromTestCase(RDB_TestClass) diff --git a/testdata/ruby/rdbTest.rb b/testdata/ruby/rdbTest.rb index 392e0cfa4..91ecb9bf2 100644 --- a/testdata/ruby/rdbTest.rb +++ b/testdata/ruby/rdbTest.rb @@ -1131,6 +1131,80 @@ class RDB_TestClass < TestBase end + def test_15 + + p = RBA::DPolygon::new(RBA::DBox::new(0.5, 1, 2, 3)) + pwp = RBA::DPolygonWithProperties::new(p, { 1 => "value" }) + e = RBA::DEdge::new(RBA::DPoint::new(0, 0), RBA::DPoint::new(1, 2)) + ewp = RBA::DEdgeWithProperties::new(e, { 1 => "value" }) + ep = RBA::DEdgePair::new(e, e.moved(10, 10)) + epwp = RBA::DEdgePairWithProperties::new(ep, { 1 => "value" }) + t = RBA::DText::new("text", RBA::DTrans::R0) + twp = RBA::DTextWithProperties::new(t, { 1 => "value" }) + b = RBA::DBox::new(0, 0, 1, 2) + bwp = RBA::DBoxWithProperties::new(b, { 1 => "value" }) + + ip = RBA::Polygon::new(RBA::Box::new(0, 1, 2, 3)) + ipwp = RBA::PolygonWithProperties::new(ip, { 1 => "value" }) + ie = RBA::Edge::new(RBA::Point::new(0, 0), RBA::Point::new(1, 2)) + iewp = RBA::EdgeWithProperties::new(ie, { 1 => "value" }) + iep = RBA::EdgePair::new(ie, ie.moved(10, 10)) + iepwp = RBA::EdgePairWithProperties::new(iep, { 1 => "value" }) + it = RBA::Text::new("text", RBA::Trans::R0) + itwp = RBA::TextWithProperties::new(it, { 1 => "value" }) + ib = RBA::Box::new(0, 0, 1, 2) + ibwp = RBA::BoxWithProperties::new(ib, { 1 => "value" }) + + rdb = RBA::ReportDatabase::new + + cat = rdb.create_category("name") + cell = rdb.create_cell("TOP") + item = rdb.create_item(cell, cat) + + item.add_value(p) + item.add_value(pwp) + item.add_value(b) + item.add_value(bwp) + item.add_value(t) + item.add_value(twp) + item.add_value(e) + item.add_value(ewp) + item.add_value(ep) + item.add_value(epwp) + + item.add_value(ip) + item.add_value(ipwp) + item.add_value(ib) + item.add_value(ibwp) + item.add_value(it) + item.add_value(itwp) + item.add_value(ie) + item.add_value(iewp) + item.add_value(iep) + item.add_value(iepwp) + + item.add_value("string") + item.add_value(17.5) + + values = item.each_value.collect(&:to_s) + + assert_equal(values, [ + 'polygon: (0.5,1;0.5,3;2,3;2,1)', 'polygon: (0.5,1;0.5,3;2,3;2,1)', + 'box: (0,0;1,2)', 'box: (0,0;1,2)', + "label: ('text',r0 0,0)", "label: ('text',r0 0,0)", + 'edge: (0,0;1,2)', 'edge: (0,0;1,2)', + 'edge-pair: (0,0;1,2)/(10,10;11,12)', 'edge-pair: (0,0;1,2)/(10,10;11,12)', + 'polygon: (0,1;0,3;2,3;2,1)', 'polygon: (0,1;0,3;2,3;2,1)', + 'box: (0,0;1,2)', 'box: (0,0;1,2)', + "label: ('text',r0 0,0)", "label: ('text',r0 0,0)", + 'edge: (0,0;1,2)', 'edge: (0,0;1,2)', + 'edge-pair: (0,0;1,2)/(10,10;11,12)', 'edge-pair: (0,0;1,2)/(10,10;11,12)', + 'text: string', + 'float: 17.5' + ]) + + end + end load("test_epilogue.rb") From a24d5388d7db6101d2fcd5c8a8b55722b56c0317 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 26 Mar 2025 00:56:54 +0100 Subject: [PATCH 109/392] Region[] now returns a PolygonWithProperties object or nil --- src/db/db/gsiDeclDbRegion.cc | 16 ++++++++++++++-- src/gsi/unit_tests/gsiExpressionTests.cc | 1 - testdata/ruby/dbRegionTest.rb | 6 +++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index 11b80b936..53da87933 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -1379,6 +1379,16 @@ rasterize1 (const db::Region *region, const db::Point &origin, const db::Vector return rasterize2 (region, origin, pixel_size, pixel_size, nx, ny); } +static tl::Variant nth (const db::Region *region, size_t n) +{ + const db::Polygon *poly = region->nth (n); + if (! poly) { + return tl::Variant (); + } else { + return tl::Variant (db::PolygonWithProperties (*poly, region->nth_prop_id (n))); + } +} + static db::generic_shape_iterator begin_region (const db::Region *region) { return db::generic_shape_iterator (db::make_wp_iter (region->delegate ()->begin ())); @@ -4103,14 +4113,16 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "This returns the raw polygons if merged semantics is disabled or the merged ones if merged semantics is enabled.\n" "Starting with version 0.30, the iterator delivers a RegionWithProperties object." ) + - method ("[]", &db::Region::nth, gsi::arg ("n"), + method_ext ("[]", &nth, gsi::arg ("n"), "@brief Returns the nth polygon of the region\n" "\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" - "The \\each iterator is the more general approach to access the polygons." + "The \\each iterator is the more general approach to access the polygons.\n" + "\n" + "Since version 0.30.1, this method returns a \\PolygonWithProperties object." ) + method ("flatten", &db::Region::flatten, "@brief Explicitly flattens a region\n" diff --git a/src/gsi/unit_tests/gsiExpressionTests.cc b/src/gsi/unit_tests/gsiExpressionTests.cc index 105383e92..a395a73d9 100644 --- a/src/gsi/unit_tests/gsiExpressionTests.cc +++ b/src/gsi/unit_tests/gsiExpressionTests.cc @@ -898,4 +898,3 @@ TEST(17) "it.add_value(17.5)").execute (); } - diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 5f8588f8b..e92798056 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -226,10 +226,14 @@ class DBRegion_TestClass < TestBase r.flatten assert_equal(r.has_valid_polygons?, true) - assert_equal(r[1].to_s, "(-10,80;-10,120;10,120;10,80)") + assert_equal(r[1].to_s, "(-10,80;-10,120;10,120;10,80) props={}") assert_equal(r[4].to_s, "") assert_equal(r.bbox.to_s, "(-10,-20;210,120)") assert_equal(r.is_merged?, false) + + r = RBA::Region::new + r.insert(RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 10, 20), { 1 => 'value' })) + assert_equal(r[0].to_s, "(0,0;0,20;10,20;10,0) props={1=>value}") r = RBA::Region::new(ly.begin_shapes(c1.cell_index, l2), "*") assert_equal(csort(r.to_s), csort("(-11,-21;-11,-19;-9,-19;-9,-21);(9,19;9,21;11,21;11,19);(-11,79;-11,81;-9,81;-9,79);(9,119;9,121;11,121;11,119);(189,79;189,81;191,81;191,79);(209,119;209,121;211,121;211,119)")) From 3aebf90ecdb79e1feed1583ea0dff49f35e6cf14 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 26 Mar 2025 01:22:54 +0100 Subject: [PATCH 110/392] Texts[n] now also provides a TextWithProperties object. Bugfix: this method was not delivering any objects with properties at all. --- src/db/db/dbDeepTexts.cc | 6 +++++ src/db/db/dbDeepTexts.h | 1 + src/db/db/dbEmptyTexts.h | 1 + src/db/db/dbFlatTexts.cc | 41 ++++++++++++++++++++++++++++++- src/db/db/dbFlatTexts.h | 1 + src/db/db/dbOriginalLayerTexts.cc | 6 +++++ src/db/db/dbOriginalLayerTexts.h | 1 + src/db/db/dbTexts.h | 13 +++++++++- src/db/db/dbTextsDelegate.h | 1 + src/db/db/gsiDeclDbEdgePairs.cc | 17 +++++++++++-- src/db/db/gsiDeclDbEdges.cc | 17 +++++++++++-- src/db/db/gsiDeclDbTexts.cc | 16 ++++++++++-- src/db/unit_tests/dbTextsTests.cc | 19 ++++++++++++++ testdata/ruby/dbRegionTest.rb | 4 ++- testdata/ruby/dbTextsTest.rb | 8 ++++-- 15 files changed, 141 insertions(+), 11 deletions(-) diff --git a/src/db/db/dbDeepTexts.cc b/src/db/db/dbDeepTexts.cc index e272e25ca..d15bb3601 100644 --- a/src/db/db/dbDeepTexts.cc +++ b/src/db/db/dbDeepTexts.cc @@ -332,6 +332,12 @@ const db::Text *DeepTexts::nth (size_t) const throw tl::Exception (tl::to_string (tr ("Random access to texts is available only for flat text collections"))); } +db::properties_id_type +DeepTexts::nth_prop_id (size_t) const +{ + throw tl::Exception (tl::to_string (tr ("Random access to texts is available only for flat collections"))); +} + bool DeepTexts::has_valid_texts () const { return false; diff --git a/src/db/db/dbDeepTexts.h b/src/db/db/dbDeepTexts.h index 7425df7cd..0c9a6eabb 100644 --- a/src/db/db/dbDeepTexts.h +++ b/src/db/db/dbDeepTexts.h @@ -71,6 +71,7 @@ public: virtual Box bbox () const; virtual bool empty () const; virtual const db::Text *nth (size_t n) const; + virtual db::properties_id_type nth_prop_id (size_t n) const; virtual bool has_valid_texts () const; virtual const db::RecursiveShapeIterator *iter () const; virtual void apply_property_translator (const db::PropertiesTranslator &pt); diff --git a/src/db/db/dbEmptyTexts.h b/src/db/db/dbEmptyTexts.h index 37cdd6725..202a72399 100644 --- a/src/db/db/dbEmptyTexts.h +++ b/src/db/db/dbEmptyTexts.h @@ -71,6 +71,7 @@ public: virtual TextsDelegate *in (const Texts &, bool) const { return new EmptyTexts (); } virtual const db::Text *nth (size_t) const { tl_assert (false); } + virtual db::properties_id_type nth_prop_id (size_t) const { tl_assert (false); } virtual bool has_valid_texts () const { return true; } virtual const db::RecursiveShapeIterator *iter () const { return 0; } diff --git a/src/db/db/dbFlatTexts.cc b/src/db/db/dbFlatTexts.cc index e2e0e54a2..ef6dda20d 100644 --- a/src/db/db/dbFlatTexts.cc +++ b/src/db/db/dbFlatTexts.cc @@ -171,7 +171,46 @@ TextsDelegate *FlatTexts::add_in_place (const Texts &other) const db::Text *FlatTexts::nth (size_t n) const { - return n < mp_texts->size () ? &mp_texts->get_layer ().begin () [n] : 0; + // NOTE: this assumes that we iterate over non-property texts first and then over texts with properties + + if (n >= mp_texts->size ()) { + return 0; + } + + const db::layer &l = mp_texts->get_layer (); + if (n < l.size ()) { + return &l.begin () [n]; + } + n -= l.size (); + + const db::layer &lp = mp_texts->get_layer (); + if (n < lp.size ()) { + return &lp.begin () [n]; + } + + return 0; +} + +db::properties_id_type FlatTexts::nth_prop_id (size_t n) const +{ + // NOTE: this assumes that we iterate over non-property polygons first and then over polygons with properties + + if (n >= mp_texts->size ()) { + return 0; + } + + const db::layer &l = mp_texts->get_layer (); + if (n < l.size ()) { + return 0; + } + n -= l.size (); + + const db::layer &lp = mp_texts->get_layer (); + if (n < lp.size ()) { + return lp.begin () [n].properties_id (); + } + + return 0; } bool FlatTexts::has_valid_texts () const diff --git a/src/db/db/dbFlatTexts.h b/src/db/db/dbFlatTexts.h index f7e5ff16c..553dd58d5 100644 --- a/src/db/db/dbFlatTexts.h +++ b/src/db/db/dbFlatTexts.h @@ -76,6 +76,7 @@ public: virtual TextsDelegate *add (const Texts &other) const; virtual const db::Text *nth (size_t n) const; + virtual db::properties_id_type nth_prop_id (size_t n) const; virtual bool has_valid_texts () const; virtual const db::RecursiveShapeIterator *iter () const; diff --git a/src/db/db/dbOriginalLayerTexts.cc b/src/db/db/dbOriginalLayerTexts.cc index e4f53eb27..af633ebe5 100644 --- a/src/db/db/dbOriginalLayerTexts.cc +++ b/src/db/db/dbOriginalLayerTexts.cc @@ -190,6 +190,12 @@ OriginalLayerTexts::nth (size_t) const throw tl::Exception (tl::to_string (tr ("Random access to texts is available only for flat collections"))); } +db::properties_id_type +OriginalLayerTexts::nth_prop_id (size_t) const +{ + throw tl::Exception (tl::to_string (tr ("Random access to texts is available only for flat collections"))); +} + bool OriginalLayerTexts::has_valid_texts () const { diff --git a/src/db/db/dbOriginalLayerTexts.h b/src/db/db/dbOriginalLayerTexts.h index d294e2803..81971bd44 100644 --- a/src/db/db/dbOriginalLayerTexts.h +++ b/src/db/db/dbOriginalLayerTexts.h @@ -53,6 +53,7 @@ public: virtual bool empty () const; virtual const db::Text *nth (size_t n) const; + virtual db::properties_id_type nth_prop_id (size_t n) const; virtual bool has_valid_texts () const; virtual const db::RecursiveShapeIterator *iter () const; diff --git a/src/db/db/dbTexts.h b/src/db/db/dbTexts.h index a948f3366..51b05a5d6 100644 --- a/src/db/db/dbTexts.h +++ b/src/db/db/dbTexts.h @@ -495,7 +495,7 @@ public: /** * @brief Returns the nth text * - * This operation is available only for flat regions - i.e. such for which + * This operation is available only for flat text collections - i.e. such for which * "has_valid_texts" is true. */ const db::Text *nth (size_t n) const @@ -503,6 +503,17 @@ public: return mp_delegate->nth (n); } + /** + * @brief Returns the nth text's property ID + * + * This operation is available only for flat text collections - i.e. such for which + * "has_valid_texts" is true. + */ + db::properties_id_type nth_prop_id (size_t n) const + { + return mp_delegate->nth_prop_id (n); + } + /** * @brief Forces flattening of the text collection * diff --git a/src/db/db/dbTextsDelegate.h b/src/db/db/dbTextsDelegate.h index 4808e9679..895b1808e 100644 --- a/src/db/db/dbTextsDelegate.h +++ b/src/db/db/dbTextsDelegate.h @@ -109,6 +109,7 @@ public: virtual TextsDelegate *in (const Texts &other, bool invert) const = 0; virtual const db::Text *nth (size_t n) const = 0; + virtual db::properties_id_type nth_prop_id (size_t n) const = 0; virtual bool has_valid_texts () const = 0; virtual const db::RecursiveShapeIterator *iter () const = 0; diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 2a50e6747..282c917a8 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -783,6 +783,17 @@ static std::vector split_with_area2 (const db::EdgePairs *r, db:: return as_2edge_pairs_vector (r->split_filter (f)); } +static tl::Variant nth (const db::EdgePairs *edge_pairs, size_t n) +{ + const db::EdgePair *ep = edge_pairs->nth (n); + if (! ep) { + return tl::Variant (); + } else { + // @@@ return tl::Variant (db::EdgePairWithProperties (*ep, edge_pairs->nth_prop_id (n))); + return tl::Variant (); // @@@ + } +} + static db::generic_shape_iterator begin_edge_pairs (const db::EdgePairs *edge_pairs) { return db::generic_shape_iterator (db::make_wp_iter (edge_pairs->delegate ()->begin ())); @@ -1855,13 +1866,15 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "Starting with version 0.30, the iterator delivers EdgePairWithProperties objects." ) + - method ("[]", &db::EdgePairs::nth, gsi::arg ("n"), + method_ext ("[]", &nth, gsi::arg ("n"), "@brief Returns the nth edge pair\n" "\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." + "The \\each iterator is the more general approach to access the edge pairs.\n" + "\n" + "Since version 0.30.1, this method returns a \\EdgePairWithProperties object." ) + method ("flatten", &db::EdgePairs::flatten, "@brief Explicitly flattens an edge pair collection\n" diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index d4da722f4..3eb98edc0 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -848,6 +848,17 @@ static std::vector split_interacting_with_region (const db::Edges *r, return as_2edges_vector (r->selected_interacting_differential (other, min_count, max_count)); } +static tl::Variant nth (const db::Edges *edges, size_t n) +{ + const db::Edge *e = edges->nth (n); + if (! e) { + return tl::Variant (); + } else { + // @@@ return tl::Variant (db::EdgeWithProperties (*e, edges->nth_prop_id (n))); + return tl::Variant (); // @@@ + } +} + static db::generic_shape_iterator begin_edges (const db::Edges *edges) { return db::generic_shape_iterator (db::make_wp_iter (edges->delegate ()->begin ())); @@ -2439,14 +2450,16 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "This method has been introduced in version 0.25." "Starting with version 0.30, the iterator delivers an EdgeWithProperties object." ) + - method ("[]", &db::Edges::nth, gsi::arg ("n"), + method_ext ("[]", &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. 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." + "The \\each iterator is the more general approach to access the edges.\n" + "\n" + "Since version 0.30.1, this method returns an \\EdgeWithProperties object." ) + method ("flatten", &db::Edges::flatten, "@brief Explicitly flattens an edge collection\n" diff --git a/src/db/db/gsiDeclDbTexts.cc b/src/db/db/gsiDeclDbTexts.cc index 66e1d34a6..17a9563c9 100644 --- a/src/db/db/gsiDeclDbTexts.cc +++ b/src/db/db/gsiDeclDbTexts.cc @@ -455,6 +455,16 @@ static db::Region pull_interacting (const db::Texts *r, const db::Region &other) return out; } +static tl::Variant nth (const db::Texts *texts, size_t n) +{ + const db::Text *t = texts->nth (n); + if (! t) { + return tl::Variant (); + } else { + return tl::Variant (db::TextWithProperties (*t, texts->nth_prop_id (n))); + } +} + static db::generic_shape_iterator begin_texts (const db::Texts *texts) { return db::generic_shape_iterator (db::make_wp_iter (texts->delegate ()->begin ())); @@ -846,13 +856,15 @@ Class decl_Texts (decl_dbShapeCollection, "db", "Texts", "\n" "Starting with version 0.30, the iterator delivers TextWithProperties objects." ) + - method ("[]", &db::Texts::nth, gsi::arg ("n"), + method_ext ("[]", &nth, gsi::arg ("n"), "@brief Returns the nth text\n" "\n" "This method returns nil if the index is out of range. It is available for flat texts only - i.e. " "those for which \\has_valid_texts? is true. Use \\flatten to explicitly flatten an text collection.\n" "\n" - "The \\each iterator is the more general approach to access the texts." + "The \\each iterator is the more general approach to access the texts.\n" + "\n" + "Since version 0.30.1, this method returns a \\TextWithProperties object." ) + method ("flatten", &db::Texts::flatten, "@brief Explicitly flattens an text collection\n" diff --git a/src/db/unit_tests/dbTextsTests.cc b/src/db/unit_tests/dbTextsTests.cc index 2fc3e652b..e7666647e 100644 --- a/src/db/unit_tests/dbTextsTests.cc +++ b/src/db/unit_tests/dbTextsTests.cc @@ -307,3 +307,22 @@ TEST(9_polygons) EXPECT_EQ (r.to_string (), "(9,19;9,21;11,21;11,19){17=>ABC};(-11,-21;-11,-19;-9,-19;-9,-21){17=>XZY}"); } +TEST(10_properties) +{ + db::PropertiesSet ps; + + ps.insert (tl::Variant ("id"), 1); + db::properties_id_type pid1 = db::properties_id (ps); + + db::Texts texts; + texts.insert (db::TextWithProperties (db::Text ("string", db::Trans ()), pid1)); + texts.insert (db::Text ("abc", db::Trans ())); + + EXPECT_EQ (texts.nth (0)->to_string (), "('abc',r0 0,0)"); + EXPECT_EQ (texts.nth (1)->to_string (), "('string',r0 0,0)"); + EXPECT_EQ (texts.nth (2) == 0, true); + + EXPECT_EQ (texts.nth_prop_id (0), db::properties_id_type (0)); + EXPECT_EQ (texts.nth_prop_id (1), pid1); +} + diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index e92798056..9ccdadf82 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -233,7 +233,9 @@ class DBRegion_TestClass < TestBase r = RBA::Region::new r.insert(RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 10, 20), { 1 => 'value' })) - assert_equal(r[0].to_s, "(0,0;0,20;10,20;10,0) props={1=>value}") + r.insert(RBA::Box::new(1, 2, 11, 22)) + assert_equal(r[0].to_s, "@@@") + assert_equal(r[1].to_s, "(0,0;0,20;10,20;10,0) props={1=>value}") r = RBA::Region::new(ly.begin_shapes(c1.cell_index, l2), "*") assert_equal(csort(r.to_s), csort("(-11,-21;-11,-19;-9,-19;-9,-21);(9,19;9,21;11,21;11,19);(-11,79;-11,81;-9,81;-9,79);(9,119;9,121;11,121;11,119);(189,79;189,81;191,81;191,79);(209,119;209,121;211,121;211,119)")) diff --git a/testdata/ruby/dbTextsTest.rb b/testdata/ruby/dbTextsTest.rb index 145379068..4f481ef3f 100644 --- a/testdata/ruby/dbTextsTest.rb +++ b/testdata/ruby/dbTextsTest.rb @@ -103,7 +103,7 @@ class DBTexts_TestClass < TestBase assert_equal(r.is_empty?, false) assert_equal(r.count, 1) assert_equal(r.hier_count, 1) - assert_equal(r[0].to_s, "('uvw',r0 110,210)") + assert_equal(r[0].to_s, "('uvw',r0 110,210) props={}") assert_equal(r[1].to_s, "") assert_equal(r.bbox.to_s, "(110,210;110,210)") @@ -223,9 +223,13 @@ class DBTexts_TestClass < TestBase r.flatten assert_equal(r.has_valid_texts?, true) - assert_equal(r[1].to_s, "('abc',r0 100,-100)") + assert_equal(r[1].to_s, "('abc',r0 100,-100) props={}") assert_equal(r[100].inspect, "nil") assert_equal(r.bbox.to_s, "(100,-200;300,-100)") + + r = RBA::Texts::new + r.insert(RBA::TextWithProperties::new(RBA::Text::new("string", RBA::Trans::new), { 1 => "value" })) + assert_equal(r[0].to_s, "('string',r0 0,0) props={1=>value}") dss = RBA::DeepShapeStore::new r = RBA::Texts::new(ly.begin_shapes(c1.cell_index, l1), dss) From 40a0113ce529ebf5ed68e7a90f9613f35e1acec2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 27 Mar 2025 00:27:53 +0100 Subject: [PATCH 111/392] Fixing Texts[], EdgePairs[] and Edges[] operators - both in terms of returning xWithProperties and objects with properties at all --- src/db/db/dbDeepEdgePairs.cc | 5 ++++ src/db/db/dbDeepEdgePairs.h | 1 + src/db/db/dbDeepEdges.cc | 6 ++++ src/db/db/dbDeepEdges.h | 1 + src/db/db/dbEdgePairs.h | 13 ++++++++- src/db/db/dbEdgePairsDelegate.h | 1 + src/db/db/dbEdges.h | 13 ++++++++- src/db/db/dbEdgesDelegate.h | 1 + src/db/db/dbEmptyEdgePairs.h | 1 + src/db/db/dbEmptyEdges.h | 1 + src/db/db/dbFlatEdgePairs.cc | 41 ++++++++++++++++++++++++++- src/db/db/dbFlatEdgePairs.h | 1 + src/db/db/dbFlatEdges.cc | 41 ++++++++++++++++++++++++++- src/db/db/dbFlatEdges.h | 1 + src/db/db/dbOriginalLayerEdgePairs.cc | 6 ++++ src/db/db/dbOriginalLayerEdgePairs.h | 1 + src/db/db/dbOriginalLayerEdges.cc | 6 ++++ src/db/db/dbOriginalLayerEdges.h | 1 + src/db/db/gsiDeclDbEdgePairs.cc | 3 +- src/db/db/gsiDeclDbEdges.cc | 3 +- src/db/unit_tests/dbEdgePairsTests.cc | 21 ++++++++++++++ src/db/unit_tests/dbEdgesTests.cc | 19 +++++++++++++ testdata/ruby/dbEdgePairsTest.rb | 5 ++-- testdata/ruby/dbEdgesTest.rb | 7 +++-- 24 files changed, 186 insertions(+), 13 deletions(-) diff --git a/src/db/db/dbDeepEdgePairs.cc b/src/db/db/dbDeepEdgePairs.cc index 7593067c9..41938c618 100644 --- a/src/db/db/dbDeepEdgePairs.cc +++ b/src/db/db/dbDeepEdgePairs.cc @@ -311,6 +311,11 @@ 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"))); } +db::properties_id_type DeepEdgePairs::nth_prop_id (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; diff --git a/src/db/db/dbDeepEdgePairs.h b/src/db/db/dbDeepEdgePairs.h index c55e7f97f..7ed036d6e 100644 --- a/src/db/db/dbDeepEdgePairs.h +++ b/src/db/db/dbDeepEdgePairs.h @@ -70,6 +70,7 @@ public: virtual Box bbox () const; virtual bool empty () const; virtual const db::EdgePair *nth (size_t n) const; + virtual db::properties_id_type nth_prop_id (size_t n) const; virtual bool has_valid_edge_pairs () const; virtual const db::RecursiveShapeIterator *iter () const; virtual void apply_property_translator (const db::PropertiesTranslator &pt); diff --git a/src/db/db/dbDeepEdges.cc b/src/db/db/dbDeepEdges.cc index bd80f7ac9..3872386fe 100644 --- a/src/db/db/dbDeepEdges.cc +++ b/src/db/db/dbDeepEdges.cc @@ -438,6 +438,12 @@ DeepEdges::nth (size_t /*n*/) const throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat edge collections"))); } +db::properties_id_type +DeepEdges::nth_prop_id (size_t) const +{ + throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat collections"))); +} + bool DeepEdges::has_valid_edges () const { diff --git a/src/db/db/dbDeepEdges.h b/src/db/db/dbDeepEdges.h index 6af2bf55a..b8cf9fc2e 100644 --- a/src/db/db/dbDeepEdges.h +++ b/src/db/db/dbDeepEdges.h @@ -76,6 +76,7 @@ public: virtual bool is_merged () const; virtual const db::Edge *nth (size_t n) const; + virtual db::properties_id_type nth_prop_id (size_t n) const; virtual bool has_valid_edges () const; virtual bool has_valid_merged_edges () const; diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index 271777473..0cffe8960 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -678,7 +678,7 @@ public: /** * @brief Returns the nth edge pair * - * This operation is available only for flat regions - i.e. such for which + * This operation is available only for flat edge pair collections - i.e. such for which * "has_valid_edge_pairs" is true. */ const db::EdgePair *nth (size_t n) const @@ -686,6 +686,17 @@ public: return mp_delegate->nth (n); } + /** + * @brief Returns the nth edge pair's property ID + * + * This operation is available only for flat edge pair collections - i.e. such for which + * "has_valid_edge_pairs" is true. + */ + db::properties_id_type nth_prop_id (size_t n) const + { + return mp_delegate->nth_prop_id (n); + } + /** * @brief Forces flattening of the edge pair collection * diff --git a/src/db/db/dbEdgePairsDelegate.h b/src/db/db/dbEdgePairsDelegate.h index d8dd18849..d4a1efd97 100644 --- a/src/db/db/dbEdgePairsDelegate.h +++ b/src/db/db/dbEdgePairsDelegate.h @@ -273,6 +273,7 @@ public: virtual EdgePairsDelegate *in (const EdgePairs &other, bool invert) const = 0; virtual const db::EdgePair *nth (size_t n) const = 0; + virtual db::properties_id_type nth_prop_id (size_t n) const = 0; virtual bool has_valid_edge_pairs () const = 0; virtual const db::RecursiveShapeIterator *iter () const = 0; diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index 6b507048a..31bdc35af 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -1440,13 +1440,24 @@ public: /** * @brief Returns the nth edge * - * This operation is available only for flat regions - i.e. such for which "has_valid_edges" is true. + * This operation is available only for flat edge collections - i.e. such for which "has_valid_edges" is true. */ const db::Edge *nth (size_t n) const { return mp_delegate->nth (n); } + /** + * @brief Returns the nth edge's property ID + * + * This operation is available only for flat edge collections - i.e. such for which + * "has_valid_edges" is true. + */ + db::properties_id_type nth_prop_id (size_t n) const + { + return mp_delegate->nth_prop_id (n); + } + /** * @brief Forces flattening of the edge collection * diff --git a/src/db/db/dbEdgesDelegate.h b/src/db/db/dbEdgesDelegate.h index 496b86931..91aea92ee 100644 --- a/src/db/db/dbEdgesDelegate.h +++ b/src/db/db/dbEdgesDelegate.h @@ -280,6 +280,7 @@ public: virtual std::pair in_and_out (const Edges &) const = 0; virtual const db::Edge *nth (size_t n) const = 0; + virtual db::properties_id_type nth_prop_id (size_t n) const = 0; virtual bool has_valid_edges () const = 0; virtual bool has_valid_merged_edges () const = 0; diff --git a/src/db/db/dbEmptyEdgePairs.h b/src/db/db/dbEmptyEdgePairs.h index f63cfd1a8..92650b923 100644 --- a/src/db/db/dbEmptyEdgePairs.h +++ b/src/db/db/dbEmptyEdgePairs.h @@ -89,6 +89,7 @@ public: virtual EdgePairsDelegate *in (const EdgePairs &, bool) const { return new EmptyEdgePairs (); } virtual const db::EdgePair *nth (size_t) const { tl_assert (false); } + virtual db::properties_id_type nth_prop_id (size_t) const { tl_assert (false); } virtual bool has_valid_edge_pairs () const { return true; } virtual const db::RecursiveShapeIterator *iter () const { return 0; } diff --git a/src/db/db/dbEmptyEdges.h b/src/db/db/dbEmptyEdges.h index 84032b448..bf45753cd 100644 --- a/src/db/db/dbEmptyEdges.h +++ b/src/db/db/dbEmptyEdges.h @@ -119,6 +119,7 @@ public: virtual std::pair in_and_out (const Edges &) const { return std::make_pair (new EmptyEdges (), new EmptyEdges ()); } virtual const db::Edge *nth (size_t) const { tl_assert (false); } + virtual db::properties_id_type nth_prop_id (size_t) const { tl_assert (false); } virtual bool has_valid_edges () const { return true; } virtual bool has_valid_merged_edges () const { return true; } diff --git a/src/db/db/dbFlatEdgePairs.cc b/src/db/db/dbFlatEdgePairs.cc index cb554a3ea..a42bce475 100644 --- a/src/db/db/dbFlatEdgePairs.cc +++ b/src/db/db/dbFlatEdgePairs.cc @@ -173,7 +173,46 @@ EdgePairsDelegate *FlatEdgePairs::add_in_place (const EdgePairs &other) const db::EdgePair *FlatEdgePairs::nth (size_t n) const { - return n < mp_edge_pairs->size () ? &mp_edge_pairs->get_layer ().begin () [n] : 0; + // NOTE: this assumes that we iterate over non-property edge pairs first and then over edges with properties + + if (n >= mp_edge_pairs->size ()) { + return 0; + } + + const db::layer &l = mp_edge_pairs->get_layer (); + if (n < l.size ()) { + return &l.begin () [n]; + } + n -= l.size (); + + const db::layer &lp = mp_edge_pairs->get_layer (); + if (n < lp.size ()) { + return &lp.begin () [n]; + } + + return 0; +} + +db::properties_id_type FlatEdgePairs::nth_prop_id (size_t n) const +{ + // NOTE: this assumes that we iterate over non-property edge pairs first and then over edges with properties + + if (n >= mp_edge_pairs->size ()) { + return 0; + } + + const db::layer &l = mp_edge_pairs->get_layer (); + if (n < l.size ()) { + return 0; + } + n -= l.size (); + + const db::layer &lp = mp_edge_pairs->get_layer (); + if (n < lp.size ()) { + return lp.begin () [n].properties_id (); + } + + return 0; } bool FlatEdgePairs::has_valid_edge_pairs () const diff --git a/src/db/db/dbFlatEdgePairs.h b/src/db/db/dbFlatEdgePairs.h index dc38dc088..ce203c4aa 100644 --- a/src/db/db/dbFlatEdgePairs.h +++ b/src/db/db/dbFlatEdgePairs.h @@ -75,6 +75,7 @@ public: virtual EdgePairsDelegate *add (const EdgePairs &other) const; virtual const db::EdgePair *nth (size_t n) const; + virtual db::properties_id_type nth_prop_id (size_t n) const; virtual bool has_valid_edge_pairs () const; virtual const db::RecursiveShapeIterator *iter () const; diff --git a/src/db/db/dbFlatEdges.cc b/src/db/db/dbFlatEdges.cc index 44299bc8c..27e323cae 100644 --- a/src/db/db/dbFlatEdges.cc +++ b/src/db/db/dbFlatEdges.cc @@ -361,7 +361,46 @@ EdgesDelegate *FlatEdges::add_in_place (const Edges &other) const db::Edge *FlatEdges::nth (size_t n) const { - return n < mp_edges->size () ? &mp_edges->get_layer ().begin () [n] : 0; + // NOTE: this assumes that we iterate over non-property edges first and then over edges with properties + + if (n >= mp_edges->size ()) { + return 0; + } + + const db::layer &l = mp_edges->get_layer (); + if (n < l.size ()) { + return &l.begin () [n]; + } + n -= l.size (); + + const db::layer &lp = mp_edges->get_layer (); + if (n < lp.size ()) { + return &lp.begin () [n]; + } + + return 0; +} + +db::properties_id_type FlatEdges::nth_prop_id (size_t n) const +{ + // NOTE: this assumes that we iterate over non-property polygons first and then over polygons with properties + + if (n >= mp_edges->size ()) { + return 0; + } + + const db::layer &l = mp_edges->get_layer (); + if (n < l.size ()) { + return 0; + } + n -= l.size (); + + const db::layer &lp = mp_edges->get_layer (); + if (n < lp.size ()) { + return lp.begin () [n].properties_id (); + } + + return 0; } bool FlatEdges::has_valid_edges () const diff --git a/src/db/db/dbFlatEdges.h b/src/db/db/dbFlatEdges.h index f5464be30..551c33ce4 100644 --- a/src/db/db/dbFlatEdges.h +++ b/src/db/db/dbFlatEdges.h @@ -89,6 +89,7 @@ public: virtual EdgesDelegate *add (const Edges &other) const; virtual const db::Edge *nth (size_t n) const; + virtual db::properties_id_type nth_prop_id (size_t n) const; virtual bool has_valid_edges () const; virtual bool has_valid_merged_edges () const; diff --git a/src/db/db/dbOriginalLayerEdgePairs.cc b/src/db/db/dbOriginalLayerEdgePairs.cc index 301208ebc..5038a1885 100644 --- a/src/db/db/dbOriginalLayerEdgePairs.cc +++ b/src/db/db/dbOriginalLayerEdgePairs.cc @@ -190,6 +190,12 @@ OriginalLayerEdgePairs::nth (size_t) const throw tl::Exception (tl::to_string (tr ("Random access to edge pairs is available only for flat collections"))); } +db::properties_id_type +OriginalLayerEdgePairs::nth_prop_id (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 { diff --git a/src/db/db/dbOriginalLayerEdgePairs.h b/src/db/db/dbOriginalLayerEdgePairs.h index bcecf1f7c..41000da51 100644 --- a/src/db/db/dbOriginalLayerEdgePairs.h +++ b/src/db/db/dbOriginalLayerEdgePairs.h @@ -53,6 +53,7 @@ public: virtual bool empty () const; virtual const db::EdgePair *nth (size_t n) const; + virtual db::properties_id_type nth_prop_id (size_t n) const; virtual bool has_valid_edge_pairs () const; virtual const db::RecursiveShapeIterator *iter () const; diff --git a/src/db/db/dbOriginalLayerEdges.cc b/src/db/db/dbOriginalLayerEdges.cc index a01314308..3484b926e 100644 --- a/src/db/db/dbOriginalLayerEdges.cc +++ b/src/db/db/dbOriginalLayerEdges.cc @@ -233,6 +233,12 @@ OriginalLayerEdges::nth (size_t) const throw tl::Exception (tl::to_string (tr ("Random access to edges is available only for flat collections"))); } +db::properties_id_type +OriginalLayerEdges::nth_prop_id (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 { diff --git a/src/db/db/dbOriginalLayerEdges.h b/src/db/db/dbOriginalLayerEdges.h index 89d6fc647..53254e852 100644 --- a/src/db/db/dbOriginalLayerEdges.h +++ b/src/db/db/dbOriginalLayerEdges.h @@ -58,6 +58,7 @@ public: virtual bool is_merged () const; virtual const db::Edge *nth (size_t n) const; + virtual db::properties_id_type nth_prop_id (size_t n) const; virtual bool has_valid_edges () const; virtual bool has_valid_merged_edges () const; diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 282c917a8..903aba802 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -789,8 +789,7 @@ static tl::Variant nth (const db::EdgePairs *edge_pairs, size_t n) if (! ep) { return tl::Variant (); } else { - // @@@ return tl::Variant (db::EdgePairWithProperties (*ep, edge_pairs->nth_prop_id (n))); - return tl::Variant (); // @@@ + return tl::Variant (db::EdgePairWithProperties (*ep, edge_pairs->nth_prop_id (n))); } } diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index 3eb98edc0..0383d14ac 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -854,8 +854,7 @@ static tl::Variant nth (const db::Edges *edges, size_t n) if (! e) { return tl::Variant (); } else { - // @@@ return tl::Variant (db::EdgeWithProperties (*e, edges->nth_prop_id (n))); - return tl::Variant (); // @@@ + return tl::Variant (db::EdgeWithProperties (*e, edges->nth_prop_id (n))); } } diff --git a/src/db/unit_tests/dbEdgePairsTests.cc b/src/db/unit_tests/dbEdgePairsTests.cc index 1f6153c3d..1a8680fdd 100644 --- a/src/db/unit_tests/dbEdgePairsTests.cc +++ b/src/db/unit_tests/dbEdgePairsTests.cc @@ -279,3 +279,24 @@ TEST(6_add_with_properties) EXPECT_EQ ((ro1 + rf2).to_string (), "(10,20;-20,60)/(10,30;-20,70){net=>17};(-10,20;20,60)/(-10,30;20,70){net=>17}"); } +TEST(7_properties) +{ + db::PropertiesSet ps; + + ps.insert (tl::Variant ("id"), 1); + db::properties_id_type pid1 = db::properties_id (ps); + + db::EdgePairs edge_pairs; + db::Edge e1 (db::Point (0, 0), db::Point (10, 20)); + db::Edge e2 (db::Point (1, 2), db::Point (11, 22)); + edge_pairs.insert (db::EdgePairWithProperties (db::EdgePair (e1, e2), pid1)); + edge_pairs.insert (db::EdgePair (e1, e2)); + + EXPECT_EQ (edge_pairs.nth (0)->to_string (), "(0,0;10,20)/(1,2;11,22)"); + EXPECT_EQ (edge_pairs.nth (1)->to_string (), "(0,0;10,20)/(1,2;11,22)"); + EXPECT_EQ (edge_pairs.nth (2) == 0, true); + + EXPECT_EQ (edge_pairs.nth_prop_id (0), db::properties_id_type (0)); + EXPECT_EQ (edge_pairs.nth_prop_id (1), pid1); +} + diff --git a/src/db/unit_tests/dbEdgesTests.cc b/src/db/unit_tests/dbEdgesTests.cc index 15a4b562b..0db2b9676 100644 --- a/src/db/unit_tests/dbEdgesTests.cc +++ b/src/db/unit_tests/dbEdgesTests.cc @@ -1443,6 +1443,25 @@ TEST(32_add_with_properties) EXPECT_EQ ((ro1 + rf2).to_string (), "(10,20;40,60){net=>17};(-10,20;20,60){net=>17}"); } +TEST(33_properties) +{ + db::PropertiesSet ps; + + ps.insert (tl::Variant ("id"), 1); + db::properties_id_type pid1 = db::properties_id (ps); + + db::Edges edges; + edges.insert (db::EdgeWithProperties (db::Edge (db::Point (0, 0), db::Point (10, 20)), pid1)); + edges.insert (db::Edge (db::Point (0, 0), db::Point (10, 20))); + + EXPECT_EQ (edges.nth (0)->to_string (), "(0,0;10,20)"); + EXPECT_EQ (edges.nth (1)->to_string (), "(0,0;10,20)"); + EXPECT_EQ (edges.nth (2) == 0, true); + + EXPECT_EQ (edges.nth_prop_id (0), db::properties_id_type (0)); + EXPECT_EQ (edges.nth_prop_id (1), pid1); +} + // GitHub issue #72 (Edges/Region NOT issue) TEST(100) { diff --git a/testdata/ruby/dbEdgePairsTest.rb b/testdata/ruby/dbEdgePairsTest.rb index d67bc33c6..ac157ff53 100644 --- a/testdata/ruby/dbEdgePairsTest.rb +++ b/testdata/ruby/dbEdgePairsTest.rb @@ -111,7 +111,7 @@ class DBEdgePairs_TestClass < TestBase assert_equal(csort(r.edges.to_s), csort("(0,0;0,100);(-10,0;-20,50)")) assert_equal(r.is_empty?, false) assert_equal(r.size, 1) - assert_equal(r[0].to_s, "(0,0;0,100)/(-10,0;-20,50)") + assert_equal(r[0].to_s, "(0,0;0,100)/(-10,0;-20,50) props={}") assert_equal(r[1].to_s, "") assert_equal(r.bbox.to_s, "(-20,0;0,100)") @@ -221,7 +221,7 @@ class DBEdgePairs_TestClass < TestBase 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[1].to_s, "(0,101;2,103)/(10,111;12,113) props={}") assert_equal(r[100].inspect, "nil") assert_equal(r.bbox.to_s, "(0,1;212,113)") @@ -622,6 +622,7 @@ class DBEdgePairs_TestClass < TestBase r = RBA::EdgePairs::new([ RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500)), { 1 => "one" }) ]) assert_equal(r.to_s, "(0,0;100,100)/(200,300;200,500){1=>one}") + assert_equal(r[0].to_s, "(0,0;100,100)/(200,300;200,500) props={1=>one}") r = RBA::EdgePairs::new([]) assert_equal(r.to_s, "") diff --git a/testdata/ruby/dbEdgesTest.rb b/testdata/ruby/dbEdgesTest.rb index 086a0eb9c..7a8047b23 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -235,7 +235,7 @@ class DBEdges_TestClass < TestBase assert_equal(r.is_empty?, false) assert_equal(r.count, 12) assert_equal(r.hier_count, 12) - assert_equal(r[1].to_s, "(-10,20;10,20)") + assert_equal(r[1].to_s, "(-10,20;10,20) props={}") assert_equal(r[100].to_s, "") assert_equal(r.bbox.to_s, "(-10,-20;210,120)") assert_equal(r.is_merged?, false) @@ -252,11 +252,11 @@ class DBEdges_TestClass < TestBase r.flatten assert_equal(r.has_valid_edges?, true) - assert_equal(r[1].to_s, "(-10,80;10,120)") + assert_equal(r[1].to_s, "(-10,80;10,120) props={}") assert_equal(r[100].to_s, "") assert_equal(r.bbox.to_s, "(-10,-20;210,120)") assert_equal(r.is_merged?, false) - + r = RBA::Edges::new(ly.begin_shapes(c1.cell_index, l1), RBA::ICplxTrans::new(10, 20), true) assert_equal(r.to_s(30), "(0,0;0,40);(0,40;20,40);(20,40;20,0);(20,0;0,0);(0,100;0,140);(0,140;20,140);(20,140;20,100);(20,100;0,100);(200,100;200,140);(200,140;220,140);(220,140;220,100);(220,100;200,100)") assert_equal(r.is_empty?, false) @@ -970,6 +970,7 @@ class DBEdges_TestClass < TestBase r = RBA::Edges::new([ RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 100), { 1 => "one" }) ]) assert_equal(r.to_s, "(0,0;100,100){1=>one}") + assert_equal(r[0].to_s, "(0,0;100,100) props={1=>one}") r = RBA::Edges::new([]) assert_equal(r.to_s, "") From a38bea30860d2ea096799a0e1df7fe634e68d253 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 29 Mar 2025 19:35:26 +0100 Subject: [PATCH 112/392] Fixed a unit test --- testdata/ruby/dbRegionTest.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 9ccdadf82..985dd9406 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -234,7 +234,7 @@ class DBRegion_TestClass < TestBase r = RBA::Region::new r.insert(RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 10, 20), { 1 => 'value' })) r.insert(RBA::Box::new(1, 2, 11, 22)) - assert_equal(r[0].to_s, "@@@") + assert_equal(r[0].to_s, "(1,2;1,22;11,22;11,2) props={}") assert_equal(r[1].to_s, "(0,0;0,20;10,20;10,0) props={1=>value}") r = RBA::Region::new(ly.begin_shapes(c1.cell_index, l2), "*") From b0f05b5327714b2992a8324b24f5e4f7de8ca067 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 29 Mar 2025 23:18:16 +0100 Subject: [PATCH 113/392] WIP --- src/db/db/db.pro | 2 + src/db/db/dbPolygonGraph.cc | 846 +++++++++++++++++++++++ src/db/db/dbPolygonGraph.h | 777 +++++++++++++++++++++ src/db/unit_tests/dbPolygonGraphTests.cc | 51 ++ src/db/unit_tests/unit_tests.pro | 1 + 5 files changed, 1677 insertions(+) create mode 100644 src/db/db/dbPolygonGraph.cc create mode 100644 src/db/db/dbPolygonGraph.h create mode 100644 src/db/unit_tests/dbPolygonGraphTests.cc diff --git a/src/db/db/db.pro b/src/db/db/db.pro index bbae9671a..732d42aa1 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -76,6 +76,7 @@ SOURCES = \ dbPCellVariant.cc \ dbPoint.cc \ dbPolygon.cc \ + dbPolygonGraph.cc \ dbPolygonNeighborhood.cc \ dbPolygonTools.cc \ dbPolygonGenerators.cc \ @@ -314,6 +315,7 @@ HEADERS = \ dbPCellVariant.h \ dbPoint.h \ dbPolygon.h \ + dbPolygonGraph.h \ dbPolygonNeighborhood.h \ dbPolygonTools.h \ dbPolygonGenerators.h \ diff --git a/src/db/db/dbPolygonGraph.cc b/src/db/db/dbPolygonGraph.cc new file mode 100644 index 000000000..c7ae4a349 --- /dev/null +++ b/src/db/db/dbPolygonGraph.cc @@ -0,0 +1,846 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbPolygonGraph.h" +#include "dbLayout.h" +#include "dbWriter.h" +#include "tlStream.h" +#include "tlLog.h" +#include "tlTimer.h" + +#include +#include +#include +#include + +namespace db +{ + +// ------------------------------------------------------------------------------------- +// GVertex implementation + +GVertex::GVertex () + : DPoint (), m_is_precious (false) +{ + // .. nothing yet .. +} + +GVertex::GVertex (const db::DPoint &p) + : DPoint (p), m_is_precious (false) +{ + // .. nothing yet .. +} + +GVertex::GVertex (const GVertex &v) + : DPoint (), m_is_precious (false) +{ + operator= (v); +} + +GVertex &GVertex::operator= (const GVertex &v) +{ + if (this != &v) { + // NOTE: edges are not copied! + db::DPoint::operator= (v); + m_is_precious = v.m_is_precious; + } + return *this; +} + +GVertex::GVertex (db::DCoord x, db::DCoord y) + : DPoint (x, y), m_is_precious (false) +{ + // .. nothing yet .. +} + +#if 0 // @@@ +bool +GVertex::is_outside () const +{ + for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) { + if ((*e)->is_outside ()) { + return true; + } + } + return false; +} +#endif + +std::vector +GVertex::polygons () const +{ + std::set seen; + std::vector res; + for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) { + for (auto t = (*e)->begin_polygons (); t != (*e)->end_polygons (); ++t) { + if (seen.insert (t.operator-> ()).second) { + res.push_back (t.operator-> ()); + } + } + } + return res; +} + +bool +GVertex::has_edge (const GPolygonEdge *edge) const +{ + for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) { + if (*e == edge) { + return true; + } + } + return false; +} + +size_t +GVertex::num_edges (int max_count) const +{ + if (max_count < 0) { + // NOTE: this can be slow for a std::list, so we have max_count to limit this effort + return mp_edges.size (); + } else { + size_t n = 0; + for (auto i = mp_edges.begin (); i != mp_edges.end () && --max_count >= 0; ++i) { + ++n; + } + return n; + } +} + +std::string +GVertex::to_string (bool with_id) const +{ + std::string res = tl::sprintf ("(%.12g, %.12g)", x (), y()); + if (with_id) { + res += tl::sprintf ("[%x]", (size_t)this); + } + return res; +} + +int +GVertex::in_circle (const DPoint &point, const DPoint ¢er, double radius) +{ + double dx = point.x () - center.x (); + double dy = point.y () - center.y (); + double d2 = dx * dx + dy * dy; + double r2 = radius * radius; + double delta = fabs (d2 + r2) * db::epsilon; + if (d2 < r2 - delta) { + return 1; + } else if (d2 < r2 + delta) { + return 0; + } else { + return -1; + } +} + +// ------------------------------------------------------------------------------------- +// GPolygonEdge implementation + +GPolygonEdge::GPolygonEdge () + : mp_v1 (0), mp_v2 (0), mp_left (), mp_right (), m_level (0), m_id (0), m_is_segment (false) +{ + // .. nothing yet .. +} + +GPolygonEdge::GPolygonEdge (GVertex *v1, GVertex *v2) + : mp_v1 (v1), mp_v2 (v2), mp_left (), mp_right (), m_level (0), m_id (0), m_is_segment (false) +{ + // .. nothing yet .. +} + +void +GPolygonEdge::set_left (GPolygon *t) +{ + mp_left = t; +} + +void +GPolygonEdge::set_right (GPolygon *t) +{ + mp_right = t; +} + +void +GPolygonEdge::link () +{ + mp_v1->mp_edges.push_back (this); + m_ec_v1 = --mp_v1->mp_edges.end (); + + mp_v2->mp_edges.push_back (this); + m_ec_v2 = --mp_v2->mp_edges.end (); +} + +void +GPolygonEdge::unlink () +{ + if (mp_v1) { + mp_v1->remove_edge (m_ec_v1); + } + if (mp_v2) { + mp_v2->remove_edge (m_ec_v2); + } + mp_v1 = mp_v2 = 0; +} + +GPolygon * +GPolygonEdge::other (const GPolygon *t) const +{ + if (t == mp_left) { + return mp_right; + } + if (t == mp_right) { + return mp_left; + } + tl_assert (false); + return 0; +} + +GVertex * +GPolygonEdge::other (const GVertex *t) const +{ + if (t == mp_v1) { + return mp_v2; + } + if (t == mp_v2) { + return mp_v1; + } + tl_assert (false); + return 0; +} + +bool +GPolygonEdge::has_vertex (const GVertex *v) const +{ + return mp_v1 == v || mp_v2 == v; +} + +GVertex * +GPolygonEdge::common_vertex (const GPolygonEdge *other) const +{ + if (has_vertex (other->v1 ())) { + return (other->v1 ()); + } + if (has_vertex (other->v2 ())) { + return (other->v2 ()); + } + return 0; +} + +std::string +GPolygonEdge::to_string (bool with_id) const +{ + std::string res = std::string ("(") + mp_v1->to_string (with_id) + ", " + mp_v2->to_string (with_id) + ")"; + if (with_id) { + res += tl::sprintf ("[%x]", (size_t)this); + } + return res; +} + +double +GPolygonEdge::distance (const db::DEdge &e, const db::DPoint &p) +{ + double l = db::sprod (p - e.p1 (), e.d ()) / e.d ().sq_length (); + db::DPoint pp; + if (l <= 0.0) { + pp = e.p1 (); + } else if (l >= 1.0) { + pp = e.p2 (); + } else { + pp = e.p1 () + e.d () * l; + } + return (p - pp).length (); +} + +bool +GPolygonEdge::crosses (const db::DEdge &e, const db::DEdge &other) +{ + return e.side_of (other.p1 ()) * e.side_of (other.p2 ()) < 0 && + other.side_of (e.p1 ()) * other.side_of (e.p2 ()) < 0; +} + +bool +GPolygonEdge::crosses_including (const db::DEdge &e, const db::DEdge &other) +{ + return e.side_of (other.p1 ()) * e.side_of (other.p2 ()) <= 0 && + other.side_of (e.p1 ()) * other.side_of (e.p2 ()) <= 0; +} + +db::DPoint +GPolygonEdge::intersection_point (const db::DEdge &e, const db::DEdge &other) +{ + return e.intersect_point (other).second; +} + +bool +GPolygonEdge::point_on (const db::DEdge &edge, const db::DPoint &point) +{ + if (edge.side_of (point) != 0) { + return false; + } else { + return db::sprod_sign (point - edge.p1 (), edge.d ()) * db::sprod_sign(point - edge.p2 (), edge.d ()) < 0; + } +} + +#if 0 // @@@ +bool +GPolygonEdge::is_for_outside_polygons () const +{ + return (left () && left ()->is_outside ()) || (right () && right ()->is_outside ()); +} +#endif + +bool +GPolygonEdge::has_polygon (const GPolygon *t) const +{ + return t != 0 && (left () == t || right () == t); +} + +// ------------------------------------------------------------------------------------- +// GPolygon implementation + +GPolygon::GPolygon () + : m_id (0) +{ + // .. nothing yet .. +} + +void +GPolygon::init () +{ + m_id = 0; + + if (mp_e.empty ()) { + return; + } + + std::vector e; + e.swap (mp_e); + + std::multimap v2e; + + for (auto i = e.begin (); i != e.end (); ++i) { + if (i != e.begin ()) { + v2e.insert (std::make_pair ((*i)->v1 (), *i)); + v2e.insert (std::make_pair ((*i)->v2 (), *i)); + } + } + + mp_e.reserve (e.size ()); + mp_e.push_back (e.front ()); + // NOTE: we assume the edges follow the clockwise orientation + mp_e.back ()->set_right (this); + + mp_v.reserve (e.size ()); + mp_v.push_back (mp_e.back ()->v1 ()); + + auto v = mp_e.back ()->v2 (); + + // join the edges in the order of the polygon + while (! v2e.empty ()) { + + mp_v.push_back (v); + + auto i = v2e.find (v); + tl_assert (i != v2e.end () && i->first == v && i->second != mp_e.back ()); + v2e.erase (i); + mp_e.push_back (i->second); + // NOTE: we assume the edges follow the clockwise orientation + mp_e.back ()->set_right (this); + + v = i->second->other (v); + i = v2e.find (v); + while (i != v2e.end () && i->first == v) { + if (i->second == mp_e.back ()) { + v2e.erase (i); + break; + } + ++i; + } + + } +} + +GPolygon::~GPolygon () +{ + unlink (); +} + +void +GPolygon::unlink () +{ + for (auto e = mp_e.begin (); e != mp_e.end (); ++e) { + if ((*e)->left () == this) { + (*e)->set_left (0); + } + if ((*e)->right () == this) { + (*e)->set_right (0); + } + } +} + +std::string +GPolygon::to_string (bool with_id) const +{ + std::string res = "("; + for (int i = 0; i < int (size ()); ++i) { + if (i > 0) { + res += ", "; + } + if (vertex (i)) { + res += vertex (i)->to_string (with_id); + } else { + res += "(null)"; + } + } + res += ")"; + return res; +} + +double +GPolygon::area () const +{ + return fabs (db::vprod (mp_e[0]->d (), mp_e[1]->d ())) * 0.5; +} + +db::DBox +GPolygon::bbox () const +{ + db::DBox box; + for (auto i = mp_v.begin (); i != mp_v.end (); ++i) { + box += **i; + } + return box; +} + +GPolygonEdge * +GPolygon::find_edge_with (const GVertex *v1, const GVertex *v2) const +{ + for (auto e = mp_e.begin (); e != mp_e.end (); ++e) { + if ((*e)->has_vertex (v1) && (*e)->has_vertex (v2)) { + return *e; + } + } + tl_assert (false); +} + +GPolygonEdge * +GPolygon::common_edge (const GPolygon *other) const +{ + for (auto e = mp_e.begin (); e != mp_e.end (); ++e) { + if ((*e)->other (this) == other) { + return *e; + } + } + return 0; +} + +#if 0 // @@@ +int +GPolygon::contains (const db::DPoint &point) const +{ + auto c = *mp_v[2] - *mp_v[0]; + auto b = *mp_v[1] - *mp_v[0]; + + int vps = db::vprod_sign (c, b); + if (vps == 0) { + return db::vprod_sign (point - *mp_v[0], b) == 0 && db::vprod_sign (point - *mp_v[0], c) == 0 ? 0 : -1; + } + + int res = 1; + + const GVertex *vl = mp_v[2]; + for (int i = 0; i < 3; ++i) { + const GVertex *v = mp_v[i]; + int n = db::vprod_sign (point - *vl, *v - *vl) * vps; + if (n < 0) { + return -1; + } else if (n == 0) { + res = 0; + } + vl = v; + } + + return res; +} +#endif + +double +GPolygon::min_edge_length () const +{ + double lmin = mp_e[0]->d ().length (); + for (auto e = mp_e.begin (); e != mp_e.end (); ++e) { + lmin = std::min (lmin, (*e)->d ().length ()); + } + return lmin; +} + +#if 0 // @@@ +double +GPolygon::b () const +{ + double lmin = min_edge_length (); + bool ok = false; + auto cr = circumcircle (&ok); + return ok ? lmin / cr.second : 0.0; +} +#endif + +bool +GPolygon::has_segment () const +{ + for (auto e = mp_e.begin (); e != mp_e.end (); ++e) { + if ((*e)->is_segment ()) { + return true; + } + } + return false; +} + +unsigned int +GPolygon::num_segments () const +{ + unsigned int n = 0; + for (auto e = mp_e.begin (); e != mp_e.end (); ++e) { + if ((*e)->is_segment ()) { + ++n; + } + } + return n; +} + +// ----------------------------------------------------------------------------------- + +static inline bool is_equal (const db::DPoint &a, const db::DPoint &b) +{ + return std::abs (a.x () - b.x ()) < std::max (1.0, (std::abs (a.x ()) + std::abs (b.x ()))) * db::epsilon && + std::abs (a.y () - b.y ()) < std::max (1.0, (std::abs (a.y ()) + std::abs (b.y ()))) * db::epsilon; +} + +PolygonGraph::PolygonGraph () + : m_id (0) + // @@@: m_is_constrained (false), m_level (0), m_id (0), m_flips (0), m_hops (0) +{ + // .. nothing yet .. +} + +PolygonGraph::~PolygonGraph () +{ + clear (); +} + +db::GVertex * +PolygonGraph::create_vertex (double x, double y) +{ + m_vertex_heap.push_back (db::GVertex (x, y)); + return &m_vertex_heap.back (); +} + +db::GVertex * +PolygonGraph::create_vertex (const db::DPoint &pt) +{ + m_vertex_heap.push_back (pt); + return &m_vertex_heap.back (); +} + +db::GPolygonEdge * +PolygonGraph::create_edge (db::GVertex *v1, db::GVertex *v2) +{ + db::GPolygonEdge *edge = 0; + + if (! m_returned_edges.empty ()) { + edge = m_returned_edges.back (); + m_returned_edges.pop_back (); + *edge = db::GPolygonEdge (v1, v2); + } else { + m_edges_heap.push_back (db::GPolygonEdge (v1, v2)); + edge = &m_edges_heap.back (); + } + + edge->link (); + edge->set_id (++m_id); + return edge; +} + +void +PolygonGraph::remove_polygon (db::GPolygon *poly) +{ + std::vector edges; + edges.reserve (poly->size ()); + for (int i = 0; i < int (poly->size ()); ++i) { + edges [i] = poly->edge (i); + } + + delete poly; + + // clean up edges we do no longer need + for (auto e = edges.begin (); e != edges.end (); ++e) { + if ((*e) && (*e)->left () == 0 && (*e)->right () == 0 && (*e)->v1 ()) { + (*e)->unlink (); + m_returned_edges.push_back (*e); + } + } +} + +void +PolygonGraph::insert_polygon (const db::DPolygon &polygon) +{ + if (polygon.begin_edge ().at_end ()) { + return; + } + + std::vector edges; + + for (unsigned int c = 0; c < polygon.holes () + 1; ++c) { + const db::DPolygon::contour_type &ctr = polygon.contour (c); + db::GVertex *v0 = 0, *vv, *v; + for (auto p = ctr.begin (); p != ctr.end (); ++p) { + v = create_vertex ((*p).x (), (*p).y ()); + if (! v0) { + v0 = v; + } else { + edges.push_back (create_edge (vv, v)); + } + vv = v; + } + if (v0 && v0 != v) { + edges.push_back (create_edge (v, v0)); + } + } + + create_polygon (edges.begin (), edges.end ()); +} + +std::string +PolygonGraph::to_string () +{ + std::string res; + for (auto t = mp_polygons.begin (); t != mp_polygons.end (); ++t) { + if (! res.empty ()) { + res += ", "; + } + res += t->to_string (); + } + return res; +} + +db::DBox +PolygonGraph::bbox () const +{ + db::DBox box; + for (auto t = mp_polygons.begin (); t != mp_polygons.end (); ++t) { + box += t->bbox (); + } + return box; +} + +#if 0 // @@@ +bool +PolygonGraph::check (bool check_delaunay) const +{ + bool res = true; + + if (check_delaunay) { + for (auto t = mp_polygons.begin (); t != mp_polygons.end (); ++t) { + auto cp = t->circumcircle (); + auto vi = find_inside_circle (cp.first, cp.second); + if (! vi.empty ()) { + res = false; + tl::error << "(check error) polygon does not meet Delaunay criterion: " << t->to_string (); + for (auto v = vi.begin (); v != vi.end (); ++v) { + tl::error << " vertex inside circumcircle: " << (*v)->to_string (true); + } + } + } + } + + for (auto t = mp_polygons.begin (); t != mp_polygons.end (); ++t) { + for (int i = 0; i < 3; ++i) { + if (! t->edge (i)->has_polygon (t.operator-> ())) { + tl::error << "(check error) edges " << t->edge (i)->to_string (true) + << " attached to polygon " << t->to_string (true) << " does not refer to this polygon"; + res = false; + } + } + } + + for (auto e = m_edges_heap.begin (); e != m_edges_heap.end (); ++e) { + + if (!e->left () && !e->right ()) { + continue; + } + + if (e->left () && e->right ()) { + if (e->left ()->is_outside () != e->right ()->is_outside () && ! e->is_segment ()) { + tl::error << "(check error) edge " << e->to_string (true) << " splits an outside and inside polygon, but is not a segment"; + res = false; + } + } + + for (auto t = e->begin_polygons (); t != e->end_polygons (); ++t) { + if (! t->has_edge (e.operator-> ())) { + tl::error << "(check error) edge " << e->to_string (true) << " not found in adjacent polygon " << t->to_string (true); + res = false; + } + if (! t->has_vertex (e->v1 ())) { + tl::error << "(check error) edges " << e->to_string (true) << " vertex 1 not found in adjacent polygon " << t->to_string (true); + res = false; + } + if (! t->has_vertex (e->v2 ())) { + tl::error << "(check error) edges " << e->to_string (true) << " vertex 2 not found in adjacent polygon " << t->to_string (true); + res = false; + } + db::GVertex *vopp = t->opposite (e.operator-> ()); + double sgn = (e->left () == t.operator-> ()) ? 1.0 : -1.0; + double vp = db::vprod (e->d(), *vopp - *e->v1 ()); // positive if on left side + if (vp * sgn <= 0.0) { + const char * side_str = sgn > 0.0 ? "left" : "right"; + tl::error << "(check error) external point " << vopp->to_string (true) << " not on " << side_str << " side of edge " << e->to_string (true); + res = false; + } + } + + if (! e->v1 ()->has_edge (e.operator-> ())) { + tl::error << "(check error) edge " << e->to_string (true) << " vertex 1 does not list this edge"; + res = false; + } + if (! e->v2 ()->has_edge (e.operator-> ())) { + tl::error << "(check error) edge " << e->to_string (true) << " vertex 2 does not list this edge"; + res = false; + } + + } + + for (auto v = m_vertex_heap.begin (); v != m_vertex_heap.end (); ++v) { + unsigned int num_outside_edges = 0; + for (auto e = v->begin_edges (); e != v->end_edges (); ++e) { + if ((*e)->is_outside ()) { + ++num_outside_edges; + } + } + if (num_outside_edges > 0 && num_outside_edges != 2) { + tl::error << "(check error) vertex " << v->to_string (true) << " has " << num_outside_edges << " outside edges (can only be 2)"; + res = false; + for (auto e = v->begin_edges (); e != v->end_edges (); ++e) { + if ((*e)->is_outside ()) { + tl::error << " Outside edge is " << (*e)->to_string (true); + } + } + } + } + + return res; +} +#endif + +db::Layout * +PolygonGraph::to_layout (bool decompose_by_id) const +{ + db::Layout *layout = new db::Layout (); + layout->dbu (0.001); + + auto dbu_trans = db::CplxTrans (layout->dbu ()).inverted (); + + db::Cell &top = layout->cell (layout->add_cell ("DUMP")); + unsigned int l1 = layout->insert_layer (db::LayerProperties (1, 0)); + // @@@ unsigned int l2 = layout->insert_layer (db::LayerProperties (2, 0)); + unsigned int l10 = layout->insert_layer (db::LayerProperties (10, 0)); + unsigned int l20 = layout->insert_layer (db::LayerProperties (20, 0)); + unsigned int l21 = layout->insert_layer (db::LayerProperties (21, 0)); + unsigned int l22 = layout->insert_layer (db::LayerProperties (22, 0)); + + std::vector pts; + for (auto t = mp_polygons.begin (); t != mp_polygons.end (); ++t) { + pts.clear (); + for (int i = 0; i < int (t->size ()); ++i) { + pts.push_back (*t->vertex (i)); + } + db::DPolygon poly; + poly.assign_hull (pts.begin (), pts.end ()); + top.shapes (/*@@@t->is_outside () ? l2 :*/ l1).insert (dbu_trans * poly); + if (decompose_by_id) { + if ((t->id () & 1) != 0) { + top.shapes (l20).insert (dbu_trans * poly); + } + if ((t->id () & 2) != 0) { + top.shapes (l21).insert (dbu_trans * poly); + } + if ((t->id () & 4) != 0) { + top.shapes (l22).insert (dbu_trans * poly); + } + } + } + + for (auto e = m_edges_heap.begin (); e != m_edges_heap.end (); ++e) { + if ((e->left () || e->right ()) && e->is_segment ()) { + top.shapes (l10).insert (dbu_trans * e->edge ()); + } + } + + return layout; +} + +void +PolygonGraph::dump (const std::string &path, bool decompose_by_id) const +{ + std::unique_ptr ly (to_layout (decompose_by_id)); + + tl::OutputStream stream (path); + + db::SaveLayoutOptions opt; + db::Writer writer (opt); + writer.write (*ly, stream); + + tl::info << "PolygonGraph written to " << path; +} + +void +PolygonGraph::clear () +{ + mp_polygons.clear (); + m_edges_heap.clear (); + m_vertex_heap.clear (); + m_returned_edges.clear (); + // @@@m_is_constrained = false; + // @@@m_level = 0; + m_id = 0; +} + +template +void +PolygonGraph::make_contours (const Poly &poly, const Trans &trans, std::vector > &edge_contours) +{ + edge_contours.push_back (std::vector ()); + for (auto pt = poly.begin_hull (); pt != poly.end_hull (); ++pt) { + edge_contours.back ().push_back (insert_point (trans * *pt)); + } + + for (unsigned int h = 0; h < poly.holes (); ++h) { + edge_contours.push_back (std::vector ()); + for (auto pt = poly.begin_hole (h); pt != poly.end_hole (h); ++pt) { + edge_contours.back ().push_back (insert_point (trans * *pt)); + } + } +} + +} diff --git a/src/db/db/dbPolygonGraph.h b/src/db/db/dbPolygonGraph.h new file mode 100644 index 000000000..cfbdee450 --- /dev/null +++ b/src/db/db/dbPolygonGraph.h @@ -0,0 +1,777 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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_dbPolygonGraph +#define HDR_dbPolygonGraph + +#include "dbCommon.h" +#include "dbTriangle.h" +#include "dbBox.h" +#include "dbRegion.h" + +#include "tlObjectCollection.h" +#include "tlStableVector.h" + +#include +#include +#include +#include + +namespace db +{ + +class Layout; + +class GPolygon; +class GPolygonEdge; + +/** + * @brief A class representing a vertex in a Delaunay triangulation graph + * + * The vertex carries information about the connected edges and + * an integer value that can be used in traversal algorithms + * ("level") + */ +class DB_PUBLIC GVertex + : public db::DPoint +{ +public: + typedef std::list edges_type; + typedef edges_type::const_iterator edges_iterator; + typedef edges_type::iterator edges_iterator_non_const; + + GVertex (); + GVertex (const DPoint &p); + GVertex (const GVertex &v); + GVertex (db::DCoord x, db::DCoord y); + + GVertex &operator= (const GVertex &v); + +#if 0 // @@@ + bool is_outside () const; +#endif + std::vector polygons () const; + + edges_iterator begin_edges () const { return mp_edges.begin (); } + edges_iterator end_edges () const { return mp_edges.end (); } + size_t num_edges (int max_count = -1) const; + + bool has_edge (const GPolygonEdge *edge) const; + + void set_is_precious (bool f) { m_is_precious = f; } + bool is_precious () const { return m_is_precious; } + + std::string to_string (bool with_id = false) const; + + /** + * @brief Returns 1 is the point is inside the circle, 0 if on the circle and -1 if outside + * TODO: Move to db::DPoint + */ + static int in_circle (const db::DPoint &point, const db::DPoint ¢er, double radius); + + /** + * @brief Returns 1 is this point is inside the circle, 0 if on the circle and -1 if outside + */ + int in_circle (const db::DPoint ¢er, double radius) const + { + return in_circle (*this, center, radius); + } + +private: + friend class GPolygonEdge; + + void remove_edge (const edges_iterator_non_const &ec) + { + mp_edges.erase (ec); + } + + edges_type mp_edges; + bool m_is_precious; +}; + +/** + * @brief A class representing an edge in the Delaunay triangulation graph + */ +class DB_PUBLIC GPolygonEdge +{ +public: + class GPolygonIterator + { + public: + typedef GPolygon value_type; + typedef GPolygon &reference; + typedef GPolygon *pointer; + + reference operator*() const + { + return *operator-> (); + } + + pointer operator->() const + { + return m_index ? mp_edge->right () : mp_edge->left (); + } + + bool operator== (const GPolygonIterator &other) const + { + return m_index == other.m_index; + } + + bool operator!= (const GPolygonIterator &other) const + { + return !operator== (other); + } + + GPolygonIterator &operator++ () + { + while (++m_index < 2 && operator-> () == 0) + ; + return *this; + } + + private: + friend class GPolygonEdge; + + GPolygonIterator (const GPolygonEdge *edge) + : mp_edge (edge), m_index (0) + { + if (! edge) { + m_index = 2; + } else { + --m_index; + operator++ (); + } + } + + const GPolygonEdge *mp_edge; + unsigned int m_index; + }; + + GPolygonEdge (); + GPolygonEdge (GVertex *v1, GVertex *v2); + + GVertex *v1 () const { return mp_v1; } + GVertex *v2 () const { return mp_v2; } + + void reverse () + { + std::swap (mp_v1, mp_v2); + std::swap (mp_left, mp_right); + } + + GPolygon *left () const { return mp_left; } + GPolygon *right () const { return mp_right; } + + GPolygonIterator begin_polygons () const + { + return GPolygonIterator (this); + } + + GPolygonIterator end_polygons () const + { + return GPolygonIterator (0); + } + + void set_level (size_t l) { m_level = l; } + size_t level () const { return m_level; } + + void set_id (size_t id) { m_id = id; } + size_t id () const { return m_id; } + + void set_is_segment (bool is_seg) { m_is_segment = is_seg; } + bool is_segment () const { return m_is_segment; } + + std::string to_string (bool with_id = false) const; + + /** + * @brief Converts to an db::DEdge + */ + db::DEdge edge () const + { + return db::DEdge (*mp_v1, *mp_v2); + } + + /** + * @brief Returns the distance of the given point to the edge + * + * The distance is the minimum distance of the point to one point from the edge. + * TODO: Move to db::DEdge + */ + static double distance (const db::DEdge &e, const db::DPoint &p); + + /** + * @brief Returns the distance of the given point to the edge + * + * The distance is the minimum distance of the point to one point from the edge. + */ + double distance (const db::DPoint &p) const + { + return distance (edge (), p); + } + + /** + * @brief Returns a value indicating whether this edge crosses the other one + * + * "crosses" is true, if both edges share at least one point which is not an endpoint + * of one of the edges. + * TODO: Move to db::DEdge + */ + static bool crosses (const db::DEdge &e, const db::DEdge &other); + + /** + * @brief Returns a value indicating whether this edge crosses the other one + * + * "crosses" is true, if both edges share at least one point which is not an endpoint + * of one of the edges. + */ + bool crosses (const db::DEdge &other) const + { + return crosses (edge (), other); + } + + /** + * @brief Returns a value indicating whether this edge crosses the other one + * + * "crosses" is true, if both edges share at least one point which is not an endpoint + * of one of the edges. + */ + bool crosses (const db::GPolygonEdge &other) const + { + return crosses (edge (), other.edge ()); + } + + /** + * @brief Returns a value indicating whether this edge crosses the other one + * "crosses" is true, if both edges share at least one point. + * TODO: Move to db::DEdge + */ + static bool crosses_including (const db::DEdge &e, const db::DEdge &other); + + /** + * @brief Returns a value indicating whether this edge crosses the other one + * "crosses" is true, if both edges share at least one point. + */ + bool crosses_including (const db::DEdge &other) const + { + return crosses_including (edge (), other); + } + + /** + * @brief Returns a value indicating whether this edge crosses the other one + * "crosses" is true, if both edges share at least one point. + */ + bool crosses_including (const db::GPolygonEdge &other) const + { + return crosses_including (edge (), other.edge ()); + } + + /** + * @brief Gets the intersection point + * TODO: Move to db::DEdge + */ + static db::DPoint intersection_point (const db::DEdge &e, const DEdge &other); + + /** + * @brief Gets the intersection point + */ + db::DPoint intersection_point (const db::DEdge &other) const + { + return intersection_point (edge (), other); + } + + /** + * @brief Gets the intersection point + */ + db::DPoint intersection_point (const GPolygonEdge &other) const + { + return intersection_point (edge (), other.edge ()); + } + + /** + * @brief Returns a value indicating whether the point is on the edge + * TODO: Move to db::DEdge + */ + static bool point_on (const db::DEdge &edge, const db::DPoint &point); + + /** + * @brief Returns a value indicating whether the point is on the edge + */ + bool point_on (const db::DPoint &point) const + { + return point_on (edge (), point); + } + + /** + * @brief Gets the side the point is on + * + * -1 is for "left", 0 is "on" and +1 is "right" + * TODO: correct to same definition as db::Edge (negative) + */ + static int side_of (const db::DEdge &e, const db::DPoint &point) + { + return -e.side_of (point); + } + + /** + * @brief Gets the side the point is on + * + * -1 is for "left", 0 is "on" and +1 is "right" + * TODO: correct to same definition as db::Edge (negative) + */ + int side_of (const db::DPoint &p) const + { + return -edge ().side_of (p); + } + + /** + * @brief Gets the distance vector + */ + db::DVector d () const + { + return *mp_v2 - *mp_v1; + } + + /** + * @brief Gets the other triangle for the given one + */ + GPolygon *other (const GPolygon *) const; + + /** + * @brief Gets the other vertex for the given one + */ + GVertex *other (const GVertex *) const; + + /** + * @brief Gets a value indicating whether the edge has the given vertex + */ + bool has_vertex (const GVertex *) const; + + /** + * @brief Gets the common vertex of the other edge and this edge or null if there is no common vertex + */ + GVertex *common_vertex (const GPolygonEdge *other) const; + +#if 0 // @@@ + /** + * @brief Returns a value indicating whether this edge can be flipped + */ + bool can_flip () const; + + /** + * @brief Returns a value indicating whether the edge separates two triangles that can be joined into one (via the given vertex) + */ + bool can_join_via (const GVertex *vertex) const; + + /** + * @brief Returns a value indicating whether this edge is an outside edge (no other triangles) + */ + bool is_outside () const; + + /** + * @brief Returns a value indicating whether this edge belongs to outside triangles + */ + bool is_for_outside_triangles () const; +#endif // @@@ + + /** + * @brief Returns a value indicating whether t is attached to this edge + */ + bool has_polygon (const GPolygon *t) const; + +protected: + void unlink (); + void link (); + +private: + friend class GPolygon; + friend class PolygonGraph; + + GVertex *mp_v1, *mp_v2; + GPolygon *mp_left, *mp_right; + GVertex::edges_iterator_non_const m_ec_v1, m_ec_v2; + size_t m_level; + size_t m_id; + bool m_is_segment; + + void set_left (GPolygon *t); + void set_right (GPolygon *t); +}; + +/** + * @brief A compare function that compares triangles by ID + * + * The ID acts as a more predicable unique ID for the object in sets and maps. + */ +struct GPolygonEdgeLessFunc +{ + bool operator () (GPolygonEdge *a, GPolygonEdge *b) const + { + return a->id () < b->id (); + } +}; + +/** + * @brief A class representing a triangle + */ +class DB_PUBLIC GPolygon + : public tl::list_node, public tl::Object +{ +public: + GPolygon (); + + template + GPolygon (Iter from, Iter to) + : mp_e (from, to) + { + init (); + } + + ~GPolygon (); + + void unlink (); + + void set_id (size_t id) { m_id = id; } + size_t id () const { return m_id; } + + // @@@bool is_outside () const { return m_is_outside; } + // @@@void set_outside (bool o) { m_is_outside = o; } + + std::string to_string (bool with_id = false) const; + + /** + * @brief Gets the number of vertexes + */ + size_t size () const + { + return mp_v.size (); + } + + /** + * @brief Gets the nth vertex (n wraps around and can be negative) + * The vertexes are oriented clockwise. + */ + inline GVertex *vertex (int n) const + { + size_t sz = size (); + tl_assert (sz > 0); + if (n >= 0 && size_t (n) < sz) { + return mp_v[n]; + } else { + return mp_v[(n + sz) % sz]; + } + } + + /** + * @brief Gets the nth edge (n wraps around and can be negative) + */ + inline GPolygonEdge *edge (int n) const + { + size_t sz = size (); + tl_assert (sz > 0); + if (n >= 0 && size_t (n) < sz) { + return mp_e[n]; + } else { + return mp_e[(n + sz) % sz]; + } + } + + /** + * @brief Gets the area + */ + double area () const; + + /** + * @brief Returns the bounding box of the triangle + */ + db::DBox bbox () const; + +#if 0 // @@@ + /** + * @brief Gets the center point and radius of the circumcircle + * If ok is non-null, it will receive a boolean value indicating whether the circumcircle is valid. + * An invalid circumcircle is an indicator for a degenerated triangle with area 0 (or close to). + */ + std::pair circumcircle (bool *ok = 0) const; + + /** + * @brief Gets the vertex opposite of the given edge + */ + GVertex *opposite (const GPolygonEdge *edge) const; + + /** + * @brief Gets the edge opposite of the given vertex + */ + GPolygonEdge *opposite (const GVertex *vertex) const; +#endif + + /** + * @brief Gets the edge with the given vertexes + */ + GPolygonEdge *find_edge_with (const GVertex *v1, const GVertex *v2) const; + + /** + * @brief Finds the common edge for both polygons + */ + GPolygonEdge *common_edge (const GPolygon *other) const; + +#if 0 // @@@ + /** + * @brief Returns a value indicating whether the point is inside (1), on the polygon (0) or outside (-1) + */ + int contains (const db::DPoint &point) const; +#endif + + /** + * @brief Gets a value indicating whether the triangle has the given vertex + */ + inline bool has_vertex (const db::GVertex *v) const + { + for (auto i = mp_v.begin (); i != mp_v.end (); ++i) { + if (*i == v) { + return true; + } + } + return false; + } + + /** + * @brief Gets a value indicating whether the triangle has the given edge + */ + inline bool has_edge (const db::GPolygonEdge *e) const + { + for (auto i = mp_e.begin (); i != mp_e.end (); ++i) { + if (*i == e) { + return true; + } + } + return false; + } + + /** + * @brief Returns the minimum edge length + */ + double min_edge_length () const; + +#if 0 // @@@ + /** + * @brief Returns the min edge length to circumcircle radius ratio + */ + double b () const; +#endif + + /** + * @brief Returns a value indicating whether the polygon borders to a segment + */ + bool has_segment () const; + + /** + * @brief Returns the number of segments the polygon borders to + */ + unsigned int num_segments () const; + +private: + // @@@ bool m_is_outside; + std::vector mp_e; + std::vector mp_v; + size_t m_id; + + void init (); + + // no copying + GPolygon &operator= (const GPolygon &); + GPolygon (const GPolygon &); +}; + +/** + * @brief A compare function that compares polygons by ID + * + * The ID acts as a more predicable unique ID for the object in sets and maps. + */ +struct GPolygonLessFunc +{ + bool operator () (GPolygon *a, GPolygon *b) const + { + return a->id () < b->id (); + } +}; + +class DB_PUBLIC PolygonGraph +{ +public: +#if 0 // @@@ + struct TriangulateParameters + { + TriangulateParameters () + : min_b (1.0), + min_length (0.0), + max_area (0.0), + max_area_border (0.0), + max_iterations (std::numeric_limits::max ()), + base_verbosity (30), + mark_triangles (false) + { } + + /** + * @brief Min. readius-to-shortest edge ratio + */ + double min_b; + + /** + * @brief Min. edge length + * + * This parameter does not provide a guarantee about a minimume edge length, but + * helps avoiding ever-reducing triangle splits in acute corners of the input polygon. + * Splitting of edges stops when the edge is less than the min length. + */ + double min_length; + + /** + * @brief Max area or zero for "no constraint" + */ + double max_area; + + /** + * @brief Max area for border triangles or zero for "use max_area" + */ + double max_area_border; + + /** + * @brief Max number of iterations + */ + size_t max_iterations; + + /** + * @brief The verbosity level above which triangulation reports details + */ + int base_verbosity; + + /** + * @brief If true, final triangles are marked using the "id" integer as a bit field + * + * This provides information about the result quality. + * + * Bit 0: skinny triangle + * Bit 1: bad-quality (skinny or area too large) + * Bit 2: non-Delaunay (in the strict sense) + */ + bool mark_triangles; + }; +#endif + + typedef tl::list polygons_type; + typedef polygons_type::const_iterator polygon_iterator; + + PolygonGraph (); + ~PolygonGraph (); + + /** + * @brief Inserts the given polygon + */ + void insert_polygon (const db::DPolygon &box); + + /** + * @brief Returns a string representation of the polygon graph. + */ + std::string to_string (); + + /** + * @brief Returns the bounding box of the polygon graph. + */ + db::DBox bbox () const; + + /** + * @brief Iterates the polygons in the graph (begin iterator) + */ + polygon_iterator begin () const { return mp_polygons.begin (); } + + /** + * @brief Iterates the polygons in the graph (end iterator) + */ + polygon_iterator end () const { return mp_polygons.end (); } + + /** + * @brief Returns the number of polygons in the graph + */ + size_t num_polygons () const { return mp_polygons.size (); } + + /** + * @brief Clears the polygon set + */ + void clear (); + +protected: +#if 0 // @@@ + /** + * @brief Checks the polygon graph for consistency + * This method is for testing purposes mainly. + */ + bool check (bool check_delaunay = true) const; +#endif + + /** + * @brief Dumps the polygon graph to a GDS file at the given path + * This method is for testing purposes mainly. + * + * "decompose_id" will map polygons to layer 20, 21 and 22. + * according to bit 0, 1 and 2 of the ID (useful with the 'mark_polygons' + * flat in TriangulateParameters). + */ + void dump (const std::string &path, bool decompose_by_id = false) const; + + /** + * @brief Creates a new layout object representing the polygon graph + * This method is for testing purposes mainly. + */ + db::Layout *to_layout (bool decompose_by_id = false) const; + +private: + tl::list mp_polygons; + tl::stable_vector m_edges_heap; + std::vector m_returned_edges; + tl::stable_vector m_vertex_heap; +// @@@ bool m_is_constrained; +// @@@ size_t m_level; + size_t m_id; +// @@@ size_t m_flips, m_hops; + + db::GVertex *create_vertex (double x, double y); + db::GVertex *create_vertex (const db::DPoint &pt); + db::GPolygonEdge *create_edge (db::GVertex *v1, db::GVertex *v2); + + template + db::GPolygon * + create_polygon (Iter from, Iter to) + { + db::GPolygon *res = new db::GPolygon (from ,to); + res->set_id (++m_id); + mp_polygons.push_back (res); + return res; + } + + void remove_polygon (db::GPolygon *tri); + template void make_contours (const Poly &poly, const Trans &trans, std::vector > &contours); +}; + +} + +#endif + diff --git a/src/db/unit_tests/dbPolygonGraphTests.cc b/src/db/unit_tests/dbPolygonGraphTests.cc new file mode 100644 index 000000000..9fdb91a2f --- /dev/null +++ b/src/db/unit_tests/dbPolygonGraphTests.cc @@ -0,0 +1,51 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbPolygonGraph.h" +#include "tlUnitTest.h" + +#include +#include + +class TestablePolygonGraph + : public db::PolygonGraph +{ +public: + using db::PolygonGraph::PolygonGraph; + // @@@ using db::PolygonGraph::check; + using db::PolygonGraph::dump; +}; + + +TEST(basic) +{ + db::DBox box (0, 0, 100.0, 200.0); + + TestablePolygonGraph pg; + pg.insert_polygon (db::DPolygon (box)); + + // @@@ + tl::info << pg.to_string (); + pg.dump ("debug.gds"); // @@@ +} + diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index ecb3c7ccb..047e46ea6 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -12,6 +12,7 @@ SOURCES = \ dbFillToolTests.cc \ dbLogTests.cc \ dbObjectWithPropertiesTests.cc \ + dbPolygonGraphTests.cc \ dbPolygonNeighborhoodTests.cc \ dbPropertiesFilterTests.cc \ dbQuadTreeTests.cc \ From 0a587fa079b171c6a71f63402ac0897defc2e823 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 21:10:10 +0000 Subject: [PATCH 114/392] Bump pypa/cibuildwheel from 2.23.0 to 2.23.2 Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.23.0 to 2.23.2. - [Release notes](https://github.com/pypa/cibuildwheel/releases) - [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md) - [Commits](https://github.com/pypa/cibuildwheel/compare/v2.23.0...v2.23.2) --- updated-dependencies: - dependency-name: pypa/cibuildwheel dependency-version: 2.23.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f87194b2c..02b0beaba 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -60,7 +60,7 @@ jobs: HOST_CCACHE_DIR="$(ccache -k cache_dir)" mkdir -p $HOST_CCACHE_DIR - name: Build wheels # check https://cibuildwheel.readthedocs.io/en/stable/setup/#github-actions - uses: pypa/cibuildwheel@v2.23.0 + uses: pypa/cibuildwheel@v2.23.2 # to supply options, put them in 'env', like: # env: # CIBW_SOME_OPTION: value From c656700b44deb03a80106997ae5d28dcaaaba203 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 3 Apr 2025 20:08:33 +0200 Subject: [PATCH 115/392] Maybe fixed issue-2012 (leaking reference in Python) --- src/pya/pya/pyaConvert.cc | 3 +- src/pya/pya/pyaObject.cc | 60 +++++++++++++++++++++++---------- testdata/python/dbLayoutTest.py | 2 +- 3 files changed, 45 insertions(+), 20 deletions(-) diff --git a/src/pya/pya/pyaConvert.cc b/src/pya/pya/pyaConvert.cc index c7b7c6f64..89ecf851f 100644 --- a/src/pya/pya/pyaConvert.cc +++ b/src/pya/pya/pyaConvert.cc @@ -440,7 +440,7 @@ object_to_python (void *obj, PYAObjectBase *self, const gsi::ClassBase *cls, boo obj = clsact->create_from_adapted (obj); } - // we wil own the new object + // we will own the new object pass_obj = true; } @@ -488,6 +488,7 @@ object_to_python (void *obj, PYAObjectBase *self, const gsi::ClassBase *cls, boo PYAObjectBase *new_object = PYAObjectBase::from_pyobject_unsafe (new_pyobject); new (new_object) PYAObjectBase (clsact, new_pyobject); new_object->set (obj, pass_obj, is_const, can_destroy); + return new_pyobject; } diff --git a/src/pya/pya/pyaObject.cc b/src/pya/pya/pyaObject.cc index 63f1e8517..1805b68b8 100644 --- a/src/pya/pya/pyaObject.cc +++ b/src/pya/pya/pyaObject.cc @@ -238,9 +238,16 @@ PYAObjectBase::object_destroyed () detach (); - // NOTE: this may delete "this"! - if (!prev_owner) { - Py_DECREF (py_object ()); + if (! prev_owner) { + const gsi::ClassBase *cls = cls_decl (); + if (cls && cls->is_managed ()) { + // If the object was owned on C++ side before, we need to decrement the + // reference count to reflect the fact, that there no longer is an external + // owner. + // NOTE: this may delete "this", hence we return + Py_DECREF (py_object ()); + return; + } } } @@ -249,31 +256,44 @@ PYAObjectBase::object_destroyed () void PYAObjectBase::release () { + // "release" means to release ownership of the C++ object on the C++ side. + // In other words: to transfer ownership to the script side. Specifically to + // transfer it to the Python domain. + // If the object is managed we first reset the ownership of all other clients // and then make us the owner const gsi::ClassBase *cls = cls_decl (); if (cls && cls->is_managed ()) { void *o = obj (); if (o) { + // NOTE: "keep" means "move ownership of the C++ object to C++". In other words, + // release ownership of the C++ object on script side. cls->gsi_object (o)->keep (); + if (! m_owned) { + // We have to *decrement* the reference count as now there is no other entity + // holding a reference to this Python object. + // NOTE: this may delete "this", hence we return + m_owned = true; + Py_DECREF (py_object ()); + return; + } } } - // NOTE: this is fairly dangerous - if (!m_owned) { - m_owned = true; - // NOTE: this may delete "this"! TODO: this should not happen. Can we assert that somehow? - Py_DECREF (py_object ()); - } + m_owned = true; } void PYAObjectBase::keep_internal () { if (m_owned) { + // "keep" means to transfer ownership of the C++ object to C++ side, while + // "m_owned" refers to ownership on the Python side. So if we perform this + // transfer, we need to reflect the fact that there is another entity holding + // a reference. Py_INCREF (py_object ()); - m_owned = false; } + m_owned = false; } void @@ -284,9 +304,11 @@ PYAObjectBase::keep () void *o = obj (); if (o) { if (cls->is_managed ()) { + // dispatch the keep notification - this will call "keep_internal" through the + // event handler (StatusChangedListener) cls->gsi_object (o)->keep (); } else { - keep_internal (); + m_owned = false; } } } @@ -341,16 +363,18 @@ PYAObjectBase::set (void *obj, bool owned, bool const_ref, bool can_destroy) if (cls->is_managed ()) { gsi::ObjectBase *gsi_object = cls->gsi_object (m_obj); - // Consider the case of "keep inside constructor" if (gsi_object->already_kept ()) { - keep_internal (); + // Consider the case of "keep inside constructor" + m_owned = false; + } + if (! m_owned) { + // "m_owned = false" means ownership of the C++ object is on C++ side, + // and not on script side. In that case, we need to increment the + // reference count to reflect the fact that there is an external owner. + Py_INCREF (py_object ()); } gsi_object->status_changed_event ().add (mp_listener, &StatusChangedListener::object_status_changed); } - - if (!m_owned) { - Py_INCREF (py_object ()); - } } // TODO: a static (singleton) instance is not thread-safe @@ -587,7 +611,7 @@ PYAObjectBase::obj () throw tl::Exception (tl::to_string (tr ("Object has been destroyed already"))); } else { // delayed creation of a detached C++ object .. - set(cls_decl ()->create (), true, false, true); + set (cls_decl ()->create (), true, false, true); } } diff --git a/testdata/python/dbLayoutTest.py b/testdata/python/dbLayoutTest.py index 1179f898b..bae65e913 100644 --- a/testdata/python/dbLayoutTest.py +++ b/testdata/python/dbLayoutTest.py @@ -684,7 +684,7 @@ class DBLayoutTest(unittest.TestCase): ly = pya.Layout(True) pv = [ [ 17, "a" ], [ "b", [ 1, 5, 7 ] ] ] pid = ly.properties_id( pv ) - # does not work? @@@ + # does not work? # pv = { 17: "a", "b": [ 1, 5, 7 ] } # pid2 = ly.properties_id( pv ) # self.assertEqual( pid, pid2 ) From 78e2074b4c236a93a8900f3ed700afb4af39529d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 3 Apr 2025 21:15:49 +0200 Subject: [PATCH 116/392] Added a unit test --- testdata/python/dbShapesTest.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/testdata/python/dbShapesTest.py b/testdata/python/dbShapesTest.py index d7e65f11a..09fa12fce 100644 --- a/testdata/python/dbShapesTest.py +++ b/testdata/python/dbShapesTest.py @@ -24,7 +24,7 @@ import os class DBShapesTest(unittest.TestCase): # Shape objects as hashes - def test_12(self): + def test_1(self): s = pya.Shapes() s1 = s.insert(pya.Box(1, 2, 3, 4)) @@ -50,6 +50,18 @@ class DBShapesTest(unittest.TestCase): self.assertEqual(h[s2], 2) self.assertEqual(h[s3], 3) + # Issue #2012 (reference count) + def test_2(self): + + ly = pya.Layout() + top = ly.create_cell("TOP") + l1 = ly.layer(1, 0) + top.shapes(l1).insert(pya.Box(0, 0, 100, 200)) + + shapes = top.shapes(l1) + self.assertEqual(sys.getrefcount(shapes), 2) + + # run unit tests if __name__ == '__main__': suite = unittest.TestLoader().loadTestsFromTestCase(DBShapesTest) From aa124487470be45c4b680c2a4a25cfe18327e083 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 5 Apr 2025 20:35:11 +0200 Subject: [PATCH 117/392] First attempt to implement a solution for issue #2016 The implementation will not update the PCell on property sheet edits of the guiding shape if lazy evaluation is requested. Still, changes are committed to the PCell on committing the property page. --- src/ant/ant/antPropertiesPage.cc | 2 +- src/ant/ant/antPropertiesPage.h | 2 +- src/edt/edt/edtInstPropertiesPage.cc | 4 ++-- src/edt/edt/edtInstPropertiesPage.h | 4 ++-- src/edt/edt/edtPropertiesPages.cc | 12 +++++------ src/edt/edt/edtPropertiesPages.h | 6 +++--- src/edt/edt/edtService.cc | 29 ++++++++++++++++++++------ src/edt/edt/edtService.h | 9 ++++++-- src/img/img/imgPropertiesPage.cc | 6 +++--- src/img/img/imgPropertiesPage.h | 2 +- src/img/img/imgService.cc | 2 +- src/laybasic/laybasic/layProperties.h | 9 ++++++-- src/layui/layui/layPropertiesDialog.cc | 16 +++++++------- src/layui/layui/layPropertiesDialog.h | 2 +- 14 files changed, 66 insertions(+), 39 deletions(-) diff --git a/src/ant/ant/antPropertiesPage.cc b/src/ant/ant/antPropertiesPage.cc index 6b081bf21..15e19315e 100644 --- a/src/ant/ant/antPropertiesPage.cc +++ b/src/ant/ant/antPropertiesPage.cc @@ -517,7 +517,7 @@ PropertiesPage::readonly () } void -PropertiesPage::apply () +PropertiesPage::apply (bool /*commit*/) { ant::Object obj; get_object (obj); diff --git a/src/ant/ant/antPropertiesPage.h b/src/ant/ant/antPropertiesPage.h index 9b9438d71..c98c42d91 100644 --- a/src/ant/ant/antPropertiesPage.h +++ b/src/ant/ant/antPropertiesPage.h @@ -50,7 +50,7 @@ public: virtual void update (); virtual void leave (); virtual bool readonly (); - virtual void apply (); + virtual void apply (bool commit); private slots: void swap_points_clicked (); diff --git a/src/edt/edt/edtInstPropertiesPage.cc b/src/edt/edt/edtInstPropertiesPage.cc index da06cb814..de27a4d90 100644 --- a/src/edt/edt/edtInstPropertiesPage.cc +++ b/src/edt/edt/edtInstPropertiesPage.cc @@ -925,7 +925,7 @@ InstPropertiesPage::do_apply (bool current_only, bool relative) } void -InstPropertiesPage::apply () +InstPropertiesPage::apply (bool /*commit*/) { do_apply (true, false); } @@ -937,7 +937,7 @@ InstPropertiesPage::can_apply_to_all () const } void -InstPropertiesPage::apply_to_all (bool relative) +InstPropertiesPage::apply_to_all (bool relative, bool /*commit*/) { do_apply (false, relative); } diff --git a/src/edt/edt/edtInstPropertiesPage.h b/src/edt/edt/edtInstPropertiesPage.h index c33d7f3d6..f00a1a659 100644 --- a/src/edt/edt/edtInstPropertiesPage.h +++ b/src/edt/edt/edtInstPropertiesPage.h @@ -66,8 +66,8 @@ protected: edt::PCellParametersPage *mp_pcell_parameters; virtual bool readonly (); - virtual void apply (); - virtual void apply_to_all (bool relative); + virtual void apply (bool commit); + virtual void apply_to_all (bool relative, bool commit); virtual bool can_apply_to_all () const; void do_apply (bool current_only, bool relative); virtual ChangeApplicator *create_applicator (db::Cell &cell, const db::Instance &inst, double dbu); diff --git a/src/edt/edt/edtPropertiesPages.cc b/src/edt/edt/edtPropertiesPages.cc index 365ca1c63..26b1def30 100644 --- a/src/edt/edt/edtPropertiesPages.cc +++ b/src/edt/edt/edtPropertiesPages.cc @@ -215,7 +215,7 @@ ShapePropertiesPage::recompute_selection_ptrs (const std::vector gs = mp_service->handle_guiding_shape_changes (new_sel[index]); + std::pair gs = mp_service->handle_guiding_shape_changes (new_sel[index], commit); if (gs.first) { new_sel[index] = gs.second; @@ -350,9 +350,9 @@ ShapePropertiesPage::do_apply (bool current_only, bool relative) } void -ShapePropertiesPage::apply () +ShapePropertiesPage::apply (bool commit) { - do_apply (true, false); + do_apply (true, false, commit); } bool @@ -362,9 +362,9 @@ ShapePropertiesPage::can_apply_to_all () const } void -ShapePropertiesPage::apply_to_all (bool relative) +ShapePropertiesPage::apply_to_all (bool relative, bool commit) { - do_apply (false, relative); + do_apply (false, relative, commit); } void diff --git a/src/edt/edt/edtPropertiesPages.h b/src/edt/edt/edtPropertiesPages.h index 7d8d5b644..60d844f50 100644 --- a/src/edt/edt/edtPropertiesPages.h +++ b/src/edt/edt/edtPropertiesPages.h @@ -63,10 +63,10 @@ protected: private: virtual void update (); - virtual void apply (); - virtual void apply_to_all (bool relative); + virtual void apply (bool commit); + virtual void apply_to_all (bool relative, bool commit); virtual bool can_apply_to_all () const; - virtual void do_apply (bool current_only, bool relative); + virtual void do_apply (bool current_only, bool relative, bool commit); void recompute_selection_ptrs (const std::vector &new_sel); protected: diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index 75fbf5428..aeb4223d1 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -76,6 +76,7 @@ Service::Service (db::Manager *manager, lay::LayoutViewBase *view, db::ShapeIter m_snap_to_objects (true), m_snap_objects_to_grid (true), m_top_level_sel (false), m_show_shapes_of_instances (true), m_max_shapes_of_instances (1000), + m_pcell_lazy_evaluation (0), m_hier_copy_mode (-1), m_indicate_secondary_selection (false), m_seq (0), @@ -391,6 +392,10 @@ Service::configure (const std::string &name, const std::string &value) tl::from_string (value, m_hier_copy_mode); service_configuration_changed (); + } else if (name == cfg_edit_pcell_lazy_eval_mode) { + + tl::from_string (value, m_pcell_lazy_evaluation); + } else { lay::EditorServiceBase::configure (name, value); } @@ -598,7 +603,7 @@ Service::end_move (const db::DPoint & /*p*/, lay::angle_constraint_type ac) transform (db::DCplxTrans (m_move_trans)); move_cancel (); // formally this functionality fits here // accept changes to guiding shapes - handle_guiding_shape_changes (); + handle_guiding_shape_changes (true); } m_alt_ac = lay::AC_Global; } @@ -846,7 +851,7 @@ Service::transform (const db::DCplxTrans &trans, const std::vector -Service::handle_guiding_shape_changes (const lay::ObjectInstPath &obj) const +Service::handle_guiding_shape_changes (const lay::ObjectInstPath &obj, bool commit) const { unsigned int cv_index = obj.cv_index (); lay::CellView cv = view ()->cellview (cv_index); @@ -1874,10 +1879,22 @@ Service::handle_guiding_shape_changes (const lay::ObjectInstPath &obj) const return std::make_pair (false, lay::ObjectInstPath ()); } - if (! layout->is_pcell_instance (obj.cell_index ()).first) { + auto pcell_decl = layout->pcell_declaration_for_pcell_variant (obj.cell_index ()); + if (! pcell_decl) { return std::make_pair (false, lay::ObjectInstPath ()); } + // Don't update unless we're committing or not in lazy PCell update mode + if (! commit) { + if (m_pcell_lazy_evaluation < 0) { + if (pcell_decl->wants_lazy_evaluation ()) { + return std::make_pair (false, lay::ObjectInstPath ()); + } + } else if (m_pcell_lazy_evaluation > 0) { + return std::make_pair (false, lay::ObjectInstPath ()); + } + } + db::cell_index_type top_cell = std::numeric_limits::max (); db::cell_index_type parent_cell = std::numeric_limits::max (); db::Instance parent_inst; @@ -1944,7 +1961,7 @@ Service::handle_guiding_shape_changes (const lay::ObjectInstPath &obj) const } bool -Service::handle_guiding_shape_changes () +Service::handle_guiding_shape_changes (bool commit) { EditableSelectionIterator s = begin_selection (); @@ -1953,7 +1970,7 @@ Service::handle_guiding_shape_changes () return false; } - std::pair gs = handle_guiding_shape_changes (*s); + std::pair gs = handle_guiding_shape_changes (*s, commit); if (gs.first) { // remove superfluous proxies diff --git a/src/edt/edt/edtService.h b/src/edt/edt/edtService.h index 7e2d34801..e782bbfb5 100644 --- a/src/edt/edt/edtService.h +++ b/src/edt/edt/edtService.h @@ -398,17 +398,19 @@ public: * * @return true, if PCells have been updated, indicating that our selection is no longer valid * + * @param commit If true, changes are "final" (and PCells are updated also in lazy evaluation mode) + * * This version assumes there is only one guiding shape selected and will update the selection. * It will also call layout.cleanup() if required. */ - bool handle_guiding_shape_changes (); + bool handle_guiding_shape_changes (bool commit); /** * @brief Handle changes in a specific guiding shape, i.e. create new PCell variants if required * * @return A pair of bool (indicating that the object path has changed) and the new guiding shape path */ - std::pair handle_guiding_shape_changes (const lay::ObjectInstPath &obj) const; + std::pair handle_guiding_shape_changes (const lay::ObjectInstPath &obj, bool commit) const; /** * @brief Gets a value indicating whether a move operation is ongoing @@ -672,9 +674,12 @@ private: bool m_snap_to_objects; bool m_snap_objects_to_grid; db::DVector m_global_grid; + + // Other attributes bool m_top_level_sel; bool m_show_shapes_of_instances; unsigned int m_max_shapes_of_instances; + int m_pcell_lazy_evaluation; // Hierarchical copy mode (-1: ask, 0: shallow, 1: deep) int m_hier_copy_mode; diff --git a/src/img/img/imgPropertiesPage.cc b/src/img/img/imgPropertiesPage.cc index 4ff7a954b..056e7e5ce 100644 --- a/src/img/img/imgPropertiesPage.cc +++ b/src/img/img/imgPropertiesPage.cc @@ -784,7 +784,7 @@ PropertiesPage::reverse_color_order () } void -PropertiesPage::apply () +PropertiesPage::apply (bool /*commit*/) { bool has_error = false; @@ -915,7 +915,7 @@ PropertiesPage::browse () { BEGIN_PROTECTED - apply (); + apply (true); lay::FileDialog file_dialog (this, tl::to_string (QObject::tr ("Load Image File")), tl::to_string (QObject::tr ("All files (*)"))); @@ -941,7 +941,7 @@ PropertiesPage::save_pressed () { BEGIN_PROTECTED - apply (); + apply (true); lay::FileDialog file_dialog (this, tl::to_string (QObject::tr ("Save As KLayout Image File")), tl::to_string (QObject::tr ("KLayout image files (*.lyimg);;All files (*)"))); diff --git a/src/img/img/imgPropertiesPage.h b/src/img/img/imgPropertiesPage.h index b9ccedc33..e715f99cd 100644 --- a/src/img/img/imgPropertiesPage.h +++ b/src/img/img/imgPropertiesPage.h @@ -57,7 +57,7 @@ public: virtual void update (); virtual void leave (); virtual bool readonly (); - virtual void apply (); + virtual void apply (bool commit); void set_direct_image (img::Object *image); diff --git a/src/img/img/imgService.cc b/src/img/img/imgService.cc index 0dc06ae74..3b1c85589 100644 --- a/src/img/img/imgService.cc +++ b/src/img/img/imgService.cc @@ -71,7 +71,7 @@ public: BEGIN_PROTECTED properties_frame->set_direct_image (mp_image_object); - properties_frame->apply (); + properties_frame->apply (true); if (mp_image_object->is_empty ()) { throw tl::Exception (tl::to_string (tr ("No data loaded for that image"))); diff --git a/src/laybasic/laybasic/layProperties.h b/src/laybasic/laybasic/layProperties.h index ef4b8ec3c..09b16c2b9 100644 --- a/src/laybasic/laybasic/layProperties.h +++ b/src/laybasic/laybasic/layProperties.h @@ -154,8 +154,10 @@ public: * Apply any changes to the current objects. If nothing was * changed, the object may be left untouched. * The dialog will start a transaction on the manager object. + * + * @param commit Is true for the "final" changes (i.e. not during editing) */ - virtual void apply () + virtual void apply (bool /*commit*/) { // default implementation is empty. } @@ -174,8 +176,11 @@ public: * Apply any changes to the current object plus all other objects of the same kind. * If nothing was changed, the objects may be left untouched. * The dialog will start a transaction on the manager object. + * + * @param relative Is true if relative mode is selected + * @param commit Is true for the "final" changes (i.e. not during editing) */ - virtual void apply_to_all (bool /*relative*/) + virtual void apply_to_all (bool /*relative*/, bool /*commit*/) { // default implementation is empty. } diff --git a/src/layui/layui/layPropertiesDialog.cc b/src/layui/layui/layPropertiesDialog.cc index b714742e8..edd85844b 100644 --- a/src/layui/layui/layPropertiesDialog.cc +++ b/src/layui/layui/layPropertiesDialog.cc @@ -197,7 +197,7 @@ PropertiesDialog::PropertiesDialog (QWidget * /*parent*/, db::Manager *manager, } for (size_t i = 0; i < mp_properties_pages.size (); ++i) { mp_stack->addWidget (mp_properties_pages [i]); - connect (mp_properties_pages [i], SIGNAL (edited ()), this, SLOT (apply ())); + connect (mp_properties_pages [i], SIGNAL (edited ()), this, SLOT (properties_edited ())); } // Necessary to maintain the page order for UI regression testing of 0.18 vs. 0.19 (because tl::Collection has changed to order) .. @@ -314,7 +314,7 @@ PropertiesDialog::current_index_changed (const QModelIndex &index, const QModelI db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id); - mp_properties_pages [m_index]->apply (); + mp_properties_pages [m_index]->apply (true); if (! t.is_empty ()) { m_transaction_id = t.id (); @@ -437,7 +437,7 @@ BEGIN_PROTECTED if (! mp_properties_pages [m_index]->readonly ()) { db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id); - mp_properties_pages [m_index]->apply (); + mp_properties_pages [m_index]->apply (true); if (! t.is_empty ()) { m_transaction_id = t.id (); } @@ -485,7 +485,7 @@ BEGIN_PROTECTED if (! mp_properties_pages [m_index]->readonly ()) { db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id); - mp_properties_pages [m_index]->apply (); + mp_properties_pages [m_index]->apply (true); if (! t.is_empty ()) { m_transaction_id = t.id (); } @@ -567,7 +567,7 @@ PropertiesDialog::any_prev () const } void -PropertiesDialog::apply () +PropertiesDialog::properties_edited () { BEGIN_PROTECTED @@ -580,9 +580,9 @@ BEGIN_PROTECTED try { if (mp_ui->apply_to_all_cbx->isChecked () && mp_properties_pages [m_index]->can_apply_to_all ()) { - mp_properties_pages [m_index]->apply_to_all (mp_ui->relative_cbx->isChecked ()); + mp_properties_pages [m_index]->apply_to_all (mp_ui->relative_cbx->isChecked (), false); } else { - mp_properties_pages [m_index]->apply (); + mp_properties_pages [m_index]->apply (false); } mp_properties_pages [m_index]->update (); @@ -632,7 +632,7 @@ BEGIN_PROTECTED db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id); - mp_properties_pages [m_index]->apply (); + mp_properties_pages [m_index]->apply (true); mp_properties_pages [m_index]->update (); if (! t.is_empty ()) { diff --git a/src/layui/layui/layPropertiesDialog.h b/src/layui/layui/layPropertiesDialog.h index 153002127..01e09db45 100644 --- a/src/layui/layui/layPropertiesDialog.h +++ b/src/layui/layui/layPropertiesDialog.h @@ -105,7 +105,7 @@ private: void update_controls (); public slots: - void apply (); + void properties_edited (); void next_pressed (); void prev_pressed (); void cancel_pressed (); From bcf14ede3e1e839bc8f057570ba54165f3e5b9f8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 5 Apr 2025 22:06:29 +0200 Subject: [PATCH 118/392] Fixed issue #2014 (strm2oas lef/def/gds collect drops cells referenced by sky130 spare) Problem was that there was the implicit assumption that substitution cells would be top cells (or at least: not child cells of other substitution cells). --- src/db/db/dbCellMapping.cc | 48 ++++++++++++++----------- src/db/unit_tests/dbCellMappingTests.cc | 35 +++++++++++++++++- 2 files changed, 62 insertions(+), 21 deletions(-) diff --git a/src/db/db/dbCellMapping.cc b/src/db/db/dbCellMapping.cc index b3bdd4e6a..7bc3dc712 100644 --- a/src/db/db/dbCellMapping.cc +++ b/src/db/db/dbCellMapping.cc @@ -354,6 +354,13 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout & std::vector &new_cells = *(new_cells_ptr ? new_cells_ptr : &new_cells_int); std::vector new_cells_b; + std::vector > all_a2b; + for (std::vector::const_iterator b = cell_index_b.begin (); b != cell_index_b.end (); ++b) { + auto m = m_b2a_mapping.find (*b); + tl_assert (m != m_b2a_mapping.end ()); + all_a2b.push_back (std::make_pair (m->second, *b)); + } + std::set called_b; for (std::vector::const_iterator i = cell_index_b.begin (); i != cell_index_b.end (); ++i) { layout_b.cell (*i).collect_called_cells (called_b); @@ -368,6 +375,7 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout & db::cell_index_type new_cell = layout_a.add_cell (layout_b, *b); new_cells.push_back (new_cell); new_cells_b.push_back (*b); + all_a2b.push_back (std::make_pair (new_cell, *b)); if (mapped_pairs) { mapped_pairs->push_back (std::make_pair (*b, new_cell)); @@ -378,34 +386,34 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout & } } - if (! new_cells.empty ()) { + if (all_a2b.empty ()) { + return; + } - // Note: this avoids frequent cell index table rebuilds if source and target layout are identical - db::LayoutLocker locker (&layout_a); + // Note: this avoids frequent cell index table rebuilds if source and target layout are identical + db::LayoutLocker locker (&layout_a); - // Create instances for the new cells in layout A according to their instantiation in layout B - double mag = layout_b.dbu () / layout_a.dbu (); - for (size_t i = 0; i < new_cells.size (); ++i) { + // Create instances for the new cells in layout A according to their instantiation in layout B + double mag = layout_b.dbu () / layout_a.dbu (); + for (auto i = all_a2b.begin (); i != all_a2b.end (); ++i) { - const db::Cell &b = layout_b.cell (new_cells_b [i]); - for (db::Cell::parent_inst_iterator pb = b.begin_parent_insts (); ! pb.at_end (); ++pb) { + const db::Cell &b = layout_b.cell (i->second); + for (db::Cell::parent_inst_iterator pb = b.begin_parent_insts (); ! pb.at_end (); ++pb) { - if (called_b.find (pb->parent_cell_index ()) != called_b.end ()) { + if (called_b.find (pb->parent_cell_index ()) != called_b.end ()) { - db::Cell &pa = layout_a.cell (m_b2a_mapping [pb->parent_cell_index ()]); + db::Cell &pa = layout_a.cell (m_b2a_mapping [pb->parent_cell_index ()]); - db::Instance bi = pb->child_inst (); + db::Instance bi = pb->child_inst (); - db::CellInstArray bci = bi.cell_inst (); - bci.object ().cell_index (new_cells [i]); - bci.transform_into (db::ICplxTrans (mag), &layout_a.array_repository ()); - - if (bi.has_prop_id ()) { - pa.insert (db::CellInstArrayWithProperties (bci, bi.prop_id ())); - } else { - pa.insert (bci); - } + db::CellInstArray bci = bi.cell_inst (); + bci.object ().cell_index (i->first); + bci.transform_into (db::ICplxTrans (mag), &layout_a.array_repository ()); + if (bi.has_prop_id ()) { + pa.insert (db::CellInstArrayWithProperties (bci, bi.prop_id ())); + } else { + pa.insert (bci); } } diff --git a/src/db/unit_tests/dbCellMappingTests.cc b/src/db/unit_tests/dbCellMappingTests.cc index b20c8ec66..f6ec6a767 100644 --- a/src/db/unit_tests/dbCellMappingTests.cc +++ b/src/db/unit_tests/dbCellMappingTests.cc @@ -485,8 +485,41 @@ TEST(7) cib.push_back (b1.cell_index ()); cib.push_back (b2.cell_index ()); cm.create_multi_mapping_full (h, cib, *g, cia); - EXPECT_EQ (m2s (cm, *g, h), "a0->b0;a1->b1;a2->b2;a3->a3;a4->a4;a5->a5"); + EXPECT_EQ (m2s (cm, h, *g), "b0->a0;b1->a1;b2->a2;a3->a3;a4->a4;a5->a5"); EXPECT_EQ (l2s (h), "b0#0:;b1#1:cell_index=3 r0 0,0,cell_index=4 r0 0,0;b2#2:cell_index=4 r0 0,0;a3#3:cell_index=4 r0 0,0,cell_index=5 r0 0,0;a4#4:;a5#5:"); } +// Issue #2014 +TEST(8) +{ + std::unique_ptr g (new db::Layout ()); + db::Cell &a (g->cell (g->add_cell ("a"))); + db::Cell &b (g->cell (g->add_cell ("b"))); + db::Cell &b1 (g->cell (g->add_cell ("b1"))); + db::Cell &b2 (g->cell (g->add_cell ("b2"))); + db::Cell &c (g->cell (g->add_cell ("c"))); + + b.insert (db::CellInstArray (db::CellInst (a.cell_index ()), db::Trans ())); + b.insert (db::CellInstArray (db::CellInst (c.cell_index ()), db::Trans ())); + b.insert (db::CellInstArray (db::CellInst (b1.cell_index ()), db::Trans ())); + b.insert (db::CellInstArray (db::CellInst (b2.cell_index ()), db::Trans ())); + + db::Layout h; + db::Cell &ha (h.cell (h.add_cell ("a"))); + db::Cell &hb (h.cell (h.add_cell ("b"))); + db::Cell &hc (h.cell (h.add_cell ("c"))); + + db::CellMapping cm; + std::vector cib, cia; + cia.push_back (a.cell_index ()); + cia.push_back (b.cell_index ()); + cia.push_back (c.cell_index ()); + cib.push_back (ha.cell_index ()); + cib.push_back (hb.cell_index ()); + cib.push_back (hc.cell_index ()); + cm.create_multi_mapping_full (h, cib, *g, cia); + EXPECT_EQ (m2s (cm, h, *g), "a->a;b->b;b1->b1;b2->b2;c->c"); + + EXPECT_EQ (l2s (h), "a#0:;b#1:cell_index=0 r0 0,0,cell_index=2 r0 0,0,cell_index=3 r0 0,0,cell_index=4 r0 0,0;c#2:;b1#3:;b2#4:"); +} From 163c3b8edc1ffeb73cd10ef8464ee084b0de9957 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Apr 2025 12:50:34 +0200 Subject: [PATCH 119/392] Making "assume FOREIGN always default for strm* tools", OASIS warns on ghost cells --- src/buddies/src/bd/bdReaderOptions.cc | 4 +++- src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/buddies/src/bd/bdReaderOptions.cc b/src/buddies/src/bd/bdReaderOptions.cc index 53133d373..752d20edd 100644 --- a/src/buddies/src/bd/bdReaderOptions.cc +++ b/src/buddies/src/bd/bdReaderOptions.cc @@ -120,7 +120,9 @@ GenericReaderOptions::GenericReaderOptions () m_lefdef_separate_groups = load_options.get_option_by_name ("lefdef_config.separate_groups").to_bool (); m_lefdef_joined_paths = load_options.get_option_by_name ("lefdef_config.joined_paths").to_bool (); m_lefdef_map_file = load_options.get_option_by_name ("lefdef_config.map_file").to_string (); - m_lefdef_macro_resolution_mode = load_options.get_option_by_name ("lefdef_config.macro_resolution_mode").to_int (); + // Don't take the default, as in practice, it's more common to substitute LEF macros by layouts + // m_lefdef_macro_resolution_mode = load_options.get_option_by_name ("lefdef_config.macro_resolution_mode").to_int (); + m_lefdef_macro_resolution_mode = 2; // "assume FOREIGN always" } void diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index 548a4e447..6a7f5afee 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -1671,6 +1671,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save // skip cell body if the cell is not to be written if (skip_cell_body (cref)) { + tl::warn << tl::to_string (tr ("Cannot write ghost cell to OASIS - skipping cell: ")) << layout.cell_name (*cell); continue; } From 83e0c172910d19862028eb5da49a3dd6e26aa1a8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Apr 2025 19:20:38 +0200 Subject: [PATCH 120/392] Print total runtime for converter buddy tools with -d 11 --- src/buddies/src/bd/bdConverterMain.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/buddies/src/bd/bdConverterMain.cc b/src/buddies/src/bd/bdConverterMain.cc index d44f9b05e..d3f418b06 100644 --- a/src/buddies/src/bd/bdConverterMain.cc +++ b/src/buddies/src/bd/bdConverterMain.cc @@ -27,6 +27,7 @@ #include "dbReader.h" #include "dbWriter.h" #include "tlCommandLineParser.h" +#include "tlTimer.h" namespace bd { @@ -53,6 +54,8 @@ int converter_main (int argc, char *argv[], const std::string &format) db::Layout layout; + tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total"))); + { db::LoadLayoutOptions load_options; generic_reader_options.configure (load_options); From 6b5268e5f768c35d35a3d6ca33db90fd32345552 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 6 Apr 2025 19:21:02 +0200 Subject: [PATCH 121/392] Feature glob expansion on LEF and GDS lists for LEF/DEF reader options. --- .../lefdef/db_plugin/dbLEFDEFImporter.cc | 25 +++-- .../lefdef/db_plugin/dbLEFDEFImporter.h | 2 +- .../lefdef/db_plugin/dbLEFDEFPlugin.cc | 56 +++++++----- src/tl/tl/tlFileUtils.cc | 48 ++++++++++ src/tl/tl/tlFileUtils.h | 8 ++ src/tl/unit_tests/tlFileUtilsTests.cc | 91 +++++++++++++++++++ 6 files changed, 198 insertions(+), 32 deletions(-) diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc index ff229aeb9..d95144c75 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc @@ -38,7 +38,7 @@ namespace db // ----------------------------------------------------------------------------------- // Path resolution utility -std::string correct_path (const std::string &fn_in, const db::Layout &layout, const std::string &base_path) +std::vector correct_path (const std::string &fn_in, const db::Layout &layout, const std::string &base_path, bool glob) { const db::Technology *tech = layout.technology (); @@ -64,19 +64,28 @@ std::string correct_path (const std::string &fn_in, const db::Layout &layout, co if (tech && ! tech->base_path ().empty ()) { std::string new_fn = tl::combine_path (tech->base_path (), fn); if (tl::file_exists (new_fn)) { - return new_fn; + std::vector res; + res.push_back (new_fn); + return res; + } else if (glob) { + return tl::glob_expand (new_fn); } } if (! base_path.empty ()) { - return tl::combine_path (base_path, fn); - } else { - return fn; + fn = tl::combine_path (base_path, fn); } - } else { - return fn; } + + if (tl::file_exists (fn) || ! glob) { + std::vector res; + res.push_back (fn); + return res; + } else { + return tl::glob_expand (fn); + } + } // ----------------------------------------------------------------------------------- @@ -1059,7 +1068,7 @@ LEFDEFReaderState::read_map_file (const std::string &filename, db::Layout &layou std::map, std::vector > layer_map; for (std::vector::const_iterator p = paths.begin (); p != paths.end (); ++p) { - read_single_map_file (correct_path (*p, layout, base_path), layer_map); + read_single_map_file (correct_path (*p, layout, base_path, false).front (), layer_map); } // build an explicit layer mapping now. diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h index 607410b6a..2a5736f0e 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h @@ -52,7 +52,7 @@ struct MacroDesc; * @brief Correct a path relative to the stream and technology */ DB_PLUGIN_PUBLIC -std::string correct_path (const std::string &fn, const db::Layout &layout, const std::string &base_path); +std::vector correct_path (const std::string &fn, const db::Layout &layout, const std::string &base_path, bool glob); /** * @brief Convers a string to a MASKSHIFT index list diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc index 8d37fa283..c8d3d840c 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc @@ -141,11 +141,12 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti for (std::vector::const_iterator l = effective_options.begin_lef_files (); l != effective_options.end_lef_files (); ++l) { - std::string lp = correct_path (*l, layout, base_path); - - tl::InputStream lef_stream (lp); - tl::log << tl::to_string (tr ("Reading")) << " " << lp; - importer.read (lef_stream, layout, state); + auto paths = correct_path (*l, layout, base_path, true); + for (auto lp = paths.begin (); lp != paths.end (); ++lp) { + tl::InputStream lef_stream (*lp); + tl::log << tl::to_string (tr ("Reading")) << " " << *lp; + importer.read (lef_stream, layout, state); + } } @@ -164,14 +165,20 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti for (std::vector::const_iterator l = effective_options.begin_lef_files (); l != effective_options.end_lef_files (); ++l) { - std::string lp = correct_path (*l, layout, base_path); - lef_files_read.insert (tl::normalize_path (lp)); + auto paths = correct_path (*l, layout, base_path, true); + for (auto lp = paths.begin (); lp != paths.end (); ++lp) { - tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF file: ")) + lp); + if (lef_files_read.insert (tl::normalize_path (*lp)).second) { - tl::InputStream lef_stream (lp); - tl::log << tl::to_string (tr ("Reading")) << " " << lp; - importer.read_lef (lef_stream, layout, state); + tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF file: ")) + *lp); + + tl::InputStream lef_stream (*lp); + tl::log << tl::to_string (tr ("Reading")) << " " << *lp; + importer.read_lef (lef_stream, layout, state); + + } + + } } @@ -223,22 +230,25 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti tl::shared_collection macro_layout_object_holder; for (std::vector::const_iterator l = effective_options.begin_macro_layout_files (); l != effective_options.end_macro_layout_files (); ++l) { - std::string lp = correct_path (*l, layout, base_path); + auto paths = correct_path (*l, layout, base_path, true); + for (auto lp = paths.begin (); lp != paths.end (); ++lp) { - tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF macro layout file: ")) + lp); + tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF macro layout file: ")) + *lp); - tl::InputStream macro_layout_stream (lp); - tl::log << tl::to_string (tr ("Reading")) << " " << lp; - db::Layout *new_layout = new db::Layout (false); - macro_layout_object_holder.push_back (new_layout); - macro_layouts.push_back (new_layout); + tl::InputStream macro_layout_stream (*lp); + tl::log << tl::to_string (tr ("Reading")) << " " << *lp; + db::Layout *new_layout = new db::Layout (false); + macro_layout_object_holder.push_back (new_layout); + macro_layouts.push_back (new_layout); - db::Reader reader (macro_layout_stream); - reader.read (*new_layout, options); + db::Reader reader (macro_layout_stream); + reader.read (*new_layout, options); + + if (fabs (new_layout->dbu () / layout.dbu () - 1.0) > db::epsilon) { + importer.warn (tl::sprintf (tl::to_string (tr ("DBU of macro layout file '%s' does not match reader DBU (layout DBU is %.12g, reader DBU is set to %.12g)")), + *lp, new_layout->dbu (), layout.dbu ())); + } - if (fabs (new_layout->dbu () / layout.dbu () - 1.0) > db::epsilon) { - importer.warn (tl::sprintf (tl::to_string (tr ("DBU of macro layout file '%s' does not match reader DBU (layout DBU is %.12g, reader DBU is set to %.12g)")), - lp, new_layout->dbu (), layout.dbu ())); } } diff --git a/src/tl/tl/tlFileUtils.cc b/src/tl/tl/tlFileUtils.cc index 7debc3591..a3b755db0 100644 --- a/src/tl/tl/tlFileUtils.cc +++ b/src/tl/tl/tlFileUtils.cc @@ -25,6 +25,7 @@ #include "tlLog.h" #include "tlInternational.h" #include "tlEnv.h" +#include "tlGlobPattern.h" #include #include @@ -430,6 +431,53 @@ std::vector dir_entries (const std::string &s, bool with_files, boo return ee; } +static void glob_partial (const std::string &where, std::vector::const_iterator pfrom, std::vector::const_iterator pto, std::vector &res) +{ + if (pfrom == pto) { + if (! is_dir (where)) { + res.push_back (where); + } + return; + } + + auto p = where + *pfrom; + if (file_exists (p)) { + glob_partial (p, pfrom + 1, pto, res); + return; + } + + if (tl::trimmed_part (*pfrom) == "**") { + if (pfrom + 1 == pto) { + // a glob pattern can't be "**" without anything after that + return; + } + auto subdirs = dir_entries (where, false, true, true); + for (auto s = subdirs.begin (); s != subdirs.end (); ++s) { + glob_partial (combine_path (where, *s), pfrom, pto, res); + } + ++pfrom; + } + + tl::GlobPattern glob (tl::trimmed_part (*pfrom)); + ++pfrom; + auto entries = dir_entries (where, true, true, true); + for (auto e = entries.begin (); e != entries.end (); ++e) { + if (glob.match (*e)) { + glob_partial (combine_path (where, *e), pfrom, pto, res); + } + } +} + +std::vector glob_expand (const std::string &path) +{ + auto apath = absolute_file_path (path); + auto parts = split_path (apath); + + std::vector res; + glob_partial (std::string (), parts.begin (), parts.end (), res); + return res; +} + bool mkdir (const std::string &path) { #if defined(_WIN32) diff --git a/src/tl/tl/tlFileUtils.h b/src/tl/tl/tlFileUtils.h index 3456eb6da..4401ac41b 100644 --- a/src/tl/tl/tlFileUtils.h +++ b/src/tl/tl/tlFileUtils.h @@ -138,6 +138,14 @@ bool TL_PUBLIC is_dir (const std::string &s); */ std::vector TL_PUBLIC dir_entries (const std::string &s, bool with_files = true, bool with_dirs = true, bool without_dotfiles = false); +/** + * @brief Expands a glob pattern into a set of files + * + * This version supports "**" for recursive directory expansion. + * Apart from that the features of tl::GlobPattern are supported. + */ +std::vector TL_PUBLIC glob_expand (const std::string &path); + /** * @brief Rename the given file */ diff --git a/src/tl/unit_tests/tlFileUtilsTests.cc b/src/tl/unit_tests/tlFileUtilsTests.cc index c12776608..2eafe8bc3 100644 --- a/src/tl/unit_tests/tlFileUtilsTests.cc +++ b/src/tl/unit_tests/tlFileUtilsTests.cc @@ -995,3 +995,94 @@ TEST (24) EXPECT_EQ (tl::file_exists (p), false); } +// glob_expand +TEST (25) +{ + tl::TemporaryDirectory tmpdir ("tl_tests"); + auto p = tmpdir.path (); + + auto ad = tl::combine_path (p, "a"); + tl::mkpath (ad); + auto aad = tl::combine_path (ad, "a"); + tl::mkpath (aad); + auto aaad = tl::combine_path (aad, "a"); + tl::mkpath (aaad); + auto bd = tl::combine_path (p, "b"); + tl::mkpath (bd); + + { + std::ofstream os (tl::combine_path (ad, "test.txt")); + os << "A test"; + os.close (); + } + + { + std::ofstream os (tl::combine_path (aad, "test.txt")); + os << "A test"; + os.close (); + } + + { + std::ofstream os (tl::combine_path (aaad, "test.txt")); + os << "A test"; + os.close (); + } + + { + std::ofstream os (tl::combine_path (aaad, "test2.txt")); + os << "A test"; + os.close (); + } + + { + std::ofstream os (tl::combine_path (bd, "test.txt")); + os << "A test"; + os.close (); + } + + { + std::ofstream os (tl::combine_path (p, "test2.txt")); + os << "A test"; + os.close (); + } + + std::vector au; + + auto res = tl::glob_expand (tl::combine_path (p, "*.txt")); + au.push_back (tl::combine_path (p, "test2.txt")); + + std::sort (res.begin (), res.end ()); + std::sort (au.begin (), au.end ()); + EXPECT_EQ (res == au, true); + + res = tl::glob_expand (tl::combine_path (tl::combine_path (p, "**"), "*.txt")); + au.clear (); + au.push_back (tl::combine_path (p, "test2.txt")); + au.push_back (tl::combine_path (ad, "test.txt")); + au.push_back (tl::combine_path (aad, "test.txt")); + au.push_back (tl::combine_path (aaad, "test.txt")); + au.push_back (tl::combine_path (aaad, "test2.txt")); + au.push_back (tl::combine_path (bd, "test.txt")); + + std::sort (res.begin (), res.end ()); + std::sort (au.begin (), au.end ()); + EXPECT_EQ (res == au, true); + + res = tl::glob_expand (tl::combine_path (tl::combine_path (p, "**"), "*2.txt")); + au.clear (); + au.push_back (tl::combine_path (p, "test2.txt")); + au.push_back (tl::combine_path (aaad, "test2.txt")); + + std::sort (res.begin (), res.end ()); + std::sort (au.begin (), au.end ()); + EXPECT_EQ (res == au, true); + + res = tl::glob_expand (tl::combine_path (tl::combine_path (tl::combine_path (p, "**"), "a"), "*2.txt")); + au.clear (); + au.push_back (tl::combine_path (aaad, "test2.txt")); + + std::sort (res.begin (), res.end ()); + std::sort (au.begin (), au.end ()); + EXPECT_EQ (res == au, true); +} + From 41e9cb5893a60c0b14b8ecb67af16f91070c36b6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 7 Apr 2025 00:53:43 +0200 Subject: [PATCH 122/392] Maybe fixing basic issues with strmxor 1. Output of shape countsi in deep mode was hierarchical with output file, flat without 2. Refactoring of XOR (for_merged optimization) needed to create cover cell variants --- src/buddies/src/bd/strmxor.cc | 26 +-- src/db/db/dbArray.h | 11 ++ src/db/db/dbHierarchyBuilder.cc | 22 ++- src/db/db/dbHierarchyBuilder.h | 14 +- src/db/db/dbRecursiveShapeIterator.cc | 115 ++++++++----- src/db/db/dbRecursiveShapeIterator.h | 15 +- .../dbRecursiveShapeIteratorTests.cc | 153 ++++++++++++++++-- src/rdb/rdb/rdbUtils.cc | 2 +- 8 files changed, 280 insertions(+), 78 deletions(-) diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 94ff7da1d..a18fc36cd 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -572,14 +572,22 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) if (! silent && ! no_summary) { if (result) { - tl::info << "No differences found"; + tl::info << tl::to_string (tr ("No differences found")); } else { const char *line_format = " %-10s %-12s %s"; - const char *sep = " -------------------------------------------------------"; - tl::info << "Result summary (layers without differences are not shown):" << tl::endl; - tl::info << tl::sprintf (line_format, "Layer", "Output", "Differences (shape count)") << tl::endl << sep; + std::string headline; + if (deep) { + headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")), tl::to_string (tr ("Differences (hierarchical shape count)"))); + } else { + headline = tl::sprintf (line_format, tl::to_string (tr ("Layer")), tl::to_string (tr ("Output")), tl::to_string (tr ("Differences (shape count)"))); + } + + const char *sep = " ----------------------------------------------------------------"; + + tl::info << tl::to_string (tr ("Result summary (layers without differences are not shown):")) << tl::endl; + tl::info << headline << tl::endl << sep; int ti = -1; for (std::map, ResultDescriptor>::const_iterator r = results.begin (); r != results.end (); ++r) { @@ -587,17 +595,17 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) if (r->first.first != ti) { ti = r->first.first; if (tolerances[ti] > db::epsilon) { - tl::info << tl::endl << "Tolerance " << tl::micron_to_string (tolerances[ti]) << ":" << tl::endl; - tl::info << tl::sprintf (line_format, "Layer", "Output", "Differences (shape count)") << tl::endl << sep; + tl::info << tl::endl << tl::to_string (tr ("Tolerance ")) << tl::micron_to_string (tolerances[ti]) << ":" << tl::endl; + tl::info << headline << tl::endl << sep; } } std::string out ("-"); std::string value; if (r->second.layer_a < 0 && ! dont_summarize_missing_layers) { - value = "(no such layer in first layout)"; + value = tl::to_string (tr ("(no such layer in first layout)")); } else if (r->second.layer_b < 0 && ! dont_summarize_missing_layers) { - value = "(no such layer in second layout)"; + value = tl::to_string (tr ("(no such layer in second layout)")); } else if (! r->second.is_empty ()) { if (r->second.layer_output >= 0 && r->second.layout) { out = r->second.layout->get_properties (r->second.layer_output).to_string (); @@ -857,7 +865,7 @@ bool run_deep_xor (const XORData &xor_data) result.layer_output = result.layout->insert_layer (lp); xor_res.insert_into (xor_data.output_layout, xor_data.output_cell, result.layer_output); } else { - result.shape_count = xor_res.count (); + result.shape_count = xor_res.hier_count (); } ++tol_index; diff --git a/src/db/db/dbArray.h b/src/db/db/dbArray.h index a4f51760f..ec8d16ef6 100644 --- a/src/db/db/dbArray.h +++ b/src/db/db/dbArray.h @@ -1571,6 +1571,17 @@ struct array_iterator } } + /** + * @brief Gets a value indicating whether the iterator is a synthetic one + * + * "is_singular" is true, if the iterator was default-created or with a single + * transformation. + */ + bool is_singular () const + { + return mp_base == 0; + } + private: trans_type m_trans; basic_array_iterator *mp_base; diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index ac3a31116..aa26e0adb 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -273,7 +273,7 @@ HierarchyBuilder::begin (const RecursiveShapeIterator *iter) return; } - CellMapKey key (iter->top_cell ()->cell_index (), false, std::set ()); + CellMapKey key (iter->top_cell ()->cell_index (), false, std::set (), false); m_cm_entry = m_cell_map.find (key); if (m_cm_entry == m_cell_map.end ()) { @@ -351,7 +351,6 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co if (! key.clip_region.empty ()) { cn += "$CLIP_VAR"; description += "CLIP"; - } if (key.inactive) { cn += "$DIS"; @@ -360,6 +359,13 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co } description += "DISABLED"; } + if (key.skip_shapes) { + cn += "$SKIP"; + if (! description.empty ()) { + description += "/"; + } + description += "SKIPPED"; + } new_cell = mp_target->add_cell (cn.c_str ()); @@ -383,11 +389,11 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co } HierarchyBuilder::new_inst_mode -HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) +HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool skip_shapes) { if (all) { - CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst.object ().cell_index ()), std::set ()); + CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst.object ().cell_index ()), std::set (), skip_shapes); db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst.object ().cell_index ())); // for new cells, create this instance @@ -402,7 +408,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn } // 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; + return (! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end ()) ? NI_single : NI_skip; } else { @@ -413,7 +419,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn } bool -HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all) +HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes) { if (all) { @@ -429,7 +435,7 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db: return false; } - CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst_cell), clip_variant.second); + CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst_cell), clip_variant.second, skip_shapes); db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst_cell)); // for a new cell, create this instance @@ -441,7 +447,7 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db: } } - return (m_cells_seen.find (key) == m_cells_seen.end ()); + return ! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end (); } } diff --git a/src/db/db/dbHierarchyBuilder.h b/src/db/db/dbHierarchyBuilder.h index 31a125f44..2bc2793d1 100644 --- a/src/db/db/dbHierarchyBuilder.h +++ b/src/db/db/dbHierarchyBuilder.h @@ -249,16 +249,16 @@ public: struct CellMapKey { CellMapKey () - : original_cell (0), inactive (false) + : original_cell (0), inactive (false), skip_shapes (false) { } - CellMapKey (db::cell_index_type _original_cell, bool _inactive, const std::set &_clip_region) - : original_cell (_original_cell), inactive (_inactive), clip_region (_clip_region) + CellMapKey (db::cell_index_type _original_cell, bool _inactive, const std::set &_clip_region, bool _skip_shapes) + : original_cell (_original_cell), inactive (_inactive), clip_region (_clip_region), skip_shapes (_skip_shapes) { } bool operator== (const CellMapKey &other) const { - return original_cell == other.original_cell && inactive == other.inactive && clip_region == other.clip_region; + return original_cell == other.original_cell && inactive == other.inactive && clip_region == other.clip_region && skip_shapes == other.skip_shapes; } bool operator< (const CellMapKey &other) const @@ -266,12 +266,14 @@ public: if (original_cell != other.original_cell) { return original_cell < other.original_cell; } if (inactive != other.inactive) { return inactive < other.inactive; } if (clip_region != other.clip_region) { return clip_region < other.clip_region; } + if (skip_shapes != other.skip_shapes) { return skip_shapes < other.skip_shapes; } return false; } db::cell_index_type original_cell; bool inactive; std::set clip_region; + bool skip_shapes; }; @@ -294,8 +296,8 @@ public: 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 ICplxTrans &always_apply, 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 ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all); + virtual new_inst_mode new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes); + virtual bool new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes); virtual void shape (const RecursiveShapeIterator *iter, const db::Shape &shape, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region); /** diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index c92cd4bc4..b3965f9ce 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -74,6 +74,8 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI m_layer = d.m_layer; mp_cell = d.mp_cell; m_current_layer = d.m_current_layer; + m_skip_shapes = d.m_skip_shapes; + m_skip_shapes_member = d.m_skip_shapes_member; m_shape = d.m_shape; m_trans = d.m_trans; m_global_trans = d.m_global_trans; @@ -85,6 +87,7 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI m_local_complex_region_stack = d.m_local_complex_region_stack; m_local_region_stack = d.m_local_region_stack; m_skip_shapes_stack = d.m_skip_shapes_stack; + m_skip_shapes_member_stack = d.m_skip_shapes_member_stack; m_needs_reinit = d.m_needs_reinit; m_inst_quad_id = d.m_inst_quad_id; m_inst_quad_id_stack = d.m_inst_quad_id_stack; @@ -469,6 +472,8 @@ RecursiveShapeIterator::validate (RecursiveShapeReceiver *receiver) const m_local_region_stack.push_back (m_global_trans.inverted () * m_region); m_skip_shapes_stack.clear (); m_skip_shapes_stack.push_back (false); + m_skip_shapes_member_stack.clear (); + m_skip_shapes_member_stack.push_back (false); m_local_complex_region_stack.clear (); if (mp_complex_region.get ()) { @@ -814,39 +819,6 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const bool RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const { - bool skip_shapes = false; - - if (m_for_merged_input && ! m_skip_shapes_stack.back () && (! m_has_layers || m_layers.size () == 1)) { - - // Try some optimization: if the instance we're looking at is entirely covered - // by a rectangle (other objects are too expensive to check), then we skip it - // - // We check 10 shapes max. - - box_type inst_bx; - if (m_inst->size () == 1) { - inst_bx = m_inst->bbox (m_box_convert); - } else { - inst_bx = m_inst->complex_trans (*m_inst_array) * m_box_convert (m_inst->cell_inst ().object ()); - } - - unsigned int l = m_has_layers ? m_layers.front () : m_layer; - auto si = cell ()->shapes (l).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel); - size_t nmax = 10; - while (! si.at_end () && nmax-- > 0) { - if (inst_bx.inside (si->rectangle ())) { - skip_shapes = true; - break; - } - ++si; - } - - } - - if (skip_shapes && (! receiver || ! receiver->wants_all_cells ())) { - return false; - } - tl_assert (mp_layout); m_trans_stack.push_back (m_trans); @@ -874,7 +846,8 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const } m_local_region_stack.push_back (new_region); - m_skip_shapes_stack.push_back (m_skip_shapes_stack.back () || skip_shapes); + m_skip_shapes_stack.push_back (m_skip_shapes); + m_skip_shapes_member_stack.push_back (m_skip_shapes_member); if (! m_local_complex_region_stack.empty ()) { @@ -948,6 +921,8 @@ RecursiveShapeIterator::pop () const m_inst = m_inst_iterators.back (); m_inst_array = m_inst_array_iterators.back (); m_inst_quad_id = m_inst_quad_id_stack.back (); + m_skip_shapes = m_skip_shapes_stack.back (); + m_skip_shapes_member = m_skip_shapes_member_stack.back (); m_inst_iterators.pop_back (); m_inst_array_iterators.pop_back (); m_inst_quad_id_stack.pop_back (); @@ -958,6 +933,7 @@ RecursiveShapeIterator::pop () const m_cells.pop_back (); m_local_region_stack.pop_back (); m_skip_shapes_stack.pop_back (); + m_skip_shapes_member_stack.pop_back (); if (! m_local_complex_region_stack.empty ()) { m_local_complex_region_stack.pop_back (); } @@ -982,7 +958,7 @@ RecursiveShapeIterator::start_shapes () const void RecursiveShapeIterator::new_layer () const { - if (m_skip_shapes_stack.back () || int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) { + if (skip_shapes () || int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) { m_shape = shape_iterator (); } else if (! m_overlapping) { m_shape = cell ()->shapes (m_layer).begin_touching (m_local_region_stack.back (), m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel); @@ -1029,6 +1005,32 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const new_inst (receiver); } +bool +RecursiveShapeIterator::instance_is_covered (const box_type &inst_bx, unsigned int layer) const +{ + // Try some optimization: if the instance we're looking at is entirely covered + // by a rectangle (other objects are too expensive to check), then we skip it + // + // We check 10 shapes max. + + auto si = cell ()->shapes (layer).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel); + size_t nmax = 10; + while (! si.at_end () && nmax-- > 0) { + if (inst_bx.inside (si->rectangle ())) { + return true; + } + ++si; + } + + return false; +} + +bool +RecursiveShapeIterator::skip_shapes () const +{ + return m_skip_shapes_stack.back () || m_skip_shapes_member_stack.back (); +} + void RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const { @@ -1055,9 +1057,19 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const all_of_instance = m_local_complex_region_stack.empty (); } + m_skip_shapes = skip_shapes (); + m_skip_shapes_member = false; + + if (m_for_merged_input && ! m_skip_shapes && (! m_has_layers || m_layers.size () == 1)) { + box_type inst_bx = m_inst->bbox (m_box_convert); + m_skip_shapes = instance_is_covered (inst_bx, m_has_layers ? m_layers.front () : m_layer); + } + RecursiveShapeReceiver::new_inst_mode ni = RecursiveShapeReceiver::NI_all; if (receiver) { - ni = receiver->new_inst (this, m_inst->cell_inst (), always_apply (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance); + ni = receiver->new_inst (this, m_inst->cell_inst (), always_apply (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance, m_skip_shapes); + } else if (m_skip_shapes) { + ni = RecursiveShapeReceiver::NI_skip; } if (ni == RecursiveShapeReceiver::NI_skip) { @@ -1095,7 +1107,7 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const // 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) * cell_bbox (m_inst->cell_index ()); + box_type ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ()); if (! is_outside_complex_region (ia_box)) { break; } else { @@ -1105,12 +1117,31 @@ RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const } - while (! m_inst_array.at_end () && receiver) { - if (receiver->new_inst_member (this, m_inst->cell_inst (), always_apply (), 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; + m_skip_shapes_member = false; + + while (! m_inst_array.at_end () && (m_for_merged_input || receiver)) { + + m_skip_shapes_member = m_skip_shapes; + if (m_for_merged_input && ! m_inst_array.is_singular () && ! m_skip_shapes && (! m_has_layers || m_layers.size () == 1)) { + + box_type ia_box = m_inst->complex_trans (*m_inst_array) * cell_bbox (m_inst->cell_index ()); + m_skip_shapes_member = instance_is_covered (ia_box, m_has_layers ? m_layers.front () : m_layer); + } + + bool skip = false; + if (receiver) { + skip = ! receiver->new_inst_member (this, m_inst->cell_inst (), always_apply (), 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 (), m_skip_shapes_member); + } else { + skip = m_skip_shapes_member; + } + + if (skip) { + ++m_inst_array; + } else { + break; + } + } } diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index 26ee32b7e..b0e685686 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -868,6 +868,7 @@ private: mutable unsigned int m_layer; mutable const cell_type *mp_cell; mutable size_t m_current_layer; + mutable bool m_skip_shapes, m_skip_shapes_member; mutable shape_iterator m_shape; mutable cplx_trans_type m_trans; mutable std::vector m_trans_stack; @@ -876,7 +877,7 @@ private: mutable std::vector m_cells; mutable std::vector m_local_complex_region_stack; mutable std::vector m_local_region_stack; - mutable std::vector m_skip_shapes_stack; + mutable std::vector m_skip_shapes_stack, m_skip_shapes_member_stack; mutable bool m_needs_reinit; mutable size_t m_inst_quad_id; mutable std::vector m_inst_quad_id_stack; @@ -899,6 +900,8 @@ private: bool down (RecursiveShapeReceiver *receiver) const; void pop () const; + bool instance_is_covered (const box_type &inst_bx, unsigned int layer) const; + bool skip_shapes () const; bool is_outside_complex_region (const db::Box &box) const; void set_inactive (bool a) const @@ -1013,8 +1016,11 @@ public: * - 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) + * + * The "skip_shapes" parameter indicates that the instance is visited with the + * purpose of skipping all shapes. This is used to implement the "for_merged" optimization. */ - virtual new_inst_mode new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return NI_all; } + virtual new_inst_mode new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/, bool /*skip_shapes*/) { return NI_all; } /** * @brief Enters a new array member of the instance @@ -1026,8 +1032,11 @@ public: * "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. + * + * The "skip_shapes" parameter indicates that the instance member is visited with the + * purpose of skipping all shapes. This is used to implement the "for_merged" optimization. */ - virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { return true; } + virtual bool new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/, bool /*skip_shapes*/) { return true; } /** * @brief Delivers a shape diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index 7c386f011..c7205fbf3 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -756,15 +756,25 @@ namespace { : public db::RecursiveShapeReceiver { public: - FlatPusher (std::set *boxes) : mp_boxes (boxes) { } + FlatPusher (std::set *boxes = 0) : mp_boxes (boxes ? boxes : &m_boxes) { } void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans & /*always_apply*/, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { mp_boxes->insert (trans * shape.bbox ()); } + std::string to_string () const + { + std::vector s; + for (auto i = mp_boxes->begin (); i != mp_boxes->end (); ++i) { + s.push_back (i->to_string ()); + } + return tl::join (s.begin (), s.end (), ";"); + } + private: std::set *mp_boxes; + std::set m_boxes; }; } @@ -1038,7 +1048,7 @@ public: 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::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool /*skip_shapes*/) { m_text += std::string ("new_inst(") + iter->layout ()->cell_name (inst.object ().cell_index ()); if (all) { @@ -1048,7 +1058,7 @@ public: return NI_all; } - virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) + virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool /*skip_shapes*/) { m_text += std::string ("new_inst_member(") + iter->layout ()->cell_name (inst.object ().cell_index ()) + "," + tl::to_string (always_apply * trans); if (all) { @@ -1073,9 +1083,9 @@ class ReceiverRejectingACellInstanceArray 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::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all) + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes) { - LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all); + LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all, skip_shapes); return inst.object ().cell_index () != m_rejected ? NI_all : NI_skip; } @@ -1089,9 +1099,9 @@ class ReceiverRejectingACellInstanceArrayExceptOne 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::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all) + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes) { - LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all); + LoggingReceiver::new_inst (iter, inst, always_apply, region, complex_region, all, skip_shapes); return inst.object ().cell_index () != m_rejected ? NI_all : NI_single; } @@ -1105,9 +1115,9 @@ class ReceiverRejectingACellInstance 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 &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all) + virtual bool new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes) { - LoggingReceiver::new_inst_member (iter, inst, always_apply, trans, region, complex_region, all); + LoggingReceiver::new_inst_member (iter, inst, always_apply, trans, region, complex_region, all, skip_shapes); return inst.object ().cell_index () != m_rejected || trans != m_trans_rejected; } @@ -1586,49 +1596,174 @@ TEST(12_ForMerged) db::RecursiveShapeIterator i1 (*g, c0, 0); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); i1.set_for_merged_input (true); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); std::vector lv; lv.push_back (0); i1 = db::RecursiveShapeIterator (*g, c0, lv); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); i1.set_for_merged_input (true); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); lv.push_back (1); // empty, but kills "for merged" optimization i1 = db::RecursiveShapeIterator (*g, c0, lv); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000);(100,0;1100,1100);(1200,0;2200,1100);(0,100;1000,1200)"); + } i1.set_for_merged_input (true); x = collect(i1, *g); // no longer optimized EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000);(100,0;1100,1100);(1200,0;2200,1100);(0,100;1000,1200)"); + } i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-100, 0, 100, 50)); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$3](100,0;1100,1100)/[$4](-1200,0;-100,1000)"); + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000);(100,0;1100,1100)"); + } + i1.set_for_merged_input (true); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000)"); + } i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-101, 0, 100, 50)); i1.set_overlapping (true); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000)"); + } i1.set_for_merged_input (true); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(-1200,0;-100,1000);(0,0;3000,2000)"); + } } +TEST(12b_ForMerged) +{ + std::unique_ptr g (new db::Layout ()); + g->insert_layer (0); + g->insert_layer (1); + db::Cell &c0 (g->cell (g->add_cell ())); + db::Cell &c1 (g->cell (g->add_cell ())); + + db::Box b (0, 100, 1000, 1200); + c0.shapes (0).insert (db::Box (0, 0, 3000, 2200)); + c1.shapes (0).insert (b); + + db::Trans tt; + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt)); + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (2000, 1000)), db::Vector (0, 2000), db::Vector (2000, 0), 2l, 2l)); + + std::string x; + + db::RecursiveShapeIterator i1 (*g, c0, 0); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](0,100;1000,1200)/[$2](2000,1100;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(0,100;1000,1200);(2000,1100;3000,2200);(4000,1100;5000,2200);(2000,3100;3000,4200);(4000,3100;5000,4200)"); + } + + i1.set_for_merged_input (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + { + FlatPusher f; + i1.reset (); + i1.push (&f); + EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(4000,1100;5000,2200);(2000,3100;3000,4200);(4000,3100;5000,4200)"); + } + + i1.set_for_merged_input (false); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](0,100;1000,1200)/[$2](2000,1100;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)"); + EXPECT_EQ (collect_with_copy(i1, *g), x); + + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), db::Trans (db::Vector (0, 2000)))); + + db::RecursiveShapeIterator i2 (*g, c0, 0); + + x = collect(i2, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](0,100;1000,1200)/[$2](2000,1100;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)/[$2](0,2100;1000,3200)"); + EXPECT_EQ (collect_with_copy(i2, *g), x); + + { + FlatPusher f; + i2.reset (); + i2.push (&f); + EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(0,100;1000,1200);(2000,1100;3000,2200);(4000,1100;5000,2200);(0,2100;1000,3200);(2000,3100;3000,4200);(4000,3100;5000,4200)"); + } + + i2.set_for_merged_input (true); + x = collect(i2, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2200)/[$2](2000,3100;3000,4200)/[$2](4000,1100;5000,2200)/[$2](4000,3100;5000,4200)/[$2](0,2100;1000,3200)"); + EXPECT_EQ (collect_with_copy(i2, *g), x); + + { + FlatPusher f; + i2.reset (); + i2.push (&f); + EXPECT_EQ (f.to_string (), "(0,0;3000,2200);(4000,1100;5000,2200);(0,2100;1000,3200);(2000,3100;3000,4200);(4000,3100;5000,4200)"); + } +} TEST(13_ForMergedPerformance) { diff --git a/src/rdb/rdb/rdbUtils.cc b/src/rdb/rdb/rdbUtils.cc index 6d0181162..4692b35bd 100644 --- a/src/rdb/rdb/rdbUtils.cc +++ b/src/rdb/rdb/rdbUtils.cc @@ -146,7 +146,7 @@ public: m_cell_stack.pop_back (); } - virtual new_inst_mode new_inst (const db::RecursiveShapeIterator * /*iter*/, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) + virtual new_inst_mode new_inst (const db::RecursiveShapeIterator * /*iter*/, const db::CellInstArray &inst, const db::ICplxTrans & /*always_apply*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/, bool /*skip_shapes*/) { db::cell_index_type ci = inst.object ().cell_index (); if (m_id_to_cell.find (ci) != m_id_to_cell.end ()) { From 789e183be9708d46e301aa465642c6fa41351597 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 8 Apr 2025 00:04:21 +0200 Subject: [PATCH 123/392] Shortcutting hierarchy in case of skipped shapes, this restores the original performance --- src/db/db/dbHierarchyBuilder.cc | 30 ++++++++++++++++-------------- src/db/db/dbHierarchyBuilder.h | 10 ++++------ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index aa26e0adb..bf68802fc 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -273,7 +273,7 @@ HierarchyBuilder::begin (const RecursiveShapeIterator *iter) return; } - CellMapKey key (iter->top_cell ()->cell_index (), false, std::set (), false); + CellMapKey key (iter->top_cell ()->cell_index (), false, std::set ()); m_cm_entry = m_cell_map.find (key); if (m_cm_entry == m_cell_map.end ()) { @@ -359,13 +359,6 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co } description += "DISABLED"; } - if (key.skip_shapes) { - cn += "$SKIP"; - if (! description.empty ()) { - description += "/"; - } - description += "SKIPPED"; - } new_cell = mp_target->add_cell (cn.c_str ()); @@ -391,9 +384,14 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co HierarchyBuilder::new_inst_mode HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool skip_shapes) { - if (all) { + if (skip_shapes) { - CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst.object ().cell_index ()), std::set (), skip_shapes); + // don't consider this instance if all cells are skipped + return NI_skip; + + } else if (all) { + + CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst.object ().cell_index ()), std::set ()); db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst.object ().cell_index ())); // for new cells, create this instance @@ -408,7 +406,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn } // To see the cell once, use NI_single. If we did see the cell already, skip the whole instance array. - return (! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end ()) ? NI_single : NI_skip; + return m_cells_seen.find (key) == m_cells_seen.end () ? NI_single : NI_skip; } else { @@ -421,7 +419,11 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn bool HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes) { - if (all) { + if (skip_shapes) { + + return false; + + } else if (all) { return true; @@ -435,7 +437,7 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db: return false; } - CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst_cell), clip_variant.second, skip_shapes); + CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst_cell), clip_variant.second); db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst_cell)); // for a new cell, create this instance @@ -447,7 +449,7 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db: } } - return ! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end (); + return m_cells_seen.find (key) == m_cells_seen.end (); } } diff --git a/src/db/db/dbHierarchyBuilder.h b/src/db/db/dbHierarchyBuilder.h index 2bc2793d1..d1d056718 100644 --- a/src/db/db/dbHierarchyBuilder.h +++ b/src/db/db/dbHierarchyBuilder.h @@ -249,16 +249,16 @@ public: struct CellMapKey { CellMapKey () - : original_cell (0), inactive (false), skip_shapes (false) + : original_cell (0), inactive (false) { } - CellMapKey (db::cell_index_type _original_cell, bool _inactive, const std::set &_clip_region, bool _skip_shapes) - : original_cell (_original_cell), inactive (_inactive), clip_region (_clip_region), skip_shapes (_skip_shapes) + CellMapKey (db::cell_index_type _original_cell, bool _inactive, const std::set &_clip_region) + : original_cell (_original_cell), inactive (_inactive), clip_region (_clip_region) { } bool operator== (const CellMapKey &other) const { - return original_cell == other.original_cell && inactive == other.inactive && clip_region == other.clip_region && skip_shapes == other.skip_shapes; + return original_cell == other.original_cell && inactive == other.inactive && clip_region == other.clip_region; } bool operator< (const CellMapKey &other) const @@ -266,14 +266,12 @@ public: if (original_cell != other.original_cell) { return original_cell < other.original_cell; } if (inactive != other.inactive) { return inactive < other.inactive; } if (clip_region != other.clip_region) { return clip_region < other.clip_region; } - if (skip_shapes != other.skip_shapes) { return skip_shapes < other.skip_shapes; } return false; } db::cell_index_type original_cell; bool inactive; std::set clip_region; - bool skip_shapes; }; From 8150e732af03365792ee2bc8c26b36b01db169b7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 8 Apr 2025 19:14:01 +0200 Subject: [PATCH 124/392] Hopefully fixing strm2xor finally, added a test --- src/buddies/unit_tests/bdStrmxorTests.cc | 57 +++++++++++++++++++---- src/db/db/dbHierarchyBuilder.cc | 26 ++++------- testdata/bd/strmxor_au7d.oas | Bin 0 -> 522 bytes testdata/bd/strmxor_covered1.gds | Bin 0 -> 1152 bytes testdata/bd/strmxor_covered2.gds | Bin 0 -> 1296 bytes 5 files changed, 58 insertions(+), 25 deletions(-) create mode 100644 testdata/bd/strmxor_au7d.oas create mode 100644 testdata/bd/strmxor_covered1.gds create mode 100644 testdata/bd/strmxor_covered2.gds diff --git a/src/buddies/unit_tests/bdStrmxorTests.cc b/src/buddies/unit_tests/bdStrmxorTests.cc index cde4afde0..c065bbb43 100644 --- a/src/buddies/unit_tests/bdStrmxorTests.cc +++ b/src/buddies/unit_tests/bdStrmxorTests.cc @@ -105,7 +105,7 @@ TEST(1A_Flat) "Result summary (layers without differences are not shown):\n" "\n" " Layer Output Differences (shape count)\n" - " -------------------------------------------------------\n" + " ----------------------------------------------------------------\n" " 3/0 3/0 30\n" " 6/0 6/0 41\n" " 8/1 8/1 1\n" @@ -146,8 +146,8 @@ TEST(1A_Deep) "Layer 10/0 is not present in first layout, but in second\n" "Result summary (layers without differences are not shown):\n" "\n" - " Layer Output Differences (shape count)\n" - " -------------------------------------------------------\n" + " Layer Output Differences (hierarchical shape count)\n" + " ----------------------------------------------------------------\n" " 3/0 3/0 3\n" " 6/0 6/0 314\n" " 8/1 8/1 1\n" @@ -177,7 +177,7 @@ TEST(1B_Flat) "Result summary (layers without differences are not shown):\n" "\n" " Layer Output Differences (shape count)\n" - " -------------------------------------------------------\n" + " ----------------------------------------------------------------\n" " 3/0 - 30\n" " 6/0 - 41\n" " 8/1 - 1\n" @@ -206,9 +206,9 @@ TEST(1B_Deep) "Layer 10/0 is not present in first layout, but in second\n" "Result summary (layers without differences are not shown):\n" "\n" - " Layer Output Differences (shape count)\n" - " -------------------------------------------------------\n" - " 3/0 - 30\n" + " Layer Output Differences (hierarchical shape count)\n" + " ----------------------------------------------------------------\n" + " 3/0 - 3\n" " 6/0 - 314\n" " 8/1 - 1\n" " 10/0 - (no such layer in first layout)\n" @@ -417,7 +417,7 @@ TEST(3_FlatCount) "Result summary (layers without differences are not shown):\n" "\n" " Layer Output Differences (shape count)\n" - " -------------------------------------------------------\n" + " ----------------------------------------------------------------\n" " 3/0 - 31\n" " 6/0 - 217\n" " 8/1 - 168\n" @@ -483,7 +483,7 @@ TEST(3_FlatCountHeal) "Result summary (layers without differences are not shown):\n" "\n" " Layer Output Differences (shape count)\n" - " -------------------------------------------------------\n" + " ----------------------------------------------------------------\n" " 3/0 - 30\n" " 6/0 - 41\n" " 8/1 - 1\n" @@ -756,3 +756,42 @@ TEST(6_Deep) "Layer 10/0 is not present in first layout, but in second\n" ); } + +TEST(7_OptimizeDeep) +{ + tl::CaptureChannel cap; + + std::string input_a = tl::testdata (); + input_a += "/bd/strmxor_covered1.gds"; + + std::string input_b = tl::testdata (); + input_b += "/bd/strmxor_covered2.gds"; + + std::string au = tl::testdata (); + au += "/bd/strmxor_au7d.oas"; + + std::string output = this->tmp_file ("tmp.oas"); + + const char *argv[] = { "x", "-u", input_a.c_str (), input_b.c_str (), output.c_str () }; + + EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons)); + EXPECT_EQ (cap.captured_text (), + "Result summary (layers without differences are not shown):\n" + "\n" + " Layer Output Differences (hierarchical shape count)\n" + " ----------------------------------------------------------------\n" + " 2/0 2/0 1\n" + " 3/0 3/0 8\n" + "\n" + ); +} diff --git a/src/db/db/dbHierarchyBuilder.cc b/src/db/db/dbHierarchyBuilder.cc index bf68802fc..b54354858 100644 --- a/src/db/db/dbHierarchyBuilder.cc +++ b/src/db/db/dbHierarchyBuilder.cc @@ -152,15 +152,17 @@ static std::pair > compute_clip_variant (const db::Box & } 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_wants_all_cells (false), m_trans (trans) + : mp_target (target), m_target_layer (target_layer), m_wants_all_cells (false), m_trans (trans) { set_shape_receiver (pipe); + reset (); } 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_wants_all_cells (false), m_trans (trans) + : mp_target (target), m_target_layer (0), m_wants_all_cells (false), m_trans (trans) { set_shape_receiver (pipe); + reset (); } HierarchyBuilder::~HierarchyBuilder () @@ -178,6 +180,8 @@ void HierarchyBuilder::reset () { m_initial_pass = true; + m_cm_new_entry = false; + mp_initial_cell = 0; m_cells_to_be_filled.clear (); @@ -186,7 +190,6 @@ HierarchyBuilder::reset () m_cells_seen.clear (); m_cell_stack.clear (); m_cm_entry = null_iterator; - m_cm_new_entry = false; } const std::pair & @@ -384,12 +387,7 @@ HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, co HierarchyBuilder::new_inst_mode HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all, bool skip_shapes) { - if (skip_shapes) { - - // don't consider this instance if all cells are skipped - return NI_skip; - - } else if (all) { + if (all) { CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst.object ().cell_index ()), std::set ()); db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst.object ().cell_index ())); @@ -406,7 +404,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn } // 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; + return (! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end ()) ? NI_single : NI_skip; } else { @@ -419,11 +417,7 @@ HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellIn bool HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &always_apply, const db::ICplxTrans &trans, const db::Box ®ion, const box_tree_type *complex_region, bool all, bool skip_shapes) { - if (skip_shapes) { - - return false; - - } else if (all) { + if (all) { return true; @@ -449,7 +443,7 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db: } } - return m_cells_seen.find (key) == m_cells_seen.end (); + return ! skip_shapes && m_cells_seen.find (key) == m_cells_seen.end (); } } diff --git a/testdata/bd/strmxor_au7d.oas b/testdata/bd/strmxor_au7d.oas new file mode 100644 index 0000000000000000000000000000000000000000..9d64f3f3893f25cab4cb296864bba870aa54b2f8 GIT binary patch literal 522 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKC?n3q!6L)YEF;ds&!EJR>XUoMnybM;fb~F; z;|1o9>4KX(8~>b$4qRRSKbqrK6n{_8gJqfnXwp1GBk`Zz`y_i$~x7q literal 0 HcmV?d00001 diff --git a/testdata/bd/strmxor_covered1.gds b/testdata/bd/strmxor_covered1.gds new file mode 100644 index 0000000000000000000000000000000000000000..f3d6eb6c4a02dd7bc3abe9987e76a9e581f1a214 GIT binary patch literal 1152 zcmb7@y-EW?6ot?JO%|ddCW;`7f{j4}mZC)@KPaN&4?!el9w854AvS_f5G~Rrg|&#K zg_SR&m4%fgjAwS4I9Zi-7lv=SyZ794XYN42w$Bh)cIFi>d;nRjnD6jyvAr$y9dIcrrfpadIh*R`yd&kXEak{o1e_t)y{3*U!kzif zw&{85|w1Fkl69|4cXBI>weR6m2HF1a^umR%N9zhc3n z*i_)vTBLgXqUzp4kazg`g{%34q0@hByzwwTsWHh=BiU_r?3TV0+%ERGuXjLR z1K7F*4hEV!t~ha5j9CYkZ-L@@#CicZ{s1&}oDAzHOph-_^YCaqKb)k5`Pn>?Qu1!b JsB5_{z5pQMw(_j#Xl-uH!p>uw_FxJ{4n5ddgGe|(O>HA@TAK$n*( zOdX9a-d{eC&)$q)oNX16%l!SHF=htvDQQ|=I51h4>f3FewUrTTtAP-3_pSXbD6O@7 zfYYmt*7cmwt8iQ^RYwTFy%x`{i+38utejFi%XlwJ&Z*^`D5G^fC-mse-%zSf=sh0K zi^r?(&`+6ThyLGrm$w?RD|5cYH)UVX3H@6j!|jNwBaH6XgtS*@uKZ@cdWXEf>2oBf zxyI3+^SJ%MQT)a5X8+cBVsU&>qn@Egy4$HAxAdLjcHn|}xB?Q4xlPv? z%mUpdAi;(-B!2!l@)2W08uNF|Gjoz)LmG1jK+iCcV8a@DW=}L?3^fwHJ0iVXon`Hp zguvt>uv}K^y0ESnwKw=bFmw)dvd4up9l+Xajk->T@l&cNFs8V&`cprwq?G!ZSZ6P? KmlM=jzK<_V<@z=N literal 0 HcmV?d00001 From e8d796aded4670a1c47badfa70323d8bfbf7770d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 8 Apr 2025 19:38:56 +0200 Subject: [PATCH 125/392] Fixed unit tests --- testdata/buddies/buddies.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testdata/buddies/buddies.rb b/testdata/buddies/buddies.rb index bf7afb096..7e309ac27 100644 --- a/testdata/buddies/buddies.rb +++ b/testdata/buddies/buddies.rb @@ -113,7 +113,7 @@ Warning: Layer 2/0 is not present in first layout, but in second Result summary (layers without differences are not shown): Layer Output Differences (shape count) - ------------------------------------------------------- + ---------------------------------------------------------------- 1/0 - (no such layer in second layout) 2/0 - (no such layer in first layout) From 3a752fd2c7813ba0f25a768d4b03e35bf7ca0339 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 8 Apr 2025 19:55:50 +0200 Subject: [PATCH 126/392] Adding 'total' time for -d11 in all scripts --- src/buddies/src/bd/strmclip.cc | 3 +++ src/buddies/src/bd/strmcmp.cc | 3 +++ src/buddies/src/bd/strmrun.cc | 4 ++++ src/buddies/src/bd/strmxor.cc | 3 +++ 4 files changed, 13 insertions(+) diff --git a/src/buddies/src/bd/strmclip.cc b/src/buddies/src/bd/strmclip.cc index ca36b2507..917b5a24d 100644 --- a/src/buddies/src/bd/strmclip.cc +++ b/src/buddies/src/bd/strmclip.cc @@ -29,6 +29,7 @@ #include "dbSaveLayoutOptions.h" #include "tlLog.h" #include "tlCommandLineParser.h" +#include "tlTimer.h" struct ClipData @@ -200,6 +201,8 @@ BD_PUBLIC int strmclip (int argc, char *argv[]) cmd.parse (argc, argv); + tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total"))); + clip (data); return 0; diff --git a/src/buddies/src/bd/strmcmp.cc b/src/buddies/src/bd/strmcmp.cc index 70cc712e9..ccc31c55c 100644 --- a/src/buddies/src/bd/strmcmp.cc +++ b/src/buddies/src/bd/strmcmp.cc @@ -25,6 +25,7 @@ #include "dbLayoutDiff.h" #include "dbReader.h" #include "tlCommandLineParser.h" +#include "tlTimer.h" BD_PUBLIC int strmcmp (int argc, char *argv[]) { @@ -141,6 +142,8 @@ BD_PUBLIC int strmcmp (int argc, char *argv[]) throw tl::Exception ("Both -ta|--top-a and -tb|--top-b top cells must be given"); } + tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total"))); + db::Layout layout_a; db::Layout layout_b; diff --git a/src/buddies/src/bd/strmrun.cc b/src/buddies/src/bd/strmrun.cc index fa15cf018..d13abefe7 100644 --- a/src/buddies/src/bd/strmrun.cc +++ b/src/buddies/src/bd/strmrun.cc @@ -28,6 +28,7 @@ #include "tlLog.h" #include "tlCommandLineParser.h" #include "tlFileUtils.h" +#include "tlTimer.h" #include "rba.h" #include "pya.h" #include "gsi.h" @@ -97,5 +98,8 @@ BD_PUBLIC int strmrun (int argc, char *argv[]) lym::Macro macro; macro.load_from (script); macro.set_file_path (script); + + tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total"))); + return macro.run (); } diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index a18fc36cd..645ae9a2c 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -32,6 +32,7 @@ #include "gsiExpression.h" #include "tlCommandLineParser.h" #include "tlThreads.h" +#include "tlTimer.h" namespace { @@ -455,6 +456,8 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) } } + tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Total"))); + db::Layout layout_a; db::Layout layout_b; From 0542ef835ae6015dded16dde4704d29308d1cbe6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 8 Apr 2025 21:14:19 +0200 Subject: [PATCH 127/392] strm2xor: parallelize by layer and not internally with -u --- src/buddies/src/bd/strmxor.cc | 267 ++++++++++++++++++++++------------ 1 file changed, 173 insertions(+), 94 deletions(-) diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 645ae9a2c..26cf4e3f9 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -32,6 +32,7 @@ #include "gsiExpression.h" #include "tlCommandLineParser.h" #include "tlThreads.h" +#include "tlThreadedWorkers.h" #include "tlTimer.h" namespace { @@ -320,7 +321,8 @@ struct XORData dont_summarize_missing_layers (false), silent (false), no_summary (false), threads (0), tile_size (0.0), heal_results (false), - output_layout (0), output_cell (0) + output_layout (0), output_cell (0), + layers_missing (0) { } db::Layout *layout_a, *layout_b; @@ -337,6 +339,8 @@ struct XORData db::cell_index_type output_cell; std::map, db::LPLogicalLessFunc> l2l_map; std::map, ResultDescriptor> *results; + mutable int layers_missing; + mutable tl::Mutex lock; }; } @@ -769,15 +773,170 @@ bool run_tiled_xor (const XORData &xor_data) return result; } -bool run_deep_xor (const XORData &xor_data) -{ - db::DeepShapeStore dss; - dss.set_threads (xor_data.threads); +class XORJob + : public tl::JobBase +{ +public: + XORJob (int nworkers) + : tl::JobBase (nworkers) + { + } + + virtual tl::Worker *create_worker (); +}; + +class XORWorker + : public tl::Worker +{ +public: + XORWorker (XORJob *job); + void perform_task (tl::Task *task); + + db::DeepShapeStore &dss () + { + return m_dss; + } + +private: + XORJob *mp_job; + db::DeepShapeStore m_dss; +}; + +class XORTask + : public tl::Task +{ +public: + XORTask (const XORData *xor_data, const db::LayerProperties &layer_props, int la, int lb, double dbu) + : mp_xor_data (xor_data), m_layer_props (layer_props), m_la (la), m_lb (lb), m_dbu (dbu) + { + // .. nothing yet .. + } + + void run (XORWorker *worker) const + { + if ((m_la < 0 || m_lb < 0) && ! mp_xor_data->dont_summarize_missing_layers) { + + if (m_la < 0) { + (mp_xor_data->silent ? tl::log : tl::warn) << "Layer " << m_layer_props.to_string () << " is not present in first layout, but in second"; + } else { + (mp_xor_data->silent ? tl::log : tl::warn) << "Layer " << m_layer_props.to_string () << " is not present in second layout, but in first"; + } + + tl::MutexLocker locker (&mp_xor_data->lock); + + mp_xor_data->layers_missing += 1; + + int tol_index = 0; + for (std::vector::const_iterator t = mp_xor_data->tolerances.begin (); t != mp_xor_data->tolerances.end (); ++t) { + + ResultDescriptor &result = mp_xor_data->results->insert (std::make_pair (std::make_pair (tol_index, m_layer_props), ResultDescriptor ())).first->second; + result.layer_a = m_la; + result.layer_b = m_lb; + result.layout = mp_xor_data->output_layout; + result.top_cell = mp_xor_data->output_cell; + + ++tol_index; + + } + + } else { + + tl::SelfTimer timer (tl::verbosity () >= 11, "XOR on layer " + m_layer_props.to_string ()); + + db::RecursiveShapeIterator ri_a, ri_b; + + if (m_la >= 0) { + ri_a = db::RecursiveShapeIterator (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), m_la); + ri_a.set_for_merged_input (true); + } + + if (m_lb >= 0) { + ri_b = db::RecursiveShapeIterator (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), m_lb); + ri_b.set_for_merged_input (true); + } + + db::Region in_a (ri_a, worker->dss (), db::ICplxTrans (mp_xor_data->layout_a->dbu () / m_dbu)); + db::Region in_b (ri_b, worker->dss (), db::ICplxTrans (mp_xor_data->layout_b->dbu () / m_dbu)); + + db::Region xor_res; + { + tl::SelfTimer timer (tl::verbosity () >= 21, "Basic XOR on layer " + m_layer_props.to_string ()); + xor_res = in_a ^ in_b; + } + + int tol_index = 0; + for (std::vector::const_iterator t = mp_xor_data->tolerances.begin (); t != mp_xor_data->tolerances.end (); ++t) { + + db::LayerProperties lp = m_layer_props; + if (lp.layer >= 0) { + lp.layer += tol_index * mp_xor_data->tolerance_bump; + } + + if (*t > db::epsilon) { + tl::SelfTimer timer (tl::verbosity () >= 21, "Tolerance " + tl::to_string (*t) + " on layer " + m_layer_props.to_string ()); + xor_res.size (-db::coord_traits::rounded (0.5 * *t / m_dbu)); + xor_res.size (db::coord_traits::rounded (0.5 * *t / m_dbu)); + } + + { + tl::MutexLocker locker (&mp_xor_data->lock); + + ResultDescriptor &result = mp_xor_data->results->insert (std::make_pair (std::make_pair (tol_index, m_layer_props), ResultDescriptor ())).first->second; + result.layer_a = m_la; + result.layer_b = m_lb; + result.layout = mp_xor_data->output_layout; + result.top_cell = mp_xor_data->output_cell; + + if (mp_xor_data->output_layout) { + result.layer_output = result.layout->insert_layer (lp); + xor_res.insert_into (mp_xor_data->output_layout, mp_xor_data->output_cell, result.layer_output); + } else { + result.shape_count = xor_res.hier_count (); + } + } + + ++tol_index; + + } + + } + } + +private: + const XORData *mp_xor_data; + const db::LayerProperties &m_layer_props; + int m_la; + int m_lb; + double m_dbu; +}; + +XORWorker::XORWorker (XORJob *job) + : tl::Worker (), mp_job (job) +{ // TODO: this conflicts with the "set_for_merged_input" optimization below. // It seems not to be very effective then. Why? - dss.set_wants_all_cells (true); // saves time for less cell mapping operations + m_dss.set_wants_all_cells (true); // saves time for less cell mapping operations +} +void +XORWorker::perform_task (tl::Task *task) +{ + XORTask *xor_task = dynamic_cast (task); + if (xor_task) { + xor_task->run (this); + } +} + +tl::Worker * +XORJob::create_worker () +{ + return new XORWorker (this); +} + + +bool run_deep_xor (const XORData &xor_data) +{ double dbu = std::min (xor_data.layout_a->dbu (), xor_data.layout_b->dbu ()); if (tl::verbosity () >= 20) { @@ -790,98 +949,18 @@ bool run_deep_xor (const XORData &xor_data) xor_data.output_layout->dbu (dbu); } - bool result = true; - - int index = 1; + XORJob job (xor_data.threads); for (std::map >::const_iterator ll = xor_data.l2l_map.begin (); ll != xor_data.l2l_map.end (); ++ll) { - - if ((ll->second.first < 0 || ll->second.second < 0) && ! xor_data.dont_summarize_missing_layers) { - - if (ll->second.first < 0) { - (xor_data.silent ? tl::log : tl::warn) << "Layer " << ll->first.to_string () << " is not present in first layout, but in second"; - } else { - (xor_data.silent ? tl::log : tl::warn) << "Layer " << ll->first.to_string () << " is not present in second layout, but in first"; - } - - result = false; - - int tol_index = 0; - for (std::vector::const_iterator t = xor_data.tolerances.begin (); t != xor_data.tolerances.end (); ++t) { - - ResultDescriptor &result = xor_data.results->insert (std::make_pair (std::make_pair (tol_index, ll->first), ResultDescriptor ())).first->second; - result.layer_a = ll->second.first; - result.layer_b = ll->second.second; - result.layout = xor_data.output_layout; - result.top_cell = xor_data.output_cell; - - ++tol_index; - - } - - } else { - - tl::SelfTimer timer (tl::verbosity () >= 11, "XOR on layer " + ll->first.to_string ()); - - db::RecursiveShapeIterator ri_a, ri_b; - - if (ll->second.first >= 0) { - ri_a = db::RecursiveShapeIterator (*xor_data.layout_a, xor_data.layout_a->cell (xor_data.cell_a), ll->second.first); - ri_a.set_for_merged_input (true); - } - - if (ll->second.second >= 0) { - ri_b = db::RecursiveShapeIterator (*xor_data.layout_b, xor_data.layout_b->cell (xor_data.cell_b), ll->second.second); - ri_b.set_for_merged_input (true); - } - - db::Region in_a (ri_a, dss, db::ICplxTrans (xor_data.layout_a->dbu () / dbu)); - db::Region in_b (ri_b, dss, db::ICplxTrans (xor_data.layout_b->dbu () / dbu)); - - db::Region xor_res; - { - tl::SelfTimer timer (tl::verbosity () >= 21, "Basic XOR on layer " + ll->first.to_string ()); - xor_res = in_a ^ in_b; - } - - int tol_index = 0; - for (std::vector::const_iterator t = xor_data.tolerances.begin (); t != xor_data.tolerances.end (); ++t) { - - db::LayerProperties lp = ll->first; - if (lp.layer >= 0) { - lp.layer += tol_index * xor_data.tolerance_bump; - } - - ResultDescriptor &result = xor_data.results->insert (std::make_pair (std::make_pair (tol_index, ll->first), ResultDescriptor ())).first->second; - result.layer_a = ll->second.first; - result.layer_b = ll->second.second; - result.layout = xor_data.output_layout; - result.top_cell = xor_data.output_cell; - - if (*t > db::epsilon) { - tl::SelfTimer timer (tl::verbosity () >= 21, "Tolerance " + tl::to_string (*t) + " on layer " + ll->first.to_string ()); - xor_res.size (-db::coord_traits::rounded (0.5 * *t / dbu)); - xor_res.size (db::coord_traits::rounded (0.5 * *t / dbu)); - } - - if (xor_data.output_layout) { - result.layer_output = result.layout->insert_layer (lp); - xor_res.insert_into (xor_data.output_layout, xor_data.output_cell, result.layer_output); - } else { - result.shape_count = xor_res.hier_count (); - } - - ++tol_index; - - } - - } - - ++index; - + job.schedule (new XORTask (&xor_data, ll->first, ll->second.first, ll->second.second, dbu)); } - // Determines the output status + job.start (); + job.wait (); + + // Determine the output status + + bool result = (xor_data.layers_missing == 0); for (std::map, ResultDescriptor>::const_iterator r = xor_data.results->begin (); r != xor_data.results->end () && result; ++r) { result = r->second.is_empty (); } From a27fd3e0beaca124f0b016b0ece5e90859d10271 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 8 Apr 2025 22:40:04 +0200 Subject: [PATCH 128/392] Drop OASIS warning about ghost cells and print a DEF reader warning if a foreign cell cannot be substituted --- src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc | 8 ++++++++ src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc | 1 - 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc index c8d3d840c..e86f79ce3 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc @@ -276,6 +276,14 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti } + // Warn about cells that could not be resolved + for (std::map::iterator f = foreign_cells.begin (); f != foreign_cells.end (); ++f) { + if (f->second != seen) { + importer.warn (tl::sprintf (tl::to_string (tr ("Could not find a substitution layout for foreign cell '%s'")), + f->first)); + } + } + } state.finish (layout); diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index 6a7f5afee..548a4e447 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -1671,7 +1671,6 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save // skip cell body if the cell is not to be written if (skip_cell_body (cref)) { - tl::warn << tl::to_string (tr ("Cannot write ghost cell to OASIS - skipping cell: ")) << layout.cell_name (*cell); continue; } From 0ec8e181739a5303cb68be2f76014c6b90c6a977 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 8 Apr 2025 23:52:50 +0200 Subject: [PATCH 129/392] refining the DEF warning on missing foreign cell --- src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc index e86f79ce3..06e42a0c7 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc @@ -278,7 +278,7 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti // Warn about cells that could not be resolved for (std::map::iterator f = foreign_cells.begin (); f != foreign_cells.end (); ++f) { - if (f->second != seen) { + if (f->second != seen && layout.cell (f->second).is_ghost_cell ()) { importer.warn (tl::sprintf (tl::to_string (tr ("Could not find a substitution layout for foreign cell '%s'")), f->first)); } From 09329442f0781a180b0145d6bb7a889c50f5a70a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 9 Apr 2025 23:30:13 +0200 Subject: [PATCH 130/392] Fixing issue #2019 (build issue against Qt 6.9) --- scripts/mkqtdecl6/mkqtdecl.conf | 3 +- src/gsiqt/qt6/QtXml/gsiDeclQDomNodeList.cc | 49 ++++------------------ 2 files changed, 11 insertions(+), 41 deletions(-) diff --git a/scripts/mkqtdecl6/mkqtdecl.conf b/scripts/mkqtdecl6/mkqtdecl.conf index 427141e2b..4b7c15fa5 100644 --- a/scripts/mkqtdecl6/mkqtdecl.conf +++ b/scripts/mkqtdecl6/mkqtdecl.conf @@ -547,7 +547,7 @@ drop_method "QDebug", /QDebug::operator\s*<<\((?!const\s+QString\s*&)/ # don't m drop_method "", /::operator\s*<<\(QDebug\s*\w*\s*,\s*(?!const\s+QString\s*&)/ # don't map the others right now - too many (TODO: how to map?) drop_method "QNoDebug", /QNoDebug::operator<add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_excl__eq__c2484 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QDomNodeList &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QDomNodeList *)cls)->operator!= (arg1)); -} - - // QDomNodeList &QDomNodeList::operator=(const QDomNodeList &) @@ -191,25 +178,6 @@ static void _call_f_operator_eq__2484 (const qt_gsi::GenericMethod * /*decl*/, v } -// bool QDomNodeList::operator==(const QDomNodeList &) - - -static void _init_f_operator_eq__eq__c2484 (qt_gsi::GenericMethod *decl) -{ - static gsi::ArgSpecBase argspec_0 ("arg1"); - decl->add_arg (argspec_0); - decl->set_return (); -} - -static void _call_f_operator_eq__eq__c2484 (const qt_gsi::GenericMethod * /*decl*/, void *cls, gsi::SerialArgs &args, gsi::SerialArgs &ret) -{ - __SUPPRESS_UNUSED_WARNING(args); - tl::Heap heap; - const QDomNodeList &arg1 = gsi::arg_reader() (args, heap); - ret.write ((bool)((QDomNodeList *)cls)->operator== (arg1)); -} - - // int QDomNodeList::size() @@ -238,14 +206,15 @@ static gsi::Methods methods_QDomNodeList () { methods += new qt_gsi::GenericMethod ("isEmpty?", "@brief Method bool QDomNodeList::isEmpty()\n", true, &_init_f_isEmpty_c0, &_call_f_isEmpty_c0); methods += new qt_gsi::GenericMethod ("item", "@brief Method QDomNode QDomNodeList::item(int index)\n", true, &_init_f_item_c767, &_call_f_item_c767); methods += new qt_gsi::GenericMethod ("length", "@brief Method int QDomNodeList::length()\n", true, &_init_f_length_c0, &_call_f_length_c0); - methods += new qt_gsi::GenericMethod ("!=", "@brief Method bool QDomNodeList::operator!=(const QDomNodeList &)\n", true, &_init_f_operator_excl__eq__c2484, &_call_f_operator_excl__eq__c2484); methods += new qt_gsi::GenericMethod ("assign", "@brief Method QDomNodeList &QDomNodeList::operator=(const QDomNodeList &)\n", false, &_init_f_operator_eq__2484, &_call_f_operator_eq__2484); - methods += new qt_gsi::GenericMethod ("==", "@brief Method bool QDomNodeList::operator==(const QDomNodeList &)\n", true, &_init_f_operator_eq__eq__c2484, &_call_f_operator_eq__eq__c2484); methods += new qt_gsi::GenericMethod ("size", "@brief Method int QDomNodeList::size()\n", true, &_init_f_size_c0, &_call_f_size_c0); return methods; } gsi::Class decl_QDomNodeList ("QtXml", "QDomNodeList", + gsi::method_ext("==", &QDomNodeList_operator_eq, gsi::arg ("other"), "@brief Method bool QDomNodeList::operator==(const QDomNodeList &) const") + + gsi::method_ext("!=", &QDomNodeList_operator_ne, gsi::arg ("other"), "@brief Method bool QDomNodeList::operator!=(const QDomNodeList &) const") ++ methods_QDomNodeList (), "@qt\n@brief Binding of QDomNodeList"); From fa618a5b761cf3267128dac35240641b8968386b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 9 Apr 2025 23:35:09 +0200 Subject: [PATCH 131/392] Fixing the strict weak ordering issue inside the edge processor --- src/db/db/dbEdgeProcessor.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/db/dbEdgeProcessor.cc b/src/db/db/dbEdgeProcessor.cc index fd3ffb113..c26b5569f 100644 --- a/src/db/db/dbEdgeProcessor.cc +++ b/src/db/db/dbEdgeProcessor.cc @@ -1321,7 +1321,7 @@ struct edge_xmin_at_yinterval_double_compare { if (edge_xmax (a) < edge_xmin (b)) { return true; - } else if (edge_xmin (a) >= edge_xmax (b)) { + } else if (edge_xmin (a) > edge_xmax (b)) { return false; } else { C xa = edge_xmin_at_yinterval_double (a, m_y1, m_y2); From 910f697d0b465d0f7cd94f9b946ec5ba0a21fda3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 10 Apr 2025 19:15:12 +0200 Subject: [PATCH 132/392] WIP --- src/db/db/dbPolygonGraph.cc | 69 ++++++++++++++++++++---- src/db/db/dbPolygonGraph.h | 4 +- src/db/unit_tests/dbPolygonGraphTests.cc | 2 +- 3 files changed, 62 insertions(+), 13 deletions(-) diff --git a/src/db/db/dbPolygonGraph.cc b/src/db/db/dbPolygonGraph.cc index c7ae4a349..f745b0da2 100644 --- a/src/db/db/dbPolygonGraph.cc +++ b/src/db/db/dbPolygonGraph.cc @@ -348,8 +348,6 @@ GPolygon::init () mp_e.reserve (e.size ()); mp_e.push_back (e.front ()); - // NOTE: we assume the edges follow the clockwise orientation - mp_e.back ()->set_right (this); mp_v.reserve (e.size ()); mp_v.push_back (mp_e.back ()->v1 ()); @@ -365,8 +363,6 @@ GPolygon::init () tl_assert (i != v2e.end () && i->first == v && i->second != mp_e.back ()); v2e.erase (i); mp_e.push_back (i->second); - // NOTE: we assume the edges follow the clockwise orientation - mp_e.back ()->set_right (this); v = i->second->other (v); i = v2e.find (v); @@ -379,6 +375,33 @@ GPolygon::init () } } + + // establish clockwise order of the vertexes + + double area = 0.0; + const db::GVertex *vm1 = vertex (-1), *v0; + for (auto i = mp_v.begin (); i != mp_v.end (); ++i) { + v0 = *i; + area += db::vprod (*vm1 - db::DPoint (), *v0 - *vm1); + vm1 = v0; + } + + if (area > db::epsilon) { + std::reverse (mp_v.begin (), mp_v.end ()); + std::reverse (mp_e.begin (), mp_e.end ()); + } + + // link the polygon to the edges + + for (size_t i = 0; i < size (); ++i) { + db::GVertex *v = mp_v[i]; + db::GPolygonEdge *e = mp_e[i]; + if (e->v1 () == v) { + e->set_right (this); + } else { + e->set_left (this); + } + } } GPolygon::~GPolygon () @@ -602,9 +625,12 @@ PolygonGraph::remove_polygon (db::GPolygon *poly) } } +#if 0 // @@@ void -PolygonGraph::insert_polygon (const db::DPolygon &polygon) +PolygonGraph::convex_decompose (const db::DPolygon &polygon) { + clear (); + if (polygon.begin_edge ().at_end ()) { return; } @@ -612,23 +638,46 @@ PolygonGraph::insert_polygon (const db::DPolygon &polygon) std::vector edges; for (unsigned int c = 0; c < polygon.holes () + 1; ++c) { - const db::DPolygon::contour_type &ctr = polygon.contour (c); + + const db::DSimplePolygon::contour_type &ctr = polygon.contour (c); + db::GVertex *v0 = 0, *vv, *v; - for (auto p = ctr.begin (); p != ctr.end (); ++p) { - v = create_vertex ((*p).x (), (*p).y ()); + size_t n = ctr.size (); + for (size_t i = 0; i < n; ++i) { + + db::DPoint pm1 = ctr [i > 0 ? i - 1 : n - 1]; + db::DPoint pp1 = ctr [i + 1 < n ? i + 1 : 0]; + db::DPoint p = ctr [i]; + + bool is_convex = db::vprod_sign (p - pm1, pp1 - p); + // @@@ + + v = create_vertex (p.x (), p.y ()); if (! v0) { v0 = v; } else { edges.push_back (create_edge (vv, v)); } + vv = v; + } + if (v0 && v0 != v) { edges.push_back (create_edge (v, v0)); } - } - create_polygon (edges.begin (), edges.end ()); + } +} +#endif + +void +PolygonGraph::convex_decompose (const db::DPolygon &poly) +{ + + // @@@ + + } std::string diff --git a/src/db/db/dbPolygonGraph.h b/src/db/db/dbPolygonGraph.h index cfbdee450..d296b886f 100644 --- a/src/db/db/dbPolygonGraph.h +++ b/src/db/db/dbPolygonGraph.h @@ -684,9 +684,9 @@ public: ~PolygonGraph (); /** - * @brief Inserts the given polygon + * @brief Creates a convex decomposition for the given polygon */ - void insert_polygon (const db::DPolygon &box); + void convex_decompose (const DPolygon &poly); /** * @brief Returns a string representation of the polygon graph. diff --git a/src/db/unit_tests/dbPolygonGraphTests.cc b/src/db/unit_tests/dbPolygonGraphTests.cc index 9fdb91a2f..edf152942 100644 --- a/src/db/unit_tests/dbPolygonGraphTests.cc +++ b/src/db/unit_tests/dbPolygonGraphTests.cc @@ -42,7 +42,7 @@ TEST(basic) db::DBox box (0, 0, 100.0, 200.0); TestablePolygonGraph pg; - pg.insert_polygon (db::DPolygon (box)); + // @@@ pg.insert_polygon (db::DSimplePolygon (box)); // @@@ tl::info << pg.to_string (); From 6f9a2da04ad15084fb9341d85580cf02fba249e0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 10 Apr 2025 23:33:50 +0200 Subject: [PATCH 133/392] WIP --- src/db/unit_tests/dbTrianglesTests.cc | 290 ++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) diff --git a/src/db/unit_tests/dbTrianglesTests.cc b/src/db/unit_tests/dbTrianglesTests.cc index df7c5e357..ba8875d3d 100644 --- a/src/db/unit_tests/dbTrianglesTests.cc +++ b/src/db/unit_tests/dbTrianglesTests.cc @@ -1076,3 +1076,293 @@ TEST(triangulate_with_vertexes) } } } + +// @@@@@@@@@@q + +// Hertel-Mehlhorn :) +TEST(JoinTriangles) +{ + db::Point contour[] = { + db::Point (0, 0), + db::Point (0, 100), + db::Point (1000, 100), + db::Point (1000, 500), + db::Point (1100, 500), + db::Point (1100, 100), + db::Point (2100, 100), + db::Point (2100, -1000), + db::Point (1050, -1000), + db::Point (1050, 0) + }; + + db::Polygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + + double dbu = 0.001; + + db::Triangles::TriangulateParameters param; + param.min_b = 0.0; + + TestableTriangles tri; + db::CplxTrans trans = db::DCplxTrans (dbu) * db::CplxTrans (db::Trans (db::Point () - poly.box ().center ())); + trans = db::CplxTrans (dbu); // @@@ + tri.triangulate (poly, param, trans); + + // @@@ use edge "level" + // @@@ use edges from heap + std::unordered_set left; + + for (auto it = tri.begin (); it != tri.end (); ++it) { + for (unsigned int i = 0; i < 3; ++i) { + db::TriangleEdge *e = it->edge (i); + if (e->is_segment ()) { + left.insert (e); + } + } + } + + std::unordered_map > concave_corners; // @@@ + + while (! left.empty ()) { + + // First segment for a new loop + db::TriangleEdge *segment = *left.begin (); + + // walk along the segments in clockwise direction. Find concave + // vertexes and create new vertexes perpendicular to the incoming + // and outgoing edge. + + db::TriangleEdge *start_segment = segment; + + db::Vertex *vfrom = segment->v1 (); + db::Vertex *vto = segment->v2 (); + if (! segment->right ()) { + std::swap (vfrom, vto); + } + + do { + + left.erase (segment); + + double vp_max = 0.0; + int vp_max_sign = 0; + std::pair edges; + + db::TriangleEdge *prev_segment = segment; + segment = 0; + db::Vertex *vn = 0; + + // Look for the outgoing edge. We pick the one which bends "most", favoring + // convex corners. Multiple edges per vertex are possible is corner cases such as the + // "hourglass" configuration. + + for (auto e = vto->begin_edges (); e != vto->end_edges (); ++e) { + + db::TriangleEdge *en = *e; + if (en != prev_segment && en->is_segment ()) { + + tl_assert (left.find (en) != left.end () || en == start_segment); // @@@ + db::Vertex *v = en->other (vto); + db::DEdge e1 (*vfrom, *vto); + db::DEdge e2 (*vto, *v); + double vp = double (db::vprod (e1, e2)) / (e1.double_length () * e2.double_length ()); + + // vp > 0: concave, vp < 0: convex + + if (! segment || vp > vp_max) { + vp_max_sign = db::vprod_sign (e1, e2); + edges.first = e1; + edges.second = e2; + vp_max = vp; + segment = en; + vn = v; + } + + } + + } + + tl_assert (segment != 0); // @@@ + + if (vp_max_sign > 0) { + // concave corner + concave_corners.insert (std::make_pair (vto, edges)); + } + + vfrom = vto; + vto = vn; + + } while (segment != start_segment); + + } + + // @@@ sort convex vertexes + + std::vector > new_points; + + // Cut off pieces from convex corners by creating connections to points perpendicular + // to the incoming and outgoing edges + + for (auto cc = concave_corners.begin (); cc != concave_corners.end (); ++cc) { + + auto vtri = cc->first->triangles (); // @@@ slow? + + std::vector nvv, nvv_next; + + for (unsigned int ei = 0; ei < 2; ++ei) { + + db::DEdge ee = (ei == 0 ? cc->second.first : cc->second.second); + db::Vertex *v0 = cc->first; + + for (auto it = vtri.begin (); it != vtri.end (); ++it) { + + // Search for a segment in the direction perpendicular to the edge + nvv.clear (); + nvv.push_back (v0); + db::Triangle *t = *it; + + while (! nvv.empty ()) { + + nvv_next.clear (); + + for (auto iv = nvv.begin (); iv != nvv.end (); ++iv) { + + db::Vertex *v = *iv; + db::TriangleEdge *oe = t->opposite (v); + db::Triangle *tt = oe->other (t); + db::Vertex *v1 = oe->v1 (); + db::Vertex *v2 = oe->v2 (); + + if (db::vprod_sign (*v2 - *v, ee.d ()) >= 0 && db::vprod_sign (*v1 - *v, ee.d ()) >= 0 && + db::sprod_sign (*v2 - *v, ee.d ()) * db::sprod_sign (*v1 - *v, ee.d ()) < 0) { + + // this triangle covers the normal vector of e1 -> stop here or continue searching in that direction + if (oe->is_segment ()) { + auto cp = oe->edge ().cut_point (db::DEdge (*v0, *v0 + db::DVector (ee.dy (), -ee.dx ()))); + if (cp.first) { + new_points.push_back (std::make_pair (cp.second, v0)); + } + } else { + // continue searching in that direction + nvv_next.push_back (v1); + nvv_next.push_back (v2); + t = tt; + } + + break; + + } + + } + + nvv.swap (nvv_next); + + } + + } + + } + + } + + // Insert the new points and make connections + + std::unordered_set > clip_pairs; + + // @@@ TODO: what to do in case of equal new_points? + for (auto p = new_points.begin (); p != new_points.end (); ++p) { + auto v = tri.insert_point (p->first); + clip_pairs.insert (std::make_pair (v, p->second)); + } + + // Combine triangles, but don't cross clip edges + + db::Region result; + + std::unordered_set left_triangles; + for (auto it = tri.begin (); it != tri.end (); ++it) { + left_triangles.insert (it.operator-> ()); + } + + while (! left_triangles.empty ()) { + + std::unordered_map edges; + + const db::Triangle *tri = *left_triangles.begin (); + std::vector queue, next_queue; + queue.push_back (tri); + + while (! queue.empty ()) { + + next_queue.clear (); + + for (auto q = queue.begin (); q != queue.end (); ++q) { + + left_triangles.erase (*q); + + for (unsigned int i = 0; i < 3; ++i) { + + const db::TriangleEdge *e = (*q)->edge (i); + const db::Triangle *qq = e->other (*q); + + bool is_outer_edge = false; + if (! qq) { + is_outer_edge = true; + } else if (clip_pairs.find (std::make_pair (e->v1 (), e->v2 ())) != clip_pairs.end () || clip_pairs.find (std::make_pair (e->v2 (), e->v1 ())) != clip_pairs.end ()) { + is_outer_edge = true; + } else if (concave_corners.find (e->v1 ()) != concave_corners.end () && concave_corners.find (e->v2 ()) != concave_corners.end ()) { + is_outer_edge = true; + } + + if (is_outer_edge) { + if (e->right () == *q) { + edges.insert (std::make_pair (e->v1 (), e->v2 ())); + } else { + edges.insert (std::make_pair (e->v2 (), e->v1 ())); + } + } else if (left_triangles.find (qq) != left_triangles.end ()) { + next_queue.push_back (qq); + } + } + + } + + queue.swap (next_queue); + + } + + // stitch the loop points into a polygon + + tl_assert (! edges.empty ()); + + const db::Vertex *v = edges.begin ()->first; + const db::Vertex *v0 = v; + const db::Vertex *vv = edges.begin ()->second; + + std::vector polygon_points; + + do { + + polygon_points.push_back (*v); + + auto i = edges.find (vv); + tl_assert (i != edges.end ()); + + v = i->first; + vv = i->second; + + } while (v != v0); + + db::DPolygon poly; + poly.assign_hull (polygon_points.begin (), polygon_points.end ()); + result.insert (trans.inverted () * poly); + + } + + // @@@ + // tri.dump ("debug.gds"); + result.write ("debug.gds"); + +} + +// @@@@@@@@@@q From 7b069d17c3371905a15aa3d37131b10148fe38b8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 12 Apr 2025 22:30:00 +0200 Subject: [PATCH 134/392] WIP --- src/db/unit_tests/dbTrianglesTests.cc | 348 ++++++++++++++++++-------- 1 file changed, 239 insertions(+), 109 deletions(-) diff --git a/src/db/unit_tests/dbTrianglesTests.cc b/src/db/unit_tests/dbTrianglesTests.cc index ba8875d3d..fe09c3817 100644 --- a/src/db/unit_tests/dbTrianglesTests.cc +++ b/src/db/unit_tests/dbTrianglesTests.cc @@ -1077,11 +1077,147 @@ TEST(triangulate_with_vertexes) } } -// @@@@@@@@@@q +// @@@@@@@@@@@ + +struct SortAngleAndEdgesByEdgeLength +{ + typedef std::list > angle_and_edges_list; + + bool operator() (const angle_and_edges_list::iterator &a, const angle_and_edges_list::iterator &b) const + { + double la = a->second->edge ().double_sq_length (); + double lb = b->second->edge ().double_sq_length (); + if (fabs (la - lb) > db::epsilon) { + return la < lb; + } else { + return a->second->edge ().less (b->second->edge ()); + } + } +}; + +struct ConcaveCorner +{ + ConcaveCorner () + : corner (0), incoming (0), outgoing (0) + { + // .. nothing yet .. + } + + ConcaveCorner (db::Vertex *_corner, db::TriangleEdge *_incoming, db::TriangleEdge *_outgoing) + : corner (_corner), incoming (_incoming), outgoing (_outgoing) + { + // .. nothing yet .. + } + + db::Vertex *corner; + db::TriangleEdge *incoming, *outgoing; +}; + +db::TriangleEdge *find_outgoing_segment (db::Vertex *vertex, db::TriangleEdge *incoming, bool &is_concave) +{ + db::Vertex *vfrom = incoming->other (vertex); + db::DEdge e1 (*vfrom, *vertex); + + double vp_max = 0.0; + int vp_max_sign = 0; + db::TriangleEdge *outgoing = 0; + + // Look for the outgoing edge. We pick the one which bends "most", favoring + // convex corners. Multiple edges per vertex are possible is corner cases such as the + // "hourglass" configuration. + + for (auto e = vertex->begin_edges (); e != vertex->end_edges (); ++e) { + + db::TriangleEdge *en = *e; + if (en != incoming && en->is_segment ()) { + + db::Vertex *v = en->other (vertex); + db::DEdge e2 (*vertex, *v); + double vp = double (db::vprod (e1, e2)) / (e1.double_length () * e2.double_length ()); + + // vp > 0: concave, vp < 0: convex + + if (! outgoing || vp > vp_max) { + vp_max_sign = db::vprod_sign (e1, e2); + vp_max = vp; + outgoing = en; + } + + } + + } + + is_concave = (vp_max_sign > 0); + + tl_assert (outgoing != 0); + return outgoing; +} + +void collect_concave_vertexes (db::Triangles &tris, std::vector &concave_vertexes) +{ + concave_vertexes.clear (); + + // @@@ use edge "level" + // @@@ use edges from heap + std::unordered_set left; + + for (auto it = tris.begin (); it != tris.end (); ++it) { + for (unsigned int i = 0; i < 3; ++i) { + db::TriangleEdge *e = it->edge (i); + if (e->is_segment ()) { + left.insert (e); + } + } + } + + while (! left.empty ()) { + + // First segment for a new loop + db::TriangleEdge *segment = *left.begin (); + + // walk along the segments in clockwise direction. Find concave + // vertexes and create new vertexes perpendicular to the incoming + // and outgoing edge. + + db::TriangleEdge *start_segment = segment; + db::Vertex *vto = segment->right () ? segment->v2 () : segment->v1 (); + + do { + + left.erase (segment); + + db::TriangleEdge *prev_segment = segment; + + bool is_concave = false; + segment = find_outgoing_segment (vto, prev_segment, is_concave); + + if (is_concave) { + concave_vertexes.push_back (ConcaveCorner (vto, prev_segment, segment)); + } + + vto = segment->other (vto); + + } while (segment != start_segment); + + } +} // Hertel-Mehlhorn :) TEST(JoinTriangles) { +#if 1 + db::Point contour[] = { + db::Point (0, 0), + db::Point (0, 100), + db::Point (1000, 100), + db::Point (1000, 500), + db::Point (1100, 500), + db::Point (1100, 100), + db::Point (2100, 100), + db::Point (2100, 0) + }; +#else + db::Point contour[] = { db::Point (0, 0), db::Point (0, 100), @@ -1094,10 +1230,13 @@ TEST(JoinTriangles) db::Point (1050, -1000), db::Point (1050, 0) }; +#endif db::Polygon poly; poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + // @@@ don't to anything if already convex + double dbu = 0.001; db::Triangles::TriangulateParameters param; @@ -1108,111 +1247,33 @@ TEST(JoinTriangles) trans = db::CplxTrans (dbu); // @@@ tri.triangulate (poly, param, trans); - // @@@ use edge "level" - // @@@ use edges from heap - std::unordered_set left; + std::vector concave_vertexes; + collect_concave_vertexes (tri, concave_vertexes); - for (auto it = tri.begin (); it != tri.end (); ++it) { - for (unsigned int i = 0; i < 3; ++i) { - db::TriangleEdge *e = it->edge (i); - if (e->is_segment ()) { - left.insert (e); - } - } - } - - std::unordered_map > concave_corners; // @@@ - - while (! left.empty ()) { - - // First segment for a new loop - db::TriangleEdge *segment = *left.begin (); - - // walk along the segments in clockwise direction. Find concave - // vertexes and create new vertexes perpendicular to the incoming - // and outgoing edge. - - db::TriangleEdge *start_segment = segment; - - db::Vertex *vfrom = segment->v1 (); - db::Vertex *vto = segment->v2 (); - if (! segment->right ()) { - std::swap (vfrom, vto); - } - - do { - - left.erase (segment); - - double vp_max = 0.0; - int vp_max_sign = 0; - std::pair edges; - - db::TriangleEdge *prev_segment = segment; - segment = 0; - db::Vertex *vn = 0; - - // Look for the outgoing edge. We pick the one which bends "most", favoring - // convex corners. Multiple edges per vertex are possible is corner cases such as the - // "hourglass" configuration. - - for (auto e = vto->begin_edges (); e != vto->end_edges (); ++e) { - - db::TriangleEdge *en = *e; - if (en != prev_segment && en->is_segment ()) { - - tl_assert (left.find (en) != left.end () || en == start_segment); // @@@ - db::Vertex *v = en->other (vto); - db::DEdge e1 (*vfrom, *vto); - db::DEdge e2 (*vto, *v); - double vp = double (db::vprod (e1, e2)) / (e1.double_length () * e2.double_length ()); - - // vp > 0: concave, vp < 0: convex - - if (! segment || vp > vp_max) { - vp_max_sign = db::vprod_sign (e1, e2); - edges.first = e1; - edges.second = e2; - vp_max = vp; - segment = en; - vn = v; - } - - } - - } - - tl_assert (segment != 0); // @@@ - - if (vp_max_sign > 0) { - // concave corner - concave_corners.insert (std::make_pair (vto, edges)); - } - - vfrom = vto; - vto = vn; - - } while (segment != start_segment); - - } + // @@@ return if no concave corners // @@@ sort convex vertexes - std::vector > new_points; + std::vector new_points; // Cut off pieces from convex corners by creating connections to points perpendicular // to the incoming and outgoing edges - for (auto cc = concave_corners.begin (); cc != concave_corners.end (); ++cc) { + for (auto cc = concave_vertexes.begin (); cc != concave_vertexes.end (); ++cc) { - auto vtri = cc->first->triangles (); // @@@ slow? + auto vtri = cc->corner->triangles (); // @@@ slow? std::vector nvv, nvv_next; for (unsigned int ei = 0; ei < 2; ++ei) { - db::DEdge ee = (ei == 0 ? cc->second.first : cc->second.second); - db::Vertex *v0 = cc->first; + db::DEdge ee; + db::Vertex *v0 = cc->corner; + if (ei == 0) { + ee = db::DEdge (*cc->incoming->other (v0), *v0); + } else { + ee = db::DEdge (*v0, *cc->outgoing->other (v0)); + } for (auto it = vtri.begin (); it != vtri.end (); ++it) { @@ -1240,7 +1301,7 @@ TEST(JoinTriangles) if (oe->is_segment ()) { auto cp = oe->edge ().cut_point (db::DEdge (*v0, *v0 + db::DVector (ee.dy (), -ee.dx ()))); if (cp.first) { - new_points.push_back (std::make_pair (cp.second, v0)); + new_points.push_back (cp.second); } } else { // continue searching in that direction @@ -1267,15 +1328,93 @@ TEST(JoinTriangles) // Insert the new points and make connections - std::unordered_set > clip_pairs; - // @@@ TODO: what to do in case of equal new_points? + // -> sort, remove duplicates for (auto p = new_points.begin (); p != new_points.end (); ++p) { - auto v = tri.insert_point (p->first); - clip_pairs.insert (std::make_pair (v, p->second)); + tri.insert_point (*p); } - // Combine triangles, but don't cross clip edges + if (! new_points.empty ()) { + collect_concave_vertexes (tri, concave_vertexes); + } + + // Collect essential edges + // Every concave vertex can have up to two essential edges. + // Other then suggested by Hertel-Mehlhorn we don't pick + // them one-by-one, but using them in length order, from the + + std::unordered_set essential_edges; + + typedef std::list > angles_and_edges_list; + angles_and_edges_list angles_and_edges; + std::vector sorted_edges; + + for (auto cc = concave_vertexes.begin (); cc != concave_vertexes.end (); ++cc) { + + std::cout << "@@@ cc=" << cc->corner->to_string () << ", in: " << cc->incoming->to_string () << ", out: " << cc->outgoing->to_string () << std::endl; // @@@ + + angles_and_edges.clear (); + db::Vertex *v0 = cc->corner; + + db::TriangleEdge *e = cc->incoming; + while (e) { + + db::Triangle *t = e->v2 () == v0 ? e->right () : e->left (); + tl_assert (t != 0); + + // @@@ make a method of triangle + db::TriangleEdge *en = 0; + for (unsigned int i = 0; i < 3; ++i) { + db::TriangleEdge *ee = t->edge (i); + if (ee != e && (ee->v1 () == v0 || ee->v2 () == v0)) { + en = ee; + break; + } + } + + db::DVector v1 = e->edge ().d () * (e->v1 () == v0 ? 1.0 : -1.0); + db::DVector v2 = en->edge ().d () * (en->v1 () == v0 ? 1.0 : -1.0); + + double angle = atan2 (db::vprod (v1, v2), db::sprod (v1, v2)); + + e = (en == cc->outgoing) ? 0 : en; + angles_and_edges.push_back (std::make_pair (angle, e)); + std::cout << "@@@ [a,e] =" << angle << "," << (e ? e->to_string () : std::string ("ENDL")) << std::endl; // @@@ + + } + + sorted_edges.clear (); + + for (auto i = angles_and_edges.begin (); i != angles_and_edges.end (); ++i) { + if (i->second) { + sorted_edges.push_back (i); + } + } + + std::sort (sorted_edges.begin (), sorted_edges.end (), SortAngleAndEdgesByEdgeLength ()); + + for (auto i = sorted_edges.end (); i != sorted_edges.begin (); ) { + --i; + angles_and_edges_list::iterator ii = *i; + angles_and_edges_list::iterator iin = ii; + ++iin; + std::cout << "@@@ checking [a,e] =" << ii->first << "," << iin->first << "," << ii->second->to_string () << std::endl; // @@@ + if (ii->first + iin->first < M_PI - db::epsilon) { + printf("@@@ %.12g, %.12g\n", ii->first + iin->first, M_PI); // @@@ + // not an essential edge -> remove + iin->first += ii->first; + angles_and_edges.erase (ii); + std::cout << "@@@ -> not essential" << std::endl; // @@@ + } + } + + for (auto i = angles_and_edges.begin (); i != angles_and_edges.end (); ++i) { + essential_edges.insert (i->second); + } + + } + + // Combine triangles, but don't cross essential edges db::Region result; @@ -1305,16 +1444,7 @@ TEST(JoinTriangles) const db::TriangleEdge *e = (*q)->edge (i); const db::Triangle *qq = e->other (*q); - bool is_outer_edge = false; - if (! qq) { - is_outer_edge = true; - } else if (clip_pairs.find (std::make_pair (e->v1 (), e->v2 ())) != clip_pairs.end () || clip_pairs.find (std::make_pair (e->v2 (), e->v1 ())) != clip_pairs.end ()) { - is_outer_edge = true; - } else if (concave_corners.find (e->v1 ()) != concave_corners.end () && concave_corners.find (e->v2 ()) != concave_corners.end ()) { - is_outer_edge = true; - } - - if (is_outer_edge) { + if (! qq || essential_edges.find (e) != essential_edges.end ()) { if (e->right () == *q) { edges.insert (std::make_pair (e->v1 (), e->v2 ())); } else { @@ -1360,7 +1490,7 @@ TEST(JoinTriangles) } // @@@ - // tri.dump ("debug.gds"); + tri.dump ("debugt.gds"); result.write ("debug.gds"); } From d7193e972cbf33d384610626e8bfe535c6898c7b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 12 Apr 2025 23:59:43 +0200 Subject: [PATCH 135/392] WIP --- src/db/db/dbTriangles.h | 19 +- src/db/unit_tests/dbTrianglesTests.cc | 296 +++++++++++++++----------- 2 files changed, 179 insertions(+), 136 deletions(-) diff --git a/src/db/db/dbTriangles.h b/src/db/db/dbTriangles.h index 31da3f48b..3bb34c87d 100644 --- a/src/db/db/dbTriangles.h +++ b/src/db/db/dbTriangles.h @@ -170,6 +170,17 @@ public: void triangulate (const db::DPolygon &poly, const TriangulateParameters ¶meters, const db::DCplxTrans &trans = db::DCplxTrans ()); void triangulate (const db::DPolygon &poly, const std::vector &vertexes, const TriangulateParameters ¶meters, const db::DCplxTrans &trans = db::DCplxTrans ()); + /** + * @brief Inserts a new vertex as the given point + * + * If "new_triangles" is not null, it will receive the list of new triangles created during + * the remove step. + * + * This method can be called after "triangulate" to add new points and adjust the triangulation. + * Inserting new points will maintain the (constrained) Delaunay condition. + */ + db::Vertex *insert_point (const db::DPoint &point, std::list > *new_triangles = 0); + /** * @brief Statistics: number of flips (fixing) */ @@ -214,14 +225,6 @@ protected: */ std::vector find_points_around (Vertex *vertex, double radius); - /** - * @brief Inserts a new vertex as the given point - * - * If "new_triangles" is not null, it will receive the list of new triangles created during - * the remove step. - */ - db::Vertex *insert_point (const db::DPoint &point, std::list > *new_triangles = 0); - /** * @brief Inserts a new vertex as the given point * diff --git a/src/db/unit_tests/dbTrianglesTests.cc b/src/db/unit_tests/dbTrianglesTests.cc index fe09c3817..138248694 100644 --- a/src/db/unit_tests/dbTrianglesTests.cc +++ b/src/db/unit_tests/dbTrianglesTests.cc @@ -1081,7 +1081,7 @@ TEST(triangulate_with_vertexes) struct SortAngleAndEdgesByEdgeLength { - typedef std::list > angle_and_edges_list; + typedef std::list > angle_and_edges_list; bool operator() (const angle_and_edges_list::iterator &a, const angle_and_edges_list::iterator &b) const { @@ -1095,6 +1095,26 @@ struct SortAngleAndEdgesByEdgeLength } }; +// TODO: move to some generic header +template +struct less_compare_func +{ + bool operator() (const T &a, const T &b) const + { + return a.less (b); + } +}; + +// TODO: move to some generic header +template +struct equal_compare_func +{ + bool operator() (const T &a, const T &b) const + { + return a.equal (b); + } +}; + struct ConcaveCorner { ConcaveCorner () @@ -1113,13 +1133,13 @@ struct ConcaveCorner db::TriangleEdge *incoming, *outgoing; }; -db::TriangleEdge *find_outgoing_segment (db::Vertex *vertex, db::TriangleEdge *incoming, bool &is_concave) +db::TriangleEdge *find_outgoing_segment (db::Vertex *vertex, db::TriangleEdge *incoming, int &vp_max_sign) { db::Vertex *vfrom = incoming->other (vertex); db::DEdge e1 (*vfrom, *vertex); double vp_max = 0.0; - int vp_max_sign = 0; + vp_max_sign = 0; db::TriangleEdge *outgoing = 0; // Look for the outgoing edge. We pick the one which bends "most", favoring @@ -1147,8 +1167,6 @@ db::TriangleEdge *find_outgoing_segment (db::Vertex *vertex, db::TriangleEdge *i } - is_concave = (vp_max_sign > 0); - tl_assert (outgoing != 0); return outgoing; } @@ -1188,10 +1206,10 @@ void collect_concave_vertexes (db::Triangles &tris, std::vector & db::TriangleEdge *prev_segment = segment; - bool is_concave = false; - segment = find_outgoing_segment (vto, prev_segment, is_concave); + int vp_sign = 0; + segment = find_outgoing_segment (vto, prev_segment, vp_sign); - if (is_concave) { + if (vp_sign > 0) { concave_vertexes.push_back (ConcaveCorner (vto, prev_segment, segment)); } @@ -1202,122 +1220,94 @@ void collect_concave_vertexes (db::Triangles &tris, std::vector & } } -// Hertel-Mehlhorn :) -TEST(JoinTriangles) +std::pair +search_crossing_with_next_segment (const db::Vertex *v0, const db::DVector &direction) { -#if 1 - db::Point contour[] = { - db::Point (0, 0), - db::Point (0, 100), - db::Point (1000, 100), - db::Point (1000, 500), - db::Point (1100, 500), - db::Point (1100, 100), - db::Point (2100, 100), - db::Point (2100, 0) - }; -#else + auto vtri = v0->triangles (); // TODO: slow? + std::vector nvv, nvv_next; - db::Point contour[] = { - db::Point (0, 0), - db::Point (0, 100), - db::Point (1000, 100), - db::Point (1000, 500), - db::Point (1100, 500), - db::Point (1100, 100), - db::Point (2100, 100), - db::Point (2100, -1000), - db::Point (1050, -1000), - db::Point (1050, 0) - }; -#endif + for (auto it = vtri.begin (); it != vtri.end (); ++it) { - db::Polygon poly; - poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + // Search for a segment in the direction perpendicular to the edge + nvv.clear (); + nvv.push_back (v0); + const db::Triangle *t = *it; - // @@@ don't to anything if already convex + while (! nvv.empty ()) { - double dbu = 0.001; + nvv_next.clear (); - db::Triangles::TriangulateParameters param; - param.min_b = 0.0; + for (auto iv = nvv.begin (); iv != nvv.end (); ++iv) { - TestableTriangles tri; - db::CplxTrans trans = db::DCplxTrans (dbu) * db::CplxTrans (db::Trans (db::Point () - poly.box ().center ())); - trans = db::CplxTrans (dbu); // @@@ - tri.triangulate (poly, param, trans); + const db::Vertex *v = *iv; + const db::TriangleEdge *oe = t->opposite (v); + const db::Triangle *tt = oe->other (t); + const db::Vertex *v1 = oe->v1 (); + const db::Vertex *v2 = oe->v2 (); + if (db::sprod_sign (*v2 - *v, direction) >= 0 && db::sprod_sign (*v1 - *v, direction) >= 0 && + db::vprod_sign (*v2 - *v, direction) * db::vprod_sign (*v1 - *v, direction) < 0) { + + // this triangle covers the normal vector of e1 -> stop here or continue searching in that direction + if (oe->is_segment ()) { + auto cp = oe->edge ().cut_point (db::DEdge (*v0, *v0 + direction)); + if (cp.first) { + return std::make_pair (true, cp.second); + } + } else { + // continue searching in that direction + nvv_next.push_back (v1); + nvv_next.push_back (v2); + t = tt; + } + + break; + + } + + } + + nvv.swap (nvv_next); + + } + + } + + return std::make_pair (false, db::DPoint ()); +} + +void +hertel_mehlhorn_decomposition (db::Triangles &tri, bool diagonals_only, bool no_collinear_edges, std::list &polygons) +{ std::vector concave_vertexes; collect_concave_vertexes (tri, concave_vertexes); // @@@ return if no concave corners - // @@@ sort convex vertexes + // @@@ sort concave vertexes std::vector new_points; - // Cut off pieces from convex corners by creating connections to points perpendicular - // to the incoming and outgoing edges + if (! diagonals_only) { - for (auto cc = concave_vertexes.begin (); cc != concave_vertexes.end (); ++cc) { + // Create internal segments cutting off pieces orthogonal to the edges + // connecting the concave vertex. - auto vtri = cc->corner->triangles (); // @@@ slow? + for (auto cc = concave_vertexes.begin (); cc != concave_vertexes.end (); ++cc) { - std::vector nvv, nvv_next; + for (unsigned int ei = 0; ei < 2; ++ei) { - for (unsigned int ei = 0; ei < 2; ++ei) { - - db::DEdge ee; - db::Vertex *v0 = cc->corner; - if (ei == 0) { - ee = db::DEdge (*cc->incoming->other (v0), *v0); - } else { - ee = db::DEdge (*v0, *cc->outgoing->other (v0)); - } - - for (auto it = vtri.begin (); it != vtri.end (); ++it) { - - // Search for a segment in the direction perpendicular to the edge - nvv.clear (); - nvv.push_back (v0); - db::Triangle *t = *it; - - while (! nvv.empty ()) { - - nvv_next.clear (); - - for (auto iv = nvv.begin (); iv != nvv.end (); ++iv) { - - db::Vertex *v = *iv; - db::TriangleEdge *oe = t->opposite (v); - db::Triangle *tt = oe->other (t); - db::Vertex *v1 = oe->v1 (); - db::Vertex *v2 = oe->v2 (); - - if (db::vprod_sign (*v2 - *v, ee.d ()) >= 0 && db::vprod_sign (*v1 - *v, ee.d ()) >= 0 && - db::sprod_sign (*v2 - *v, ee.d ()) * db::sprod_sign (*v1 - *v, ee.d ()) < 0) { - - // this triangle covers the normal vector of e1 -> stop here or continue searching in that direction - if (oe->is_segment ()) { - auto cp = oe->edge ().cut_point (db::DEdge (*v0, *v0 + db::DVector (ee.dy (), -ee.dx ()))); - if (cp.first) { - new_points.push_back (cp.second); - } - } else { - // continue searching in that direction - nvv_next.push_back (v1); - nvv_next.push_back (v2); - t = tt; - } - - break; - - } - - } - - nvv.swap (nvv_next); + db::DEdge ee; + const db::Vertex *v0 = cc->corner; + if (ei == 0) { + ee = db::DEdge (*cc->incoming->other (v0), *v0); + } else { + ee = db::DEdge (*v0, *cc->outgoing->other (v0)); + } + auto cp = search_crossing_with_next_segment (v0, db::DVector (ee.dy (), -ee.dx ())); + if (cp.first) { + new_points.push_back (cp.second); } } @@ -1326,16 +1316,21 @@ TEST(JoinTriangles) } - // Insert the new points and make connections - - // @@@ TODO: what to do in case of equal new_points? - // -> sort, remove duplicates - for (auto p = new_points.begin (); p != new_points.end (); ++p) { - tri.insert_point (*p); - } + // eliminate duplicates and put the new points in some order if (! new_points.empty ()) { + + std::sort (new_points.begin (), new_points.end (), less_compare_func ()); + new_points.erase (std::unique (new_points.begin (), new_points.end (), equal_compare_func ()), new_points.end ()); + + // Insert the new points and make connections + for (auto p = new_points.begin (); p != new_points.end (); ++p) { + tri.insert_point (*p); + } + + // As the insertion invalidates the edges, we need to collect the concave vertexes again collect_concave_vertexes (tri, concave_vertexes); + } // Collect essential edges @@ -1345,27 +1340,25 @@ TEST(JoinTriangles) std::unordered_set essential_edges; - typedef std::list > angles_and_edges_list; + typedef std::list > angles_and_edges_list; angles_and_edges_list angles_and_edges; std::vector sorted_edges; for (auto cc = concave_vertexes.begin (); cc != concave_vertexes.end (); ++cc) { - std::cout << "@@@ cc=" << cc->corner->to_string () << ", in: " << cc->incoming->to_string () << ", out: " << cc->outgoing->to_string () << std::endl; // @@@ - angles_and_edges.clear (); - db::Vertex *v0 = cc->corner; + const db::Vertex *v0 = cc->corner; - db::TriangleEdge *e = cc->incoming; + const db::TriangleEdge *e = cc->incoming; while (e) { - db::Triangle *t = e->v2 () == v0 ? e->right () : e->left (); + const db::Triangle *t = e->v2 () == v0 ? e->right () : e->left (); tl_assert (t != 0); // @@@ make a method of triangle - db::TriangleEdge *en = 0; + const db::TriangleEdge *en = 0; for (unsigned int i = 0; i < 3; ++i) { - db::TriangleEdge *ee = t->edge (i); + const db::TriangleEdge *ee = t->edge (i); if (ee != e && (ee->v1 () == v0 || ee->v2 () == v0)) { en = ee; break; @@ -1379,7 +1372,6 @@ TEST(JoinTriangles) e = (en == cc->outgoing) ? 0 : en; angles_and_edges.push_back (std::make_pair (angle, e)); - std::cout << "@@@ [a,e] =" << angle << "," << (e ? e->to_string () : std::string ("ENDL")) << std::endl; // @@@ } @@ -1398,13 +1390,10 @@ TEST(JoinTriangles) angles_and_edges_list::iterator ii = *i; angles_and_edges_list::iterator iin = ii; ++iin; - std::cout << "@@@ checking [a,e] =" << ii->first << "," << iin->first << "," << ii->second->to_string () << std::endl; // @@@ - if (ii->first + iin->first < M_PI - db::epsilon) { - printf("@@@ %.12g, %.12g\n", ii->first + iin->first, M_PI); // @@@ + if (ii->first + iin->first < (no_collinear_edges ? M_PI - db::epsilon : M_PI + db::epsilon)) { // not an essential edge -> remove iin->first += ii->first; angles_and_edges.erase (ii); - std::cout << "@@@ -> not essential" << std::endl; // @@@ } } @@ -1416,8 +1405,6 @@ TEST(JoinTriangles) // Combine triangles, but don't cross essential edges - db::Region result; - std::unordered_set left_triangles; for (auto it = tri.begin (); it != tri.end (); ++it) { left_triangles.insert (it.operator-> ()); @@ -1483,11 +1470,64 @@ TEST(JoinTriangles) } while (v != v0); - db::DPolygon poly; - poly.assign_hull (polygon_points.begin (), polygon_points.end ()); - result.insert (trans.inverted () * poly); + polygons.push_back (db::DPolygon ()); + polygons.back ().assign_hull (polygon_points.begin (), polygon_points.end ()); } +} + + +// Hertel-Mehlhorn :) +TEST(JoinTriangles) +{ +#if 0 + db::Point contour[] = { + db::Point (0, 0), + db::Point (0, 100), + db::Point (1000, 100), + db::Point (1000, 500), + db::Point (1100, 500), + db::Point (1100, 100), + db::Point (2100, 100), + db::Point (2100, 0) + }; +#else + + db::Point contour[] = { + db::Point (0, 0), + db::Point (0, 100), + db::Point (1000, 100), + db::Point (1000, 500), + db::Point (1100, 500), + db::Point (1100, 100), + db::Point (2100, 100), + db::Point (2100, -1000), + db::Point (150, -1000), + db::Point (150, 0) + }; +#endif + + db::Polygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + + // @@@ don't to anything if already convex + + double dbu = 0.001; + + db::Triangles::TriangulateParameters param; + param.min_b = 0.0; + + TestableTriangles tri; + db::CplxTrans trans = db::CplxTrans (dbu); + tri.triangulate (poly, param, trans); + + std::list polygons; + hertel_mehlhorn_decomposition (tri, false, true, polygons); + + db::Region result; + for (auto p = polygons.begin (); p != polygons.end (); ++p) { + result.insert (trans.inverted () * *p); + } // @@@ tri.dump ("debugt.gds"); From 627e2444435fd93317ac53f79ad273ab38486ca2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 13 Apr 2025 22:44:14 +0200 Subject: [PATCH 136/392] Refactoring: generalization of Triangles into db::plc::Graph in dbPLC.h --- src/db/db/db.pro | 6 +- src/db/db/{dbPolygonGraph.cc => dbPLC.cc} | 458 +++-- src/db/db/{dbPolygonGraph.h => dbPLC.h} | 447 +++-- src/db/db/dbPLCTriangulation.cc | 1649 +++++++++++++++++ src/db/db/dbPLCTriangulation.h | 306 +++ ...PolygonGraphTests.cc => dbPLCGraphTest.cc} | 18 +- src/db/unit_tests/dbPLCTriangulationTests.cc | 1117 +++++++++++ src/db/unit_tests/dbTrianglesTests.cc | 2 +- src/db/unit_tests/unit_tests.pro | 3 +- 9 files changed, 3602 insertions(+), 404 deletions(-) rename src/db/db/{dbPolygonGraph.cc => dbPLC.cc} (68%) rename src/db/db/{dbPolygonGraph.h => dbPLC.h} (64%) create mode 100644 src/db/db/dbPLCTriangulation.cc create mode 100644 src/db/db/dbPLCTriangulation.h rename src/db/unit_tests/{dbPolygonGraphTests.cc => dbPLCGraphTest.cc} (76%) create mode 100644 src/db/unit_tests/dbPLCTriangulationTests.cc diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 732d42aa1..888a22dcc 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -70,13 +70,14 @@ SOURCES = \ dbNetlistSpiceReaderExpressionParser.cc \ dbObject.cc \ dbObjectWithProperties.cc \ + dbPLC.cc \ + dbPLCTriangulation.cc \ dbPath.cc \ dbPCellDeclaration.cc \ dbPCellHeader.cc \ dbPCellVariant.cc \ dbPoint.cc \ dbPolygon.cc \ - dbPolygonGraph.cc \ dbPolygonNeighborhood.cc \ dbPolygonTools.cc \ dbPolygonGenerators.cc \ @@ -309,13 +310,14 @@ HEADERS = \ dbObject.h \ dbObjectTag.h \ dbObjectWithProperties.h \ + dbPLC.h \ + dbPLCTriangulation.h \ dbPath.h \ dbPCellDeclaration.h \ dbPCellHeader.h \ dbPCellVariant.h \ dbPoint.h \ dbPolygon.h \ - dbPolygonGraph.h \ dbPolygonNeighborhood.h \ dbPolygonTools.h \ dbPolygonGenerators.h \ diff --git a/src/db/db/dbPolygonGraph.cc b/src/db/db/dbPLC.cc similarity index 68% rename from src/db/db/dbPolygonGraph.cc rename to src/db/db/dbPLC.cc index f745b0da2..559bf4572 100644 --- a/src/db/db/dbPolygonGraph.cc +++ b/src/db/db/dbPLC.cc @@ -21,7 +21,7 @@ */ -#include "dbPolygonGraph.h" +#include "dbPLC.h" #include "dbLayout.h" #include "dbWriter.h" #include "tlStream.h" @@ -36,28 +36,43 @@ namespace db { +namespace plc +{ + // ------------------------------------------------------------------------------------- -// GVertex implementation +// Vertex implementation -GVertex::GVertex () - : DPoint (), m_is_precious (false) +Vertex::Vertex (Graph *graph) + : DPoint (), mp_graph (graph), m_is_precious (false) { // .. nothing yet .. } -GVertex::GVertex (const db::DPoint &p) - : DPoint (p), m_is_precious (false) +Vertex::Vertex (Graph *graph, const db::DPoint &p) + : DPoint (p), mp_graph (graph), m_is_precious (false) { // .. nothing yet .. } -GVertex::GVertex (const GVertex &v) - : DPoint (), m_is_precious (false) +Vertex::Vertex (Graph *graph, const Vertex &v) + : DPoint (), mp_graph (graph), m_is_precious (false) { operator= (v); } -GVertex &GVertex::operator= (const GVertex &v) +Vertex::Vertex (Graph *graph, db::DCoord x, db::DCoord y) + : DPoint (x, y), mp_graph (graph), m_is_precious (false) +{ + // .. nothing yet .. +} + +Vertex::Vertex (const Vertex &v) + : DPoint (v), mp_graph (v.mp_graph), m_is_precious (v.m_is_precious) +{ + // NOTE: edges are not copied! +} + +Vertex &Vertex::operator= (const Vertex &v) { if (this != &v) { // NOTE: edges are not copied! @@ -67,15 +82,8 @@ GVertex &GVertex::operator= (const GVertex &v) return *this; } -GVertex::GVertex (db::DCoord x, db::DCoord y) - : DPoint (x, y), m_is_precious (false) -{ - // .. nothing yet .. -} - -#if 0 // @@@ bool -GVertex::is_outside () const +Vertex::is_outside () const { for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) { if ((*e)->is_outside ()) { @@ -84,13 +92,12 @@ GVertex::is_outside () const } return false; } -#endif -std::vector -GVertex::polygons () const +std::vector +Vertex::polygons () const { - std::set seen; - std::vector res; + std::set seen; + std::vector res; for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) { for (auto t = (*e)->begin_polygons (); t != (*e)->end_polygons (); ++t) { if (seen.insert (t.operator-> ()).second) { @@ -102,7 +109,7 @@ GVertex::polygons () const } bool -GVertex::has_edge (const GPolygonEdge *edge) const +Vertex::has_edge (const Edge *edge) const { for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) { if (*e == edge) { @@ -113,7 +120,7 @@ GVertex::has_edge (const GPolygonEdge *edge) const } size_t -GVertex::num_edges (int max_count) const +Vertex::num_edges (int max_count) const { if (max_count < 0) { // NOTE: this can be slow for a std::list, so we have max_count to limit this effort @@ -128,7 +135,7 @@ GVertex::num_edges (int max_count) const } std::string -GVertex::to_string (bool with_id) const +Vertex::to_string (bool with_id) const { std::string res = tl::sprintf ("(%.12g, %.12g)", x (), y()); if (with_id) { @@ -138,7 +145,7 @@ GVertex::to_string (bool with_id) const } int -GVertex::in_circle (const DPoint &point, const DPoint ¢er, double radius) +Vertex::in_circle (const DPoint &point, const DPoint ¢er, double radius) { double dx = point.x () - center.x (); double dy = point.y () - center.y (); @@ -155,34 +162,34 @@ GVertex::in_circle (const DPoint &point, const DPoint ¢er, double radius) } // ------------------------------------------------------------------------------------- -// GPolygonEdge implementation +// Edge implementation -GPolygonEdge::GPolygonEdge () - : mp_v1 (0), mp_v2 (0), mp_left (), mp_right (), m_level (0), m_id (0), m_is_segment (false) +Edge::Edge (Graph *graph) + : mp_graph (graph), mp_v1 (0), mp_v2 (0), mp_left (), mp_right (), m_level (0), m_id (0), m_is_segment (false) { // .. nothing yet .. } -GPolygonEdge::GPolygonEdge (GVertex *v1, GVertex *v2) - : mp_v1 (v1), mp_v2 (v2), mp_left (), mp_right (), m_level (0), m_id (0), m_is_segment (false) +Edge::Edge (Graph *graph, Vertex *v1, Vertex *v2) + : mp_graph (graph), mp_v1 (v1), mp_v2 (v2), mp_left (), mp_right (), m_level (0), m_id (0), m_is_segment (false) { // .. nothing yet .. } void -GPolygonEdge::set_left (GPolygon *t) +Edge::set_left (Polygon *t) { mp_left = t; } void -GPolygonEdge::set_right (GPolygon *t) +Edge::set_right (Polygon *t) { mp_right = t; } void -GPolygonEdge::link () +Edge::link () { mp_v1->mp_edges.push_back (this); m_ec_v1 = --mp_v1->mp_edges.end (); @@ -192,7 +199,7 @@ GPolygonEdge::link () } void -GPolygonEdge::unlink () +Edge::unlink () { if (mp_v1) { mp_v1->remove_edge (m_ec_v1); @@ -203,8 +210,8 @@ GPolygonEdge::unlink () mp_v1 = mp_v2 = 0; } -GPolygon * -GPolygonEdge::other (const GPolygon *t) const +Polygon * +Edge::other (const Polygon *t) const { if (t == mp_left) { return mp_right; @@ -216,8 +223,8 @@ GPolygonEdge::other (const GPolygon *t) const return 0; } -GVertex * -GPolygonEdge::other (const GVertex *t) const +Vertex * +Edge::other (const Vertex *t) const { if (t == mp_v1) { return mp_v2; @@ -230,13 +237,13 @@ GPolygonEdge::other (const GVertex *t) const } bool -GPolygonEdge::has_vertex (const GVertex *v) const +Edge::has_vertex (const Vertex *v) const { return mp_v1 == v || mp_v2 == v; } -GVertex * -GPolygonEdge::common_vertex (const GPolygonEdge *other) const +Vertex * +Edge::common_vertex (const Edge *other) const { if (has_vertex (other->v1 ())) { return (other->v1 ()); @@ -248,7 +255,7 @@ GPolygonEdge::common_vertex (const GPolygonEdge *other) const } std::string -GPolygonEdge::to_string (bool with_id) const +Edge::to_string (bool with_id) const { std::string res = std::string ("(") + mp_v1->to_string (with_id) + ", " + mp_v2->to_string (with_id) + ")"; if (with_id) { @@ -258,7 +265,7 @@ GPolygonEdge::to_string (bool with_id) const } double -GPolygonEdge::distance (const db::DEdge &e, const db::DPoint &p) +Edge::distance (const db::DEdge &e, const db::DPoint &p) { double l = db::sprod (p - e.p1 (), e.d ()) / e.d ().sq_length (); db::DPoint pp; @@ -273,27 +280,27 @@ GPolygonEdge::distance (const db::DEdge &e, const db::DPoint &p) } bool -GPolygonEdge::crosses (const db::DEdge &e, const db::DEdge &other) +Edge::crosses (const db::DEdge &e, const db::DEdge &other) { return e.side_of (other.p1 ()) * e.side_of (other.p2 ()) < 0 && other.side_of (e.p1 ()) * other.side_of (e.p2 ()) < 0; } bool -GPolygonEdge::crosses_including (const db::DEdge &e, const db::DEdge &other) +Edge::crosses_including (const db::DEdge &e, const db::DEdge &other) { return e.side_of (other.p1 ()) * e.side_of (other.p2 ()) <= 0 && other.side_of (e.p1 ()) * other.side_of (e.p2 ()) <= 0; } db::DPoint -GPolygonEdge::intersection_point (const db::DEdge &e, const db::DEdge &other) +Edge::intersection_point (const db::DEdge &e, const db::DEdge &other) { return e.intersect_point (other).second; } bool -GPolygonEdge::point_on (const db::DEdge &edge, const db::DPoint &point) +Edge::point_on (const db::DEdge &edge, const db::DPoint &point) { if (edge.side_of (point) != 0) { return false; @@ -302,42 +309,72 @@ GPolygonEdge::point_on (const db::DEdge &edge, const db::DPoint &point) } } -#if 0 // @@@ bool -GPolygonEdge::is_for_outside_polygons () const +Edge::can_flip () const +{ + if (! left () || ! right ()) { + return false; + } + + const Vertex *v1 = left ()->opposite (this); + const Vertex *v2 = right ()->opposite (this); + return crosses (db::DEdge (*v1, *v2)); +} + +bool +Edge::can_join_via (const Vertex *vertex) const +{ + if (! left () || ! right ()) { + return false; + } + + tl_assert (has_vertex (vertex)); + const Vertex *v1 = left ()->opposite (this); + const Vertex *v2 = right ()->opposite (this); + return db::DEdge (*v1, *v2).side_of (*vertex) == 0; +} + +bool +Edge::is_outside () const +{ + return left () == 0 || right () == 0; +} + +bool +Edge::is_for_outside_triangles () const { return (left () && left ()->is_outside ()) || (right () && right ()->is_outside ()); } -#endif bool -GPolygonEdge::has_polygon (const GPolygon *t) const +Edge::has_polygon (const Polygon *t) const { return t != 0 && (left () == t || right () == t); } // ------------------------------------------------------------------------------------- -// GPolygon implementation +// Polygon implementation -GPolygon::GPolygon () - : m_id (0) +Polygon::Polygon (Graph *graph) + : mp_graph (graph), m_is_outside (false), m_id (0) { // .. nothing yet .. } void -GPolygon::init () +Polygon::init () { m_id = 0; + m_is_outside = false; if (mp_e.empty ()) { return; } - std::vector e; + std::vector e; e.swap (mp_e); - std::multimap v2e; + std::multimap v2e; for (auto i = e.begin (); i != e.end (); ++i) { if (i != e.begin ()) { @@ -379,7 +416,7 @@ GPolygon::init () // establish clockwise order of the vertexes double area = 0.0; - const db::GVertex *vm1 = vertex (-1), *v0; + const Vertex *vm1 = vertex (-1), *v0; for (auto i = mp_v.begin (); i != mp_v.end (); ++i) { v0 = *i; area += db::vprod (*vm1 - db::DPoint (), *v0 - *vm1); @@ -394,8 +431,8 @@ GPolygon::init () // link the polygon to the edges for (size_t i = 0; i < size (); ++i) { - db::GVertex *v = mp_v[i]; - db::GPolygonEdge *e = mp_e[i]; + Vertex *v = mp_v[i]; + Edge *e = mp_e[i]; if (e->v1 () == v) { e->set_right (this); } else { @@ -404,13 +441,62 @@ GPolygon::init () } } -GPolygon::~GPolygon () +Polygon::Polygon (Graph *graph, Edge *e1, Edge *e2, Edge *e3) + : mp_graph (graph), m_is_outside (false), m_id (0) +{ + mp_e.resize (3, 0); + mp_v.resize (3, 0); + + mp_e[0] = e1; + mp_v[0] = e1->v1 (); + mp_v[1] = e1->v2 (); + + if (e2->has_vertex (mp_v[1])) { + mp_e[1] = e2; + mp_e[2] = e3; + } else { + mp_e[1] = e3; + mp_e[2] = e2; + } + mp_v[2] = mp_e[1]->other (mp_v[1]); + + // enforce clockwise orientation + int s = db::vprod_sign (*mp_v[2] - *mp_v[0], *mp_v[1] - *mp_v[0]); + if (s < 0) { + std::swap (mp_v[2], mp_v[1]); + } else if (s == 0) { + // Triangle is not orientable + tl_assert (false); + } + + // establish link to edges + for (int i = 0; i < 3; ++i) { + + Edge *e = mp_e[i]; + + unsigned int i1 = 0; + for ( ; e->v1 () != mp_v[i1] && i1 < 3; ++i1) + ; + unsigned int i2 = 0; + for ( ; e->v2 () != mp_v[i2] && i2 < 3; ++i2) + ; + + if ((i1 + 1) % 3 == i2) { + e->set_right (this); + } else { + e->set_left (this); + } + + } +} + +Polygon::~Polygon () { unlink (); } void -GPolygon::unlink () +Polygon::unlink () { for (auto e = mp_e.begin (); e != mp_e.end (); ++e) { if ((*e)->left () == this) { @@ -423,7 +509,7 @@ GPolygon::unlink () } std::string -GPolygon::to_string (bool with_id) const +Polygon::to_string (bool with_id) const { std::string res = "("; for (int i = 0; i < int (size ()); ++i) { @@ -441,13 +527,13 @@ GPolygon::to_string (bool with_id) const } double -GPolygon::area () const +Polygon::area () const { return fabs (db::vprod (mp_e[0]->d (), mp_e[1]->d ())) * 0.5; } db::DBox -GPolygon::bbox () const +Polygon::bbox () const { db::DBox box; for (auto i = mp_v.begin (); i != mp_v.end (); ++i) { @@ -456,8 +542,77 @@ GPolygon::bbox () const return box; } -GPolygonEdge * -GPolygon::find_edge_with (const GVertex *v1, const GVertex *v2) const +std::pair +Polygon::circumcircle (bool *ok) const +{ + tl_assert (mp_v.size () == 3); + + // see https://en.wikipedia.org/wiki/Circumcircle + // we set A=(0,0), so the formulas simplify + + if (ok) { + *ok = true; + } + + db::DVector b = *mp_v[1] - *mp_v[0]; + db::DVector c = *mp_v[2] - *mp_v[0]; + + double b2 = b.sq_length (); + double c2 = c.sq_length (); + + double sx = 0.5 * (b2 * c.y () - c2 * b.y ()); + double sy = 0.5 * (b.x () * c2 - c.x() * b2); + + double a1 = b.x() * c.y(); + double a2 = c.x() * b.y(); + double a = a1 - a2; + double a_abs = std::abs (a); + + if (a_abs < (std::abs (a1) + std::abs (a2)) * db::epsilon) { + if (ok) { + *ok = false; + return std::make_pair (db::DPoint (), 0.0); + } else { + tl_assert (false); + } + } + + double radius = sqrt (sx * sx + sy * sy) / a_abs; + db::DPoint center = *mp_v[0] + db::DVector (sx / a, sy / a); + + return std::make_pair (center, radius); +} + +Vertex * +Polygon::opposite (const Edge *edge) const +{ + tl_assert (mp_v.size () == 3); + + for (int i = 0; i < 3; ++i) { + Vertex *v = mp_v[i]; + if (! edge->has_vertex (v)) { + return v; + } + } + tl_assert (false); +} + +Edge * +Polygon::opposite (const Vertex *vertex) const +{ + tl_assert (mp_v.size () == 3); + + for (int i = 0; i < 3; ++i) { + Edge *e = mp_e[i]; + if (! e->has_vertex (vertex)) { + return e; + } + } + tl_assert (false); +} + +Edge * +Polygon::find_edge_with (const Vertex *v1, const Vertex *v2) const { for (auto e = mp_e.begin (); e != mp_e.end (); ++e) { if ((*e)->has_vertex (v1) && (*e)->has_vertex (v2)) { @@ -467,8 +622,8 @@ GPolygon::find_edge_with (const GVertex *v1, const GVertex *v2) const tl_assert (false); } -GPolygonEdge * -GPolygon::common_edge (const GPolygon *other) const +Edge * +Polygon::common_edge (const Polygon *other) const { for (auto e = mp_e.begin (); e != mp_e.end (); ++e) { if ((*e)->other (this) == other) { @@ -478,10 +633,11 @@ GPolygon::common_edge (const GPolygon *other) const return 0; } -#if 0 // @@@ int -GPolygon::contains (const db::DPoint &point) const +Polygon::contains (const db::DPoint &point) const { + tl_assert (mp_v.size () == 3); + auto c = *mp_v[2] - *mp_v[0]; auto b = *mp_v[1] - *mp_v[0]; @@ -492,9 +648,9 @@ GPolygon::contains (const db::DPoint &point) const int res = 1; - const GVertex *vl = mp_v[2]; + const Vertex *vl = mp_v[2]; for (int i = 0; i < 3; ++i) { - const GVertex *v = mp_v[i]; + const Vertex *v = mp_v[i]; int n = db::vprod_sign (point - *vl, *v - *vl) * vps; if (n < 0) { return -1; @@ -506,10 +662,9 @@ GPolygon::contains (const db::DPoint &point) const return res; } -#endif double -GPolygon::min_edge_length () const +Polygon::min_edge_length () const { double lmin = mp_e[0]->d ().length (); for (auto e = mp_e.begin (); e != mp_e.end (); ++e) { @@ -518,19 +673,17 @@ GPolygon::min_edge_length () const return lmin; } -#if 0 // @@@ double -GPolygon::b () const +Polygon::b () const { double lmin = min_edge_length (); bool ok = false; auto cr = circumcircle (&ok); return ok ? lmin / cr.second : 0.0; } -#endif bool -GPolygon::has_segment () const +Polygon::has_segment () const { for (auto e = mp_e.begin (); e != mp_e.end (); ++e) { if ((*e)->is_segment ()) { @@ -541,7 +694,7 @@ GPolygon::has_segment () const } unsigned int -GPolygon::num_segments () const +Polygon::num_segments () const { unsigned int n = 0; for (auto e = mp_e.begin (); e != mp_e.end (); ++e) { @@ -554,49 +707,42 @@ GPolygon::num_segments () const // ----------------------------------------------------------------------------------- -static inline bool is_equal (const db::DPoint &a, const db::DPoint &b) -{ - return std::abs (a.x () - b.x ()) < std::max (1.0, (std::abs (a.x ()) + std::abs (b.x ()))) * db::epsilon && - std::abs (a.y () - b.y ()) < std::max (1.0, (std::abs (a.y ()) + std::abs (b.y ()))) * db::epsilon; -} - -PolygonGraph::PolygonGraph () +Graph::Graph () : m_id (0) - // @@@: m_is_constrained (false), m_level (0), m_id (0), m_flips (0), m_hops (0) { // .. nothing yet .. } -PolygonGraph::~PolygonGraph () +Graph::~Graph () { clear (); } -db::GVertex * -PolygonGraph::create_vertex (double x, double y) +Vertex * +Graph::create_vertex (double x, double y) { - m_vertex_heap.push_back (db::GVertex (x, y)); + m_vertex_heap.push_back (Vertex (this, x, y)); return &m_vertex_heap.back (); } -db::GVertex * -PolygonGraph::create_vertex (const db::DPoint &pt) +Vertex * +Graph::create_vertex (const db::DPoint &pt) { - m_vertex_heap.push_back (pt); + m_vertex_heap.push_back (Vertex (this, pt)); return &m_vertex_heap.back (); } -db::GPolygonEdge * -PolygonGraph::create_edge (db::GVertex *v1, db::GVertex *v2) +Edge * +Graph::create_edge (Vertex *v1, Vertex *v2) { - db::GPolygonEdge *edge = 0; + Edge *edge = 0; if (! m_returned_edges.empty ()) { edge = m_returned_edges.back (); m_returned_edges.pop_back (); - *edge = db::GPolygonEdge (v1, v2); + *edge = Edge (this, v1, v2); } else { - m_edges_heap.push_back (db::GPolygonEdge (v1, v2)); + m_edges_heap.push_back (Edge (this, v1, v2)); edge = &m_edges_heap.back (); } @@ -605,11 +751,21 @@ PolygonGraph::create_edge (db::GVertex *v1, db::GVertex *v2) return edge; } -void -PolygonGraph::remove_polygon (db::GPolygon *poly) +Polygon * +Graph::create_triangle (Edge *e1, Edge *e2, Edge *e3) { - std::vector edges; - edges.reserve (poly->size ()); + Polygon *res = new Polygon (this, e1, e2, e3); + res->set_id (++m_id); + mp_polygons.push_back (res); + + return res; +} + +void +Graph::remove_polygon (Polygon *poly) +{ + std::vector edges; + edges.resize (poly->size (), 0); for (int i = 0; i < int (poly->size ()); ++i) { edges [i] = poly->edge (i); } @@ -625,63 +781,8 @@ PolygonGraph::remove_polygon (db::GPolygon *poly) } } -#if 0 // @@@ -void -PolygonGraph::convex_decompose (const db::DPolygon &polygon) -{ - clear (); - - if (polygon.begin_edge ().at_end ()) { - return; - } - - std::vector edges; - - for (unsigned int c = 0; c < polygon.holes () + 1; ++c) { - - const db::DSimplePolygon::contour_type &ctr = polygon.contour (c); - - db::GVertex *v0 = 0, *vv, *v; - size_t n = ctr.size (); - for (size_t i = 0; i < n; ++i) { - - db::DPoint pm1 = ctr [i > 0 ? i - 1 : n - 1]; - db::DPoint pp1 = ctr [i + 1 < n ? i + 1 : 0]; - db::DPoint p = ctr [i]; - - bool is_convex = db::vprod_sign (p - pm1, pp1 - p); - // @@@ - - v = create_vertex (p.x (), p.y ()); - if (! v0) { - v0 = v; - } else { - edges.push_back (create_edge (vv, v)); - } - - vv = v; - - } - - if (v0 && v0 != v) { - edges.push_back (create_edge (v, v0)); - } - - } -} -#endif - -void -PolygonGraph::convex_decompose (const db::DPolygon &poly) -{ - - // @@@ - - -} - std::string -PolygonGraph::to_string () +Graph::to_string () { std::string res; for (auto t = mp_polygons.begin (); t != mp_polygons.end (); ++t) { @@ -694,7 +795,7 @@ PolygonGraph::to_string () } db::DBox -PolygonGraph::bbox () const +Graph::bbox () const { db::DBox box; for (auto t = mp_polygons.begin (); t != mp_polygons.end (); ++t) { @@ -705,7 +806,7 @@ PolygonGraph::bbox () const #if 0 // @@@ bool -PolygonGraph::check (bool check_delaunay) const +Graph::check (bool check_delaunay) const { bool res = true; @@ -759,7 +860,7 @@ PolygonGraph::check (bool check_delaunay) const tl::error << "(check error) edges " << e->to_string (true) << " vertex 2 not found in adjacent polygon " << t->to_string (true); res = false; } - db::GVertex *vopp = t->opposite (e.operator-> ()); + Vertex *vopp = t->opposite (e.operator-> ()); double sgn = (e->left () == t.operator-> ()) ? 1.0 : -1.0; double vp = db::vprod (e->d(), *vopp - *e->v1 ()); // positive if on left side if (vp * sgn <= 0.0) { @@ -803,7 +904,7 @@ PolygonGraph::check (bool check_delaunay) const #endif db::Layout * -PolygonGraph::to_layout (bool decompose_by_id) const +Graph::to_layout (bool decompose_by_id) const { db::Layout *layout = new db::Layout (); layout->dbu (0.001); @@ -850,7 +951,7 @@ PolygonGraph::to_layout (bool decompose_by_id) const } void -PolygonGraph::dump (const std::string &path, bool decompose_by_id) const +Graph::dump (const std::string &path, bool decompose_by_id) const { std::unique_ptr ly (to_layout (decompose_by_id)); @@ -860,36 +961,19 @@ PolygonGraph::dump (const std::string &path, bool decompose_by_id) const db::Writer writer (opt); writer.write (*ly, stream); - tl::info << "PolygonGraph written to " << path; + tl::info << "Graph written to " << path; } void -PolygonGraph::clear () +Graph::clear () { mp_polygons.clear (); m_edges_heap.clear (); m_vertex_heap.clear (); m_returned_edges.clear (); - // @@@m_is_constrained = false; - // @@@m_level = 0; m_id = 0; } -template -void -PolygonGraph::make_contours (const Poly &poly, const Trans &trans, std::vector > &edge_contours) -{ - edge_contours.push_back (std::vector ()); - for (auto pt = poly.begin_hull (); pt != poly.end_hull (); ++pt) { - edge_contours.back ().push_back (insert_point (trans * *pt)); - } +} // namespace plc - for (unsigned int h = 0; h < poly.holes (); ++h) { - edge_contours.push_back (std::vector ()); - for (auto pt = poly.begin_hole (h); pt != poly.end_hole (h); ++pt) { - edge_contours.back ().push_back (insert_point (trans * *pt)); - } - } -} - -} +} // namespace db diff --git a/src/db/db/dbPolygonGraph.h b/src/db/db/dbPLC.h similarity index 64% rename from src/db/db/dbPolygonGraph.h rename to src/db/db/dbPLC.h index d296b886f..8d410d0b8 100644 --- a/src/db/db/dbPolygonGraph.h +++ b/src/db/db/dbPLC.h @@ -20,8 +20,8 @@ */ -#ifndef HDR_dbPolygonGraph -#define HDR_dbPolygonGraph +#ifndef HDR_dbPLC +#define HDR_dbPLC #include "dbCommon.h" #include "dbTriangle.h" @@ -41,8 +41,38 @@ namespace db class Layout; -class GPolygon; -class GPolygonEdge; +namespace plc +{ + +/** + * @brief A framework for piecewise linear curves + * + * This framework implements classes for dealing with piecewise linear + * curves. It is the basis for triangulation and polygon decomposition + * algorithms. + * + * The core class is the PLCGraph which is a collection of vertices, + * edges and edge loops (polygons). Vertices, edges and polygons form + * graphs. + * + * A "vertex" (db::plc::Vertex) is a point. A point connects two or + * more edges. + * A vertex has some attributes: + * * 'precious': if set, the vertex is not removed during triangulation + * for example. + * + * An "edge" (db::plc::Edge) is a line connecting two vertexes. The + * edge runs from vertex v1 to vertex v2. An edge separates two + * polygons (left and right, as seen in the run direction). + * + * A "segment" as an edge that is part of an original polygon outline. + * + * A "polygon" (db::plc::Polygon) is a loop of edges. + */ + +class Polygon; +class Edge; +class Graph; /** * @brief A class representing a vertex in a Delaunay triangulation graph @@ -51,40 +81,71 @@ class GPolygonEdge; * an integer value that can be used in traversal algorithms * ("level") */ -class DB_PUBLIC GVertex +class DB_PUBLIC Vertex : public db::DPoint { public: - typedef std::list edges_type; + typedef std::list edges_type; typedef edges_type::const_iterator edges_iterator; typedef edges_type::iterator edges_iterator_non_const; - GVertex (); - GVertex (const DPoint &p); - GVertex (const GVertex &v); - GVertex (db::DCoord x, db::DCoord y); + Vertex (const Vertex &v); + Vertex &operator= (const Vertex &v); - GVertex &operator= (const GVertex &v); - -#if 0 // @@@ + /** + * @brief Gets a value indicating whether any of the attached edges is "outside" + */ bool is_outside () const; -#endif - std::vector polygons () const; + /** + * @brief Gets a list of polygons that are attached to this vertex + */ + std::vector polygons() const; + + /** + * @brief Gets the graph object this vertex belongs to + */ + Graph *graph () const { return mp_graph; } + + /** + * @brief Iterates the edges on this vertex (begin) + */ edges_iterator begin_edges () const { return mp_edges.begin (); } + + /** + * @brief Iterates the edges on this vertex (end) + */ edges_iterator end_edges () const { return mp_edges.end (); } + + /** + * @brief Returns the number of edges attached to this vertex + */ size_t num_edges (int max_count = -1) const; - bool has_edge (const GPolygonEdge *edge) const; + /** + * @brief Returns a value indicating whether the given edge is attached to this vertex + */ + bool has_edge (const Edge *edge) const; + /** + * @brief Sets a valid indicating whether the vertex is precious + * + * "precious" vertexes are not removed during triangulation for example. + */ void set_is_precious (bool f) { m_is_precious = f; } + + /** + * @brief Gets a valid indicating whether the vertex is precious + */ bool is_precious () const { return m_is_precious; } + /** + * @brief Returns a string representation of the vertex + */ std::string to_string (bool with_id = false) const; /** * @brief Returns 1 is the point is inside the circle, 0 if on the circle and -1 if outside - * TODO: Move to db::DPoint */ static int in_circle (const db::DPoint &point, const db::DPoint ¢er, double radius); @@ -97,13 +158,20 @@ public: } private: - friend class GPolygonEdge; + friend class Edge; + friend class Graph; + + Vertex (Graph *graph); + Vertex (Graph *graph, const DPoint &p); + Vertex (Graph *graph, const Vertex &v); + Vertex (Graph *graph, db::DCoord x, db::DCoord y); void remove_edge (const edges_iterator_non_const &ec) { mp_edges.erase (ec); } + Graph *mp_graph; edges_type mp_edges; bool m_is_precious; }; @@ -111,15 +179,15 @@ private: /** * @brief A class representing an edge in the Delaunay triangulation graph */ -class DB_PUBLIC GPolygonEdge +class DB_PUBLIC Edge { public: - class GPolygonIterator + class PolygonIterator { public: - typedef GPolygon value_type; - typedef GPolygon &reference; - typedef GPolygon *pointer; + typedef Polygon value_type; + typedef Polygon &reference; + typedef Polygon *pointer; reference operator*() const { @@ -131,17 +199,17 @@ public: return m_index ? mp_edge->right () : mp_edge->left (); } - bool operator== (const GPolygonIterator &other) const + bool operator== (const PolygonIterator &other) const { return m_index == other.m_index; } - bool operator!= (const GPolygonIterator &other) const + bool operator!= (const PolygonIterator &other) const { return !operator== (other); } - GPolygonIterator &operator++ () + PolygonIterator &operator++ () { while (++m_index < 2 && operator-> () == 0) ; @@ -149,9 +217,9 @@ public: } private: - friend class GPolygonEdge; + friend class Edge; - GPolygonIterator (const GPolygonEdge *edge) + PolygonIterator (const Edge *edge) : mp_edge (edge), m_index (0) { if (! edge) { @@ -162,48 +230,72 @@ public: } } - const GPolygonEdge *mp_edge; + const Edge *mp_edge; unsigned int m_index; }; - GPolygonEdge (); - GPolygonEdge (GVertex *v1, GVertex *v2); + /** + * @brief Gets the first vertex ("from") + */ + Vertex *v1 () const { return mp_v1; } - GVertex *v1 () const { return mp_v1; } - GVertex *v2 () const { return mp_v2; } + /** + * @brief Gets the first vertex ("to") + */ + Vertex *v2 () const { return mp_v2; } + /** + * @brief Reverses the edge + */ void reverse () { std::swap (mp_v1, mp_v2); std::swap (mp_left, mp_right); } - GPolygon *left () const { return mp_left; } - GPolygon *right () const { return mp_right; } + /** + * @brief Gets the polygon on the left side (can be null) + */ + Polygon *left () const { return mp_left; } - GPolygonIterator begin_polygons () const + /** + * @brief Gets the polygon on the right side (can be null) + */ + Polygon *right () const { return mp_right; } + + /** + * @brief Iterates the polygons (one or two, begin iterator) + */ + PolygonIterator begin_polygons () const { - return GPolygonIterator (this); + return PolygonIterator (this); } - GPolygonIterator end_polygons () const + /** + * @brief Iterates the polygons (end iterator) + */ + PolygonIterator end_polygons () const { - return GPolygonIterator (0); + return PolygonIterator (0); } - void set_level (size_t l) { m_level = l; } - size_t level () const { return m_level; } - - void set_id (size_t id) { m_id = id; } - size_t id () const { return m_id; } - - void set_is_segment (bool is_seg) { m_is_segment = is_seg; } + /** + * @brief Gets a value indicating whether the edge is a segment + */ bool is_segment () const { return m_is_segment; } + /** + * @brief Gets the edge ID (a unique identifier) + */ + size_t id () const { return m_id; } + + /** + * @brief Gets a string representation of the edge + */ std::string to_string (bool with_id = false) const; /** - * @brief Converts to an db::DEdge + * @brief Converts to a db::DEdge */ db::DEdge edge () const { @@ -254,7 +346,7 @@ public: * "crosses" is true, if both edges share at least one point which is not an endpoint * of one of the edges. */ - bool crosses (const db::GPolygonEdge &other) const + bool crosses (const Edge &other) const { return crosses (edge (), other.edge ()); } @@ -279,7 +371,7 @@ public: * @brief Returns a value indicating whether this edge crosses the other one * "crosses" is true, if both edges share at least one point. */ - bool crosses_including (const db::GPolygonEdge &other) const + bool crosses_including (const Edge &other) const { return crosses_including (edge (), other.edge ()); } @@ -301,7 +393,7 @@ public: /** * @brief Gets the intersection point */ - db::DPoint intersection_point (const GPolygonEdge &other) const + db::DPoint intersection_point (const Edge &other) const { return intersection_point (edge (), other.edge ()); } @@ -353,24 +445,23 @@ public: /** * @brief Gets the other triangle for the given one */ - GPolygon *other (const GPolygon *) const; + Polygon *other (const Polygon *) const; /** * @brief Gets the other vertex for the given one */ - GVertex *other (const GVertex *) const; + Vertex *other (const Vertex *) const; /** * @brief Gets a value indicating whether the edge has the given vertex */ - bool has_vertex (const GVertex *) const; + bool has_vertex (const Vertex *) const; /** * @brief Gets the common vertex of the other edge and this edge or null if there is no common vertex */ - GVertex *common_vertex (const GPolygonEdge *other) const; + Vertex *common_vertex (const Edge *other) const; -#if 0 // @@@ /** * @brief Returns a value indicating whether this edge can be flipped */ @@ -379,81 +470,93 @@ public: /** * @brief Returns a value indicating whether the edge separates two triangles that can be joined into one (via the given vertex) */ - bool can_join_via (const GVertex *vertex) const; + bool can_join_via (const Vertex *vertex) const; + + /** + * @brief Returns a value indicating whether this edge belongs to outside triangles + */ + bool is_for_outside_triangles () const; /** * @brief Returns a value indicating whether this edge is an outside edge (no other triangles) */ bool is_outside () const; - /** - * @brief Returns a value indicating whether this edge belongs to outside triangles - */ - bool is_for_outside_triangles () const; -#endif // @@@ - /** * @brief Returns a value indicating whether t is attached to this edge */ - bool has_polygon (const GPolygon *t) const; + bool has_polygon (const Polygon *t) const; protected: void unlink (); void link (); private: - friend class GPolygon; - friend class PolygonGraph; + friend class Polygon; + friend class Graph; + friend class Triangulation; - GVertex *mp_v1, *mp_v2; - GPolygon *mp_left, *mp_right; - GVertex::edges_iterator_non_const m_ec_v1, m_ec_v2; + void set_level (size_t l) { m_level = l; } + size_t level () const { return m_level; } + + void set_id (size_t id) { m_id = id; } + + void set_is_segment (bool is_seg) { m_is_segment = is_seg; } + + Edge (Graph *graph); + Edge (Graph *graph, Vertex *v1, Vertex *v2); + + Graph *mp_graph; + Vertex *mp_v1, *mp_v2; + Polygon *mp_left, *mp_right; + Vertex::edges_iterator_non_const m_ec_v1, m_ec_v2; size_t m_level; size_t m_id; bool m_is_segment; - void set_left (GPolygon *t); - void set_right (GPolygon *t); + void set_left (Polygon *t); + void set_right (Polygon *t); }; /** - * @brief A compare function that compares triangles by ID + * @brief A compare function that compares edges by ID * * The ID acts as a more predicable unique ID for the object in sets and maps. */ -struct GPolygonEdgeLessFunc +struct EdgeLessFunc { - bool operator () (GPolygonEdge *a, GPolygonEdge *b) const + bool operator () (Edge *a, Edge *b) const { return a->id () < b->id (); } }; /** - * @brief A class representing a triangle + * @brief A class representing a polygon */ -class DB_PUBLIC GPolygon - : public tl::list_node, public tl::Object +class DB_PUBLIC Polygon + : public tl::list_node, public tl::Object { public: - GPolygon (); + Polygon (Graph *graph); + Polygon (Graph *graph, Edge *e1, Edge *e2, Edge *e3); template - GPolygon (Iter from, Iter to) - : mp_e (from, to) + Polygon (Graph *graph, Iter from, Iter to) + : mp_graph (graph), mp_e (from, to) { init (); } - ~GPolygon (); + ~Polygon (); void unlink (); void set_id (size_t id) { m_id = id; } size_t id () const { return m_id; } - // @@@bool is_outside () const { return m_is_outside; } - // @@@void set_outside (bool o) { m_is_outside = o; } + bool is_outside () const { return m_is_outside; } + void set_outside (bool o) { m_is_outside = o; } std::string to_string (bool with_id = false) const; @@ -469,7 +572,7 @@ public: * @brief Gets the nth vertex (n wraps around and can be negative) * The vertexes are oriented clockwise. */ - inline GVertex *vertex (int n) const + inline Vertex *vertex (int n) const { size_t sz = size (); tl_assert (sz > 0); @@ -483,7 +586,7 @@ public: /** * @brief Gets the nth edge (n wraps around and can be negative) */ - inline GPolygonEdge *edge (int n) const + inline Edge *edge (int n) const { size_t sz = size (); tl_assert (sz > 0); @@ -504,46 +607,50 @@ public: */ db::DBox bbox () const; -#if 0 // @@@ /** * @brief Gets the center point and radius of the circumcircle * If ok is non-null, it will receive a boolean value indicating whether the circumcircle is valid. * An invalid circumcircle is an indicator for a degenerated triangle with area 0 (or close to). + * + * This method only applies to triangles. */ std::pair circumcircle (bool *ok = 0) const; /** * @brief Gets the vertex opposite of the given edge + * + * This method only applies to triangles. */ - GVertex *opposite (const GPolygonEdge *edge) const; + Vertex *opposite (const Edge *edge) const; /** * @brief Gets the edge opposite of the given vertex + * + * This method only applies to triangles. */ - GPolygonEdge *opposite (const GVertex *vertex) const; -#endif + Edge *opposite (const Vertex *vertex) const; /** * @brief Gets the edge with the given vertexes */ - GPolygonEdge *find_edge_with (const GVertex *v1, const GVertex *v2) const; + Edge *find_edge_with (const Vertex *v1, const Vertex *v2) const; /** * @brief Finds the common edge for both polygons */ - GPolygonEdge *common_edge (const GPolygon *other) const; + Edge *common_edge (const Polygon *other) const; -#if 0 // @@@ /** * @brief Returns a value indicating whether the point is inside (1), on the polygon (0) or outside (-1) + * + * This method only applies to triangles currently. */ int contains (const db::DPoint &point) const; -#endif /** * @brief Gets a value indicating whether the triangle has the given vertex */ - inline bool has_vertex (const db::GVertex *v) const + inline bool has_vertex (const Vertex *v) const { for (auto i = mp_v.begin (); i != mp_v.end (); ++i) { if (*i == v) { @@ -556,7 +663,7 @@ public: /** * @brief Gets a value indicating whether the triangle has the given edge */ - inline bool has_edge (const db::GPolygonEdge *e) const + inline bool has_edge (const Edge *e) const { for (auto i = mp_e.begin (); i != mp_e.end (); ++i) { if (*i == e) { @@ -571,12 +678,12 @@ public: */ double min_edge_length () const; -#if 0 // @@@ /** * @brief Returns the min edge length to circumcircle radius ratio + * + * This method only applies to triangles currently. */ double b () const; -#endif /** * @brief Returns a value indicating whether the polygon borders to a segment @@ -589,16 +696,17 @@ public: unsigned int num_segments () const; private: - // @@@ bool m_is_outside; - std::vector mp_e; - std::vector mp_v; + Graph *mp_graph; + bool m_is_outside; + std::vector mp_e; + std::vector mp_v; size_t m_id; void init (); // no copying - GPolygon &operator= (const GPolygon &); - GPolygon (const GPolygon &); + Polygon &operator= (const Polygon &); + Polygon (const Polygon &); }; /** @@ -606,87 +714,29 @@ private: * * The ID acts as a more predicable unique ID for the object in sets and maps. */ -struct GPolygonLessFunc +struct PolygonLessFunc { - bool operator () (GPolygon *a, GPolygon *b) const + bool operator () (Polygon *a, Polygon *b) const { return a->id () < b->id (); } }; -class DB_PUBLIC PolygonGraph +/** + * @brief A class representing the polygon graph + * + * A polygon graph is the main container, holding vertexes, edges and polygons. + * The graph can be of "triangles" type, in which case it is guaranteed to only + * hold triangles (polygons with 3 vertexes). + */ +class DB_PUBLIC Graph { public: -#if 0 // @@@ - struct TriangulateParameters - { - TriangulateParameters () - : min_b (1.0), - min_length (0.0), - max_area (0.0), - max_area_border (0.0), - max_iterations (std::numeric_limits::max ()), - base_verbosity (30), - mark_triangles (false) - { } - - /** - * @brief Min. readius-to-shortest edge ratio - */ - double min_b; - - /** - * @brief Min. edge length - * - * This parameter does not provide a guarantee about a minimume edge length, but - * helps avoiding ever-reducing triangle splits in acute corners of the input polygon. - * Splitting of edges stops when the edge is less than the min length. - */ - double min_length; - - /** - * @brief Max area or zero for "no constraint" - */ - double max_area; - - /** - * @brief Max area for border triangles or zero for "use max_area" - */ - double max_area_border; - - /** - * @brief Max number of iterations - */ - size_t max_iterations; - - /** - * @brief The verbosity level above which triangulation reports details - */ - int base_verbosity; - - /** - * @brief If true, final triangles are marked using the "id" integer as a bit field - * - * This provides information about the result quality. - * - * Bit 0: skinny triangle - * Bit 1: bad-quality (skinny or area too large) - * Bit 2: non-Delaunay (in the strict sense) - */ - bool mark_triangles; - }; -#endif - - typedef tl::list polygons_type; + typedef tl::list polygons_type; typedef polygons_type::const_iterator polygon_iterator; - PolygonGraph (); - ~PolygonGraph (); - - /** - * @brief Creates a convex decomposition for the given polygon - */ - void convex_decompose (const DPolygon &poly); + Graph (); + ~Graph (); /** * @brief Returns a string representation of the polygon graph. @@ -718,15 +768,6 @@ public: */ void clear (); -protected: -#if 0 // @@@ - /** - * @brief Checks the polygon graph for consistency - * This method is for testing purposes mainly. - */ - bool check (bool check_delaunay = true) const; -#endif - /** * @brief Dumps the polygon graph to a GDS file at the given path * This method is for testing purposes mainly. @@ -743,35 +784,43 @@ protected: */ db::Layout *to_layout (bool decompose_by_id = false) const; -private: - tl::list mp_polygons; - tl::stable_vector m_edges_heap; - std::vector m_returned_edges; - tl::stable_vector m_vertex_heap; -// @@@ bool m_is_constrained; -// @@@ size_t m_level; - size_t m_id; -// @@@ size_t m_flips, m_hops; - - db::GVertex *create_vertex (double x, double y); - db::GVertex *create_vertex (const db::DPoint &pt); - db::GPolygonEdge *create_edge (db::GVertex *v1, db::GVertex *v2); +protected: + Vertex *create_vertex (double x, double y); + Vertex *create_vertex (const db::DPoint &pt); + Edge *create_edge (Vertex *v1, Vertex *v2); template - db::GPolygon * + Polygon * create_polygon (Iter from, Iter to) { - db::GPolygon *res = new db::GPolygon (from ,to); + Polygon *res = new Polygon (this, from ,to); res->set_id (++m_id); mp_polygons.push_back (res); return res; } - void remove_polygon (db::GPolygon *tri); - template void make_contours (const Poly &poly, const Trans &trans, std::vector > &contours); + Polygon *create_triangle (Edge *e1, Edge *e2, Edge *e3); + + void remove_polygon (Polygon *p); + +private: + friend class Triangulation; + friend class ConvexDecomposition; + + tl::list mp_polygons; + tl::stable_vector m_edges_heap; + std::vector m_returned_edges; + tl::stable_vector m_vertex_heap; + size_t m_id; + + tl::list &polygons () { return mp_polygons; } + tl::stable_vector &edges () { return m_edges_heap; } + tl::stable_vector &vertexes () { return m_vertex_heap; } }; -} +} // namespace plc + +} // namespace db #endif diff --git a/src/db/db/dbPLCTriangulation.cc b/src/db/db/dbPLCTriangulation.cc new file mode 100644 index 000000000..879cd9e7d --- /dev/null +++ b/src/db/db/dbPLCTriangulation.cc @@ -0,0 +1,1649 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbPLCTriangulation.h" +#include "dbLayout.h" +#include "dbWriter.h" +#include "tlStream.h" +#include "tlLog.h" +#include "tlTimer.h" + +#include +#include +#include +#include + +namespace db +{ + +namespace plc +{ + +static inline bool is_equal (const db::DPoint &a, const db::DPoint &b) +{ + return std::abs (a.x () - b.x ()) < std::max (1.0, (std::abs (a.x ()) + std::abs (b.x ()))) * db::epsilon && + std::abs (a.y () - b.y ()) < std::max (1.0, (std::abs (a.y ()) + std::abs (b.y ()))) * db::epsilon; +} + +Triangulation::Triangulation (Graph *graph) +{ + mp_graph = graph; + clear (); +} + +void +Triangulation::clear () +{ + mp_graph->clear (); + + m_is_constrained = false; + m_level = 0; + m_id = 0; + m_flips = m_hops = 0; +} + +void +Triangulation::init_box (const db::DBox &box) +{ + double xmin = box.left (), xmax = box.right (); + double ymin = box.bottom (), ymax = box.top (); + + Vertex *vbl = mp_graph->create_vertex (xmin, ymin); + Vertex *vtl = mp_graph->create_vertex (xmin, ymax); + Vertex *vbr = mp_graph->create_vertex (xmax, ymin); + Vertex *vtr = mp_graph->create_vertex (xmax, ymax); + + Edge *sl = mp_graph->create_edge (vbl, vtl); + Edge *sd = mp_graph->create_edge (vtl, vbr); + Edge *sb = mp_graph->create_edge (vbr, vbl); + + Edge *sr = mp_graph->create_edge (vbr, vtr); + Edge *st = mp_graph->create_edge (vtr, vtl); + + mp_graph->create_triangle (sl, sd, sb); + mp_graph->create_triangle (sd, sr, st); +} + +bool +Triangulation::check (bool check_delaunay) const +{ + bool res = true; + + for (auto t = mp_graph->polygons ().begin (); t != mp_graph->polygons ().end (); ++t) { + if (t->size () != 3) { + res = false; + tl::error << "(check error) not a triangle: " << t->to_string (); + } + } + + if (! res) { + return false; + } + + if (check_delaunay) { + for (auto t = mp_graph->polygons ().begin (); t != mp_graph->polygons ().end (); ++t) { + auto cp = t->circumcircle (); + auto vi = find_inside_circle (cp.first, cp.second); + if (! vi.empty ()) { + res = false; + tl::error << "(check error) triangle does not meet Delaunay criterion: " << t->to_string (); + for (auto v = vi.begin (); v != vi.end (); ++v) { + tl::error << " vertex inside circumcircle: " << (*v)->to_string (true); + } + } + } + } + + for (auto t = mp_graph->polygons ().begin (); t != mp_graph->polygons ().end (); ++t) { + for (int i = 0; i < 3; ++i) { + if (! t->edge (i)->has_polygon (t.operator-> ())) { + tl::error << "(check error) edges " << t->edge (i)->to_string (true) + << " attached to triangle " << t->to_string (true) << " does not refer to this triangle"; + res = false; + } + } + } + + for (auto e = mp_graph->edges ().begin (); e != mp_graph->edges ().end (); ++e) { + + if (!e->left () && !e->right ()) { + continue; + } + + if (e->left () && e->right ()) { + if (e->left ()->is_outside () != e->right ()->is_outside () && ! e->is_segment ()) { + tl::error << "(check error) edge " << e->to_string (true) << " splits an outside and inside triangle, but is not a segment"; + res = false; + } + } + + for (auto t = e->begin_polygons (); t != e->end_polygons (); ++t) { + if (! t->has_edge (e.operator-> ())) { + tl::error << "(check error) edge " << e->to_string (true) << " not found in adjacent triangle " << t->to_string (true); + res = false; + } + if (! t->has_vertex (e->v1 ())) { + tl::error << "(check error) edges " << e->to_string (true) << " vertex 1 not found in adjacent triangle " << t->to_string (true); + res = false; + } + if (! t->has_vertex (e->v2 ())) { + tl::error << "(check error) edges " << e->to_string (true) << " vertex 2 not found in adjacent triangle " << t->to_string (true); + res = false; + } + Vertex *vopp = t->opposite (e.operator-> ()); + double sgn = (e->left () == t.operator-> ()) ? 1.0 : -1.0; + double vp = db::vprod (e->d(), *vopp - *e->v1 ()); // positive if on left side + if (vp * sgn <= 0.0) { + const char * side_str = sgn > 0.0 ? "left" : "right"; + tl::error << "(check error) external point " << vopp->to_string (true) << " not on " << side_str << " side of edge " << e->to_string (true); + res = false; + } + } + + if (! e->v1 ()->has_edge (e.operator-> ())) { + tl::error << "(check error) edge " << e->to_string (true) << " vertex 1 does not list this edge"; + res = false; + } + if (! e->v2 ()->has_edge (e.operator-> ())) { + tl::error << "(check error) edge " << e->to_string (true) << " vertex 2 does not list this edge"; + res = false; + } + + } + + for (auto v = mp_graph->vertexes ().begin (); v != mp_graph->vertexes ().end (); ++v) { + unsigned int num_outside_edges = 0; + for (auto e = v->begin_edges (); e != v->end_edges (); ++e) { + if ((*e)->is_outside ()) { + ++num_outside_edges; + } + } + if (num_outside_edges > 0 && num_outside_edges != 2) { + tl::error << "(check error) vertex " << v->to_string (true) << " has " << num_outside_edges << " outside edges (can only be 2)"; + res = false; + for (auto e = v->begin_edges (); e != v->end_edges (); ++e) { + if ((*e)->is_outside ()) { + tl::error << " Outside edge is " << (*e)->to_string (true); + } + } + } + } + + return res; +} + +std::vector +Triangulation::find_points_around (Vertex *vertex, double radius) +{ + std::set seen; + seen.insert (vertex); + + std::vector res; + std::vector new_vertexes, next_vertexes; + new_vertexes.push_back (vertex); + + while (! new_vertexes.empty ()) { + next_vertexes.clear (); + for (auto v = new_vertexes.begin (); v != new_vertexes.end (); ++v) { + for (auto e = (*v)->begin_edges (); e != (*v)->end_edges (); ++e) { + Vertex *ov = (*e)->other (*v); + if (ov->in_circle (*vertex, radius) == 1 && seen.insert (ov).second) { + next_vertexes.push_back (ov); + res.push_back (ov); + } + } + } + new_vertexes.swap (next_vertexes); + } + + return res; +} + +Vertex * +Triangulation::insert_point (const db::DPoint &point, std::list > *new_triangles) +{ + return insert (mp_graph->create_vertex (point), new_triangles); +} + +Vertex * +Triangulation::insert_point (db::DCoord x, db::DCoord y, std::list > *new_triangles) +{ + return insert (mp_graph->create_vertex (x, y), new_triangles); +} + +Vertex * +Triangulation::insert (Vertex *vertex, std::list > *new_triangles) +{ + std::vector tris = find_triangle_for_point (*vertex); + + // the new vertex is outside the domain + if (tris.empty ()) { + tl_assert (! m_is_constrained); + insert_new_vertex (vertex, new_triangles); + return vertex; + } + + // check, if the new vertex is on an edge (may be edge between triangles or edge on outside) + std::vector on_edges; + std::vector on_vertex; + for (int i = 0; i < 3; ++i) { + Edge *e = tris.front ()->edge (i); + if (e->side_of (*vertex) == 0) { + if (is_equal (*vertex, *e->v1 ()) || is_equal (*vertex, *e->v2 ())) { + on_vertex.push_back (e); + } else { + on_edges.push_back (e); + } + } + } + + if (! on_vertex.empty ()) { + + tl_assert (on_vertex.size () == size_t (2)); + return on_vertex.front ()->common_vertex (on_vertex [1]); + + } else if (! on_edges.empty ()) { + + tl_assert (on_edges.size () == size_t (1)); + split_triangles_on_edge (vertex, on_edges.front (), new_triangles); + return vertex; + + } else if (tris.size () == size_t (1)) { + + // the new vertex is inside one triangle + split_triangle (tris.front (), vertex, new_triangles); + return vertex; + + } + + tl_assert (false); +} + +std::vector +Triangulation::find_triangle_for_point (const db::DPoint &point) +{ + Edge *edge = find_closest_edge (point); + + std::vector res; + if (edge) { + for (auto t = edge->begin_polygons (); t != edge->end_polygons (); ++t) { + if (t->contains (point) >= 0) { + res.push_back (t.operator-> ()); + } + } + } + + return res; +} + +Edge * +Triangulation::find_closest_edge (const db::DPoint &p, Vertex *vstart, bool inside_only) +{ + if (!vstart) { + + if (! mp_graph->polygons ().empty ()) { + + unsigned int ls = 0; + size_t n = mp_graph->vertexes ().size (); + size_t m = n; + + // A simple heuristics that takes a sqrt(N) sample from the + // vertexes to find a good starting point + + vstart = mp_graph->polygons ().begin ()->vertex (0); + double dmin = vstart->distance (p); + + while (ls * ls < m) { + m /= 2; + for (size_t i = m / 2; i < n; i += m) { + ++ls; + // NOTE: this assumes the heap is not too loaded with orphan vertexes + Vertex *v = (mp_graph->vertexes ().begin () + i).operator-> (); + if (v->begin_edges () != v->end_edges ()) { + double d = v->distance (p); + if (d < dmin) { + vstart = v; + dmin = d; + } + } + } + } + + } else { + + return 0; + + } + + } + + db::DEdge line (*vstart, p); + + double d = -1.0; + Edge *edge = 0; + Vertex *v = vstart; + + while (v) { + + Vertex *vnext = 0; + + for (auto e = v->begin_edges (); e != v->end_edges (); ++e) { + + if (inside_only) { + // NOTE: in inside mode we stay on the line of sight as we don't + // want to walk around outside pockets. + if (! (*e)->is_segment () && (*e)->is_for_outside_triangles ()) { + continue; + } + if (! (*e)->crosses_including (line)) { + continue; + } + } + + double ds = (*e)->distance (p); + + if (d < 0.0) { + + d = ds; + edge = *e; + vnext = edge->other (v); + + } else if (fabs (ds - d) < std::max (1.0, fabs (ds) + fabs (d)) * db::epsilon) { + + // this differentiation selects the edge which bends further towards + // the target point if both edges share a common point and that + // is the one the determines the distance. + Vertex *cv = edge->common_vertex (*e); + if (cv) { + db::DVector edge_d = *edge->other (cv) - *cv; + db::DVector e_d = *(*e)->other(cv) - *cv; + db::DVector r = p - *cv; + double edge_sp = db::sprod (r, edge_d) / edge_d.length (); + double s_sp = db::sprod (r, e_d) / e_d.length (); + if (s_sp > edge_sp + db::epsilon) { + edge = *e; + vnext = edge->other (v); + } + } + + } else if (ds < d) { + + d = ds; + edge = *e; + vnext = edge->other (v); + + } + + } + + ++m_hops; + + v = vnext; + + } + + return edge; +} + +void +Triangulation::insert_new_vertex (Vertex *vertex, std::list > *new_triangles_out) +{ + if (mp_graph->polygons ().empty ()) { + + tl_assert (mp_graph->vertexes ().size () <= size_t (3)); // fails if vertexes were created but not inserted. + + if (mp_graph->vertexes ().size () == 3) { + + std::vector vv; + for (auto v = mp_graph->vertexes ().begin (); v != mp_graph->vertexes ().end (); ++v) { + vv.push_back (v.operator-> ()); + } + + // form the first triangle + Edge *s1 = mp_graph->create_edge (vv[0], vv[1]); + Edge *s2 = mp_graph->create_edge (vv[1], vv[2]); + Edge *s3 = mp_graph->create_edge (vv[2], vv[0]); + + if (db::vprod_sign (s1->d (), s2->d ()) == 0) { + // avoid degenerate Triangles to happen here + tl_assert (false); + } else { + Polygon *t = mp_graph->create_triangle (s1, s2, s3); + if (new_triangles_out) { + new_triangles_out->push_back (t); + } + } + + } + + return; + + } + + std::vector new_triangles; + + // Find closest edge + Edge *closest_edge = find_closest_edge (*vertex); + tl_assert (closest_edge != 0); + + Edge *s1 = mp_graph->create_edge (vertex, closest_edge->v1 ()); + Edge *s2 = mp_graph->create_edge (vertex, closest_edge->v2 ()); + + Polygon *t = mp_graph->create_triangle (s1, closest_edge, s2); + new_triangles.push_back (t); + + add_more_triangles (new_triangles, closest_edge, closest_edge->v1 (), vertex, s1); + add_more_triangles (new_triangles, closest_edge, closest_edge->v2 (), vertex, s2); + + if (new_triangles_out) { + new_triangles_out->insert (new_triangles_out->end (), new_triangles.begin (), new_triangles.end ()); + } + + fix_triangles (new_triangles, std::vector (), new_triangles_out); +} + +void +Triangulation::add_more_triangles (std::vector &new_triangles, + Edge *incoming_edge, + Vertex *from_vertex, Vertex *to_vertex, + Edge *conn_edge) +{ + while (true) { + + Edge *next_edge = 0; + + for (auto e = from_vertex->begin_edges (); e != from_vertex->end_edges (); ++e) { + if (! (*e)->has_vertex (to_vertex) && (*e)->is_outside ()) { + // TODO: remove and break + tl_assert (next_edge == 0); + next_edge = *e; + } + } + + tl_assert (next_edge != 0); + Vertex *next_vertex = next_edge->other (from_vertex); + + db::DVector d_from_to = *to_vertex - *from_vertex; + Vertex *incoming_vertex = incoming_edge->other (from_vertex); + if (db::vprod_sign(*from_vertex - *incoming_vertex, d_from_to) * db::vprod_sign(*from_vertex - *next_vertex, d_from_to) >= 0) { + return; + } + + Edge *next_conn_edge = mp_graph->create_edge (next_vertex, to_vertex); + Polygon *t = mp_graph->create_triangle (next_conn_edge, next_edge, conn_edge); + new_triangles.push_back (t); + + incoming_edge = next_edge; + conn_edge = next_conn_edge; + from_vertex = next_vertex; + + } +} + +void +Triangulation::split_triangle (Polygon *t, Vertex *vertex, std::list > *new_triangles_out) +{ + t->unlink (); + + std::map v2new_edges; + std::vector new_edges; + for (int i = 0; i < 3; ++i) { + Vertex *v = t->vertex (i); + Edge *e = mp_graph->create_edge (v, vertex); + v2new_edges[v] = e; + new_edges.push_back (e); + } + + std::vector new_triangles; + for (int i = 0; i < 3; ++i) { + Edge *e = t->edge (i); + Polygon *new_triangle = mp_graph->create_triangle (e, v2new_edges[e->v1 ()], v2new_edges[e->v2 ()]); + if (new_triangles_out) { + new_triangles_out->push_back (new_triangle); + } + new_triangle->set_outside (t->is_outside ()); + new_triangles.push_back (new_triangle); + } + + mp_graph->remove_polygon (t); + + fix_triangles (new_triangles, new_edges, new_triangles_out); +} + +void +Triangulation::split_triangles_on_edge (Vertex *vertex, Edge *split_edge, std::list > *new_triangles_out) +{ + Edge *s1 = mp_graph->create_edge (split_edge->v1 (), vertex); + Edge *s2 = mp_graph->create_edge (split_edge->v2 (), vertex); + s1->set_is_segment (split_edge->is_segment ()); + s2->set_is_segment (split_edge->is_segment ()); + + std::vector new_triangles; + + std::vector tris; + tris.reserve (2); + for (auto t = split_edge->begin_polygons (); t != split_edge->end_polygons (); ++t) { + tris.push_back (t.operator-> ()); + } + + for (auto t = tris.begin (); t != tris.end (); ++t) { + + (*t)->unlink (); + + Vertex *ext_vertex = (*t)->opposite (split_edge); + Edge *new_edge = mp_graph->create_edge (ext_vertex, vertex); + + for (int i = 0; i < 3; ++i) { + + Edge *e = (*t)->edge (i); + if (e->has_vertex (ext_vertex)) { + + Edge *partial = e->has_vertex (split_edge->v1 ()) ? s1 : s2; + Polygon *new_triangle = mp_graph->create_triangle (new_edge, partial, e); + + if (new_triangles_out) { + new_triangles_out->push_back (new_triangle); + } + new_triangle->set_outside ((*t)->is_outside ()); + new_triangles.push_back (new_triangle); + + } + + } + + } + + for (auto t = tris.begin (); t != tris.end (); ++t) { + mp_graph->remove_polygon (*t); + } + + std::vector fixed_edges; + fixed_edges.push_back (s1); + fixed_edges.push_back (s2); + fix_triangles (new_triangles, fixed_edges, new_triangles_out); +} + +std::vector +Triangulation::find_touching (const db::DBox &box) const +{ + // NOTE: this is a naive, slow implementation for test purposes + std::vector res; + for (auto v = mp_graph->vertexes ().begin (); v != mp_graph->vertexes ().end (); ++v) { + if (v->begin_edges () != v->end_edges ()) { + if (box.contains (*v)) { + res.push_back (const_cast (v.operator-> ())); + } + } + } + return res; +} + +std::vector +Triangulation::find_inside_circle (const db::DPoint ¢er, double radius) const +{ + // NOTE: this is a naive, slow implementation for test purposes + std::vector res; + for (auto v = mp_graph->vertexes ().begin (); v != mp_graph->vertexes ().end (); ++v) { + if (v->begin_edges () != v->end_edges ()) { + if (v->in_circle (center, radius) == 1) { + res.push_back (const_cast (v.operator-> ())); + } + } + } + return res; +} + +void +Triangulation::remove (Vertex *vertex, std::list > *new_triangles) +{ + if (vertex->begin_edges () == vertex->end_edges ()) { + // removing an orphan vertex -> ignore + } else if (vertex->is_outside ()) { + remove_outside_vertex (vertex, new_triangles); + } else { + remove_inside_vertex (vertex, new_triangles); + } +} + +void +Triangulation::remove_outside_vertex (Vertex *vertex, std::list > *new_triangles_out) +{ + auto to_remove = vertex->polygons (); + + std::vector outer_edges; + for (auto t = to_remove.begin (); t != to_remove.end (); ++t) { + outer_edges.push_back ((*t)->opposite (vertex)); + } + + for (auto t = to_remove.begin (); t != to_remove.end (); ++t) { + (*t)->unlink (); + } + + auto new_triangles = fill_concave_corners (outer_edges); + + for (auto t = to_remove.begin (); t != to_remove.end (); ++t) { + mp_graph->remove_polygon (*t); + } + + fix_triangles (new_triangles, std::vector (), new_triangles_out); +} + +void +Triangulation::remove_inside_vertex (Vertex *vertex, std::list > *new_triangles_out) +{ + std::set triangles_to_fix; + + bool make_new_triangle = true; + + while (vertex->num_edges (4) > 3) { + + Edge *to_flip = 0; + for (auto e = vertex->begin_edges (); e != vertex->end_edges () && to_flip == 0; ++e) { + if ((*e)->can_flip ()) { + to_flip = *e; + } + } + if (! to_flip) { + break; + } + + // NOTE: in the "can_join" case zero-area triangles are created which we will sort out later + triangles_to_fix.erase (to_flip->left ()); + triangles_to_fix.erase (to_flip->right ()); + + auto pp = flip (to_flip); + triangles_to_fix.insert (pp.first.first); + triangles_to_fix.insert (pp.first.second); + + } + + if (vertex->num_edges (4) > 3) { + + tl_assert (vertex->num_edges (5) == 4); + + // This case can happen if two edges attached to the vertex are collinear + // in this case choose the "join" strategy + Edge *jseg = 0; + for (auto e = vertex->begin_edges (); e != vertex->end_edges () && !jseg; ++e) { + if ((*e)->can_join_via (vertex)) { + jseg = *e; + } + } + tl_assert (jseg != 0); + + Vertex *v1 = jseg->left ()->opposite (jseg); + Edge *s1 = jseg->left ()->opposite (vertex); + Vertex *v2 = jseg->right ()->opposite (jseg); + Edge *s2 = jseg->right ()->opposite (vertex); + + Edge *jseg_opp = 0; + for (auto e = vertex->begin_edges (); e != vertex->end_edges () && !jseg_opp; ++e) { + if (!(*e)->has_polygon (jseg->left ()) && !(*e)->has_polygon (jseg->right ())) { + jseg_opp = *e; + } + } + + Edge *s1opp = jseg_opp->left ()->opposite (vertex); + Edge *s2opp = jseg_opp->right ()->opposite (vertex); + + Edge *new_edge = mp_graph->create_edge (v1, v2); + Polygon *t1 = mp_graph->create_triangle (s1, s2, new_edge); + Polygon *t2 = mp_graph->create_triangle (s1opp, s2opp, new_edge); + + triangles_to_fix.insert (t1); + triangles_to_fix.insert (t2); + + make_new_triangle = false; + + } + + auto to_remove = vertex->polygons (); + + std::vector outer_edges; + for (auto t = to_remove.begin (); t != to_remove.end (); ++t) { + outer_edges.push_back ((*t)->opposite (vertex)); + } + + if (make_new_triangle) { + + tl_assert (outer_edges.size () == size_t (3)); + + Polygon *nt = mp_graph->create_triangle (outer_edges[0], outer_edges[1], outer_edges[2]); + triangles_to_fix.insert (nt); + + } + + for (auto t = to_remove.begin (); t != to_remove.end (); ++t) { + triangles_to_fix.erase (*t); + mp_graph->remove_polygon (*t); + } + + if (new_triangles_out) { + for (auto t = triangles_to_fix.begin (); t != triangles_to_fix.end (); ++t) { + new_triangles_out->push_back (*t); + } + } + + std::vector to_fix_a (triangles_to_fix.begin (), triangles_to_fix.end ()); + fix_triangles (to_fix_a, std::vector (), new_triangles_out); +} + +void +Triangulation::fix_triangles (const std::vector &tris, const std::vector &fixed_edges, std::list > *new_triangles) +{ + m_level += 1; + for (auto e = fixed_edges.begin (); e != fixed_edges.end (); ++e) { + (*e)->set_level (m_level); + } + + std::set queue, todo; + + for (auto t = tris.begin (); t != tris.end (); ++t) { + for (int i = 0; i < 3; ++i) { + Edge *e = (*t)->edge (i); + if (e->level () < m_level && ! e->is_segment ()) { + queue.insert (e); + } + } + } + + while (! queue.empty ()) { + + todo.clear (); + todo.swap (queue); + + // NOTE: we cannot be sure that already treated edges will not become + // illegal by neighbor edges flipping .. + // for s in todo: + // s.level = self.level + + for (auto e = todo.begin (); e != todo.end (); ++e) { + + if (is_illegal_edge (*e)) { + + queue.erase (*e); + + auto pp = flip (*e); + auto t1 = pp.first.first; + auto t2 = pp.first.second; + auto s12 = pp.second; + + if (new_triangles) { + new_triangles->push_back (t1); + new_triangles->push_back (t2); + } + + ++m_flips; + tl_assert (! is_illegal_edge (s12)); // TODO: remove later! + + for (int i = 0; i < 3; ++i) { + Edge *s1 = t1->edge (i); + if (s1->level () < m_level && ! s1->is_segment ()) { + queue.insert (s1); + } + } + + for (int i = 0; i < 3; ++i) { + Edge *s2 = t2->edge (i); + if (s2->level () < m_level && ! s2->is_segment ()) { + queue.insert (s2); + } + } + + } + + } + + } +} + +bool +Triangulation::is_illegal_edge (Edge *edge) +{ + Polygon *left = edge->left (); + Polygon *right = edge->right (); + if (!left || !right) { + return false; + } + + bool ok = false; + + auto lr = left->circumcircle (&ok); + if (! ok || right->opposite (edge)->in_circle (lr.first, lr.second) > 0) { + return true; + } + + auto rr = right->circumcircle(&ok); + if (! ok || left->opposite (edge)->in_circle (rr.first, rr.second) > 0) { + return true; + } + + return false; +} + +std::pair, Edge *> +Triangulation::flip (Edge *edge) +{ + Polygon *t1 = edge->left (); + Polygon *t2 = edge->right (); + + bool outside = t1->is_outside (); + tl_assert (t1->is_outside () == outside); + + // prepare for the new triangle to replace this one + t1->unlink (); + t2->unlink (); + + Vertex *t1_vext = t1->opposite (edge); + Edge *t1_sext1 = t1->find_edge_with (t1_vext, edge->v1 ()); + Edge *t1_sext2 = t1->find_edge_with (t1_vext, edge->v2 ()); + + Vertex *t2_vext = t2->opposite (edge); + Edge *t2_sext1 = t2->find_edge_with (t2_vext, edge->v1 ()); + Edge *t2_sext2 = t2->find_edge_with (t2_vext, edge->v2 ()); + + Edge *s_new = mp_graph->create_edge (t1_vext, t2_vext); + + Polygon *t1_new = mp_graph->create_triangle (s_new, t1_sext1, t2_sext1); + t1_new->set_outside (outside); + Polygon *t2_new = mp_graph->create_triangle (s_new, t1_sext2, t2_sext2); + t2_new->set_outside (outside); + + mp_graph->remove_polygon (t1); + mp_graph->remove_polygon (t2); + + return std::make_pair (std::make_pair (t1_new, t2_new), s_new); +} + +std::vector +Triangulation::fill_concave_corners (const std::vector &edges) +{ + std::vector res; + std::vector points, terminals; + + std::map > vertex2edge; + for (auto e = edges.begin (); e != edges.end (); ++e) { + + auto i = vertex2edge.insert (std::make_pair ((*e)->v1 (), std::vector ())); + if (i.second) { + points.push_back ((*e)->v1 ()); + } + i.first->second.push_back (*e); + + i = vertex2edge.insert (std::make_pair ((*e)->v2 (), std::vector ())); + if (i.second) { + points.push_back ((*e)->v2 ()); + } + i.first->second.push_back (*e); + + } + + while (points.size () > size_t (2)) { + + terminals.clear (); + for (auto p = points.begin (); p != points.end (); ++p) { + if (vertex2edge [*p].size () == 1) { + terminals.push_back (*p); + } + } + tl_assert (terminals.size () == size_t (2)); + Vertex *v = terminals[0]; + + bool any_connected = false; + Vertex *vp = 0; + + std::set to_remove; + + while (vertex2edge [v].size () >= size_t (2) || ! vp) { + + Edge *seg = 0; + std::vector &ee = vertex2edge [v]; + for (auto e = ee.begin (); e != ee.end (); ++e) { + if (! (*e)->has_vertex (vp)) { + seg = (*e); + break; + } + } + + tl_assert (seg != 0); + Polygon *tri = seg->left () ? seg->left () : seg->right (); + Vertex *vn = seg->other (v); + + std::vector &een = vertex2edge [vn]; + if (een.size () < size_t (2)) { + break; + } + tl_assert (een.size () == size_t (2)); + + Edge *segn = 0; + for (auto e = een.begin (); e != een.end (); ++e) { + if (! (*e)->has_vertex (v)) { + segn = (*e); + break; + } + } + + tl_assert (segn != 0); + Vertex *vnn = segn->other (vn); + std::vector &eenn = vertex2edge [vnn]; + + // NOTE: tri can be None in case a lonely edge stays after removing + // attached triangles + if (! tri || seg->side_of (*vnn) * seg->side_of (*tri->opposite (seg)) < 0) { + + // is concave + Edge *new_edge = mp_graph->create_edge (v, vnn); + for (auto e = ee.begin (); e != ee.end (); ++e) { + if (*e == seg) { + ee.erase (e); + break; + } + } + ee.push_back (new_edge); + + for (auto e = eenn.begin (); e != eenn.end (); ++e) { + if (*e == segn) { + eenn.erase (e); + break; + } + } + eenn.push_back (new_edge); + + vertex2edge.erase (vn); + to_remove.insert (vn); + + Polygon *new_triangle = mp_graph->create_triangle (seg, segn, new_edge); + res.push_back (new_triangle); + any_connected = true; + + } else { + + vp = v; + v = vn; + + } + + } + + if (! any_connected) { + break; + } + + std::vector::iterator wp = points.begin (); + for (auto p = points.begin (); p != points.end (); ++p) { + if (to_remove.find (*p) == to_remove.end ()) { + *wp++ = *p; + } + } + points.erase (wp, points.end ()); + + } + + return res; +} + +std::vector +Triangulation::search_edges_crossing (Vertex *from, Vertex *to) +{ + Vertex *v = from; + Vertex *vv = to; + db::DEdge edge (*from, *to); + + Polygon *current_triangle = 0; + Edge *next_edge = 0; + + std::vector result; + + for (auto e = v->begin_edges (); e != v->end_edges () && ! next_edge; ++e) { + for (auto t = (*e)->begin_polygons (); t != (*e)->end_polygons (); ++t) { + Edge *os = t->opposite (v); + if (os->has_vertex (vv)) { + return result; + } + if (os->crosses (edge)) { + result.push_back (os); + current_triangle = t.operator-> (); + next_edge = os; + break; + } + } + } + + tl_assert (current_triangle != 0); + + while (true) { + + current_triangle = next_edge->other (current_triangle); + + // Note that we're convex, so there has to be a path across triangles + tl_assert (current_triangle != 0); + + Edge *cs = next_edge; + next_edge = 0; + for (int i = 0; i < 3; ++i) { + Edge *e = current_triangle->edge (i); + if (e != cs) { + if (e->has_vertex (vv)) { + return result; + } + if (e->crosses (edge)) { + result.push_back (e); + next_edge = e; + break; + } + } + } + + tl_assert (next_edge != 0); + + } +} + +Vertex * +Triangulation::find_vertex_for_point (const db::DPoint &point) +{ + Edge *edge = find_closest_edge (point); + if (!edge) { + return 0; + } + Vertex *v = 0; + if (is_equal (*edge->v1 (), point)) { + v = edge->v1 (); + } else if (is_equal (*edge->v2 (), point)) { + v = edge->v2 (); + } + return v; +} + +Edge * +Triangulation::find_edge_for_points (const db::DPoint &p1, const db::DPoint &p2) +{ + Vertex *v = find_vertex_for_point (p1); + if (!v) { + return 0; + } + for (auto e = v->begin_edges (); e != v->end_edges (); ++e) { + if (is_equal (*(*e)->other (v), p2)) { + return *e; + } + } + return 0; +} + +std::vector +Triangulation::ensure_edge_inner (Vertex *from, Vertex *to) +{ + auto crossed_edges = search_edges_crossing (from, to); + std::vector result; + + if (crossed_edges.empty ()) { + + // no crossing edge - there should be a edge already + Edge *res = find_edge_for_points (*from, *to); + tl_assert (res != 0); + result.push_back (res); + + } else if (crossed_edges.size () == 1) { + + // can be solved by flipping + auto pp = flip (crossed_edges.front ()); + Edge *res = pp.second; + tl_assert (res->has_vertex (from) && res->has_vertex (to)); + result.push_back (res); + + } else { + + // split edge close to center + db::DPoint split_point; + double d = -1.0; + double l_half = 0.25 * (*to - *from).sq_length (); + for (auto e = crossed_edges.begin (); e != crossed_edges.end (); ++e) { + db::DPoint p = (*e)->intersection_point (db::DEdge (*from, *to)); + double dp = fabs ((p - *from).sq_length () - l_half); + if (d < 0.0 || dp < d) { + dp = d; + split_point = p; + } + } + + Vertex *split_vertex = insert_point (split_point); + + result = ensure_edge_inner (from, split_vertex); + + auto result2 = ensure_edge_inner (split_vertex, to); + result.insert (result.end (), result2.begin (), result2.end ()); + + } + + return result; +} + +std::vector +Triangulation::ensure_edge (Vertex *from, Vertex *to) +{ +#if 0 + // NOTE: this should not be required if the original segments are non-overlapping + // TODO: this is inefficient + for v in self.vertexes: + if edge.point_on(v): + return self.ensure_edge(Edge(edge.p1, v)) + self.ensure_edge(Edge(v, edge.p2)) +#endif + + auto edges = ensure_edge_inner (from, to); + for (auto e = edges.begin (); e != edges.end (); ++e) { + // mark the edges as fixed "forever" so we don't modify them when we ensure other edges + (*e)->set_level (std::numeric_limits::max ()); + } + return edges; +} + +void +Triangulation::join_edges (std::vector &edges) +{ + // edges are supposed to be ordered + for (size_t i = 1; i < edges.size (); ) { + + Edge *s1 = edges [i - 1]; + Edge *s2 = edges [i]; + tl_assert (s1->is_segment () == s2->is_segment ()); + Vertex *cp = s1->common_vertex (s2); + tl_assert (cp != 0); + + std::vector join_edges; + for (auto e = cp->begin_edges (); e != cp->end_edges (); ++e) { + if (*e != s1 && *e != s2) { + if ((*e)->can_join_via (cp)) { + join_edges.push_back (*e); + } else { + join_edges.clear (); + break; + } + } + } + + if (! join_edges.empty ()) { + + tl_assert (join_edges.size () <= 2); + + Edge *new_edge = mp_graph->create_edge (s1->other (cp), s2->other (cp)); + new_edge->set_is_segment (s1->is_segment ()); + + for (auto js = join_edges.begin (); js != join_edges.end (); ++js) { + + Polygon *t1 = (*js)->left (); + Polygon *t2 = (*js)->right (); + Edge *tedge1 = t1->opposite (cp); + Edge *tedge2 = t2->opposite (cp); + t1->unlink (); + t2->unlink (); + Polygon *tri = mp_graph->create_triangle (tedge1, tedge2, new_edge); + tri->set_outside (t1->is_outside ()); + mp_graph->remove_polygon (t1); + mp_graph->remove_polygon (t2); + } + + edges [i - 1] = new_edge; + edges.erase (edges.begin () + i); + + } else { + ++i; + } + + } +} + +void +Triangulation::constrain (const std::vector > &contours) +{ + tl_assert (! m_is_constrained); + + std::vector > > resolved_edges; + + for (auto c = contours.begin (); c != contours.end (); ++c) { + for (auto v = c->begin (); v != c->end (); ++v) { + auto vv = v; + ++vv; + if (vv == c->end ()) { + vv = c->begin (); + } + db::DEdge e (**v, **vv); + resolved_edges.push_back (std::make_pair (e, std::vector ())); + resolved_edges.back ().second = ensure_edge (*v, *vv); + } + } + + for (auto tri = mp_graph->polygons ().begin (); tri != mp_graph->polygons ().end (); ++tri) { + tri->set_outside (false); + for (int i = 0; i < 3; ++i) { + tri->edge (i)->set_is_segment (false); + } + } + + std::set new_tri; + + for (auto re = resolved_edges.begin (); re != resolved_edges.end (); ++re) { + auto edge = re->first; + auto edges = re->second; + for (auto e = edges.begin (); e != edges.end (); ++e) { + (*e)->set_is_segment (true); + Polygon *outer_tri = 0; + int d = db::sprod_sign (edge.d (), (*e)->d ()); + if (d > 0) { + outer_tri = (*e)->left (); + } + if (d < 0) { + outer_tri = (*e)->right (); + } + if (outer_tri) { + new_tri.insert (outer_tri); + outer_tri->set_outside (true); + } + } + } + + while (! new_tri.empty ()) { + + std::set next_tris; + + for (auto tri = new_tri.begin (); tri != new_tri.end (); ++tri) { + for (int i = 0; i < 3; ++i) { + auto e = (*tri)->edge (i); + if (! e->is_segment ()) { + auto ot = e->other (*tri); + if (ot && ! ot->is_outside ()) { + next_tris.insert (ot); + ot->set_outside (true); + } + } + } + } + + new_tri.swap (next_tris); + + } + + // join edges where possible + for (auto re = resolved_edges.begin (); re != resolved_edges.end (); ++re) { + auto edges = re->second; + join_edges (edges); + } + + m_is_constrained = true; +} + +void +Triangulation::remove_outside_triangles () +{ + tl_assert (m_is_constrained); + + // NOTE: don't remove while iterating + std::vector to_remove; + for (auto tri = mp_graph->begin (); tri != mp_graph->end (); ++tri) { + if (tri->is_outside ()) { + to_remove.push_back (const_cast (tri.operator-> ())); + } + } + + for (auto t = to_remove.begin (); t != to_remove.end (); ++t) { + mp_graph->remove_polygon (*t); + } +} + + +template +void +Triangulation::make_contours (const Poly &poly, const Trans &trans, std::vector > &edge_contours) +{ + edge_contours.push_back (std::vector ()); + for (auto pt = poly.begin_hull (); pt != poly.end_hull (); ++pt) { + edge_contours.back ().push_back (insert_point (trans * *pt)); + } + + for (unsigned int h = 0; h < poly.holes (); ++h) { + edge_contours.push_back (std::vector ()); + for (auto pt = poly.begin_hole (h); pt != poly.end_hole (h); ++pt) { + edge_contours.back ().push_back (insert_point (trans * *pt)); + } + } +} + +void +Triangulation::create_constrained_delaunay (const db::Region ®ion, const CplxTrans &trans) +{ + clear (); + + std::vector > edge_contours; + + for (auto p = region.begin_merged (); ! p.at_end (); ++p) { + make_contours (*p, trans, edge_contours); + } + + constrain (edge_contours); +} + +void +Triangulation::create_constrained_delaunay (const db::Polygon &p, const std::vector &vertexes, const CplxTrans &trans) +{ + clear (); + + for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { + insert_point (trans * *v)->set_is_precious (true); + } + + std::vector > edge_contours; + make_contours (p, trans, edge_contours); + + constrain (edge_contours); +} + +void +Triangulation::create_constrained_delaunay (const db::DPolygon &p, const std::vector &vertexes, const DCplxTrans &trans) +{ + clear (); + + for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { + insert_point (trans * *v)->set_is_precious (true); + } + + std::vector > edge_contours; + make_contours (p, trans, edge_contours); + + constrain (edge_contours); +} + +static bool is_skinny (const Polygon *tri, const TriangulationParameters ¶m) +{ + if (param.min_b < db::epsilon) { + return false; + } else { + double b = tri->b (); + double delta = (b + param.min_b) * db::epsilon; + return b < param.min_b - delta; + } +} + +static bool is_invalid (const Polygon *tri, const TriangulationParameters ¶m) +{ + if (is_skinny (tri, param)) { + return true; + } + + double amax = param.max_area; + if (param.max_area_border > db::epsilon) { + if (tri->has_segment ()) { + amax = param.max_area_border; + } + } + + if (amax > db::epsilon) { + double a = tri->area (); + double delta = (a + amax) * db::epsilon; + return tri->area () > amax + delta; + } else { + return false; + } +} + +void +Triangulation::triangulate (const db::Region ®ion, const TriangulationParameters ¶meters, double dbu) +{ + tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate"); + + create_constrained_delaunay (region, db::CplxTrans (dbu)); + refine (parameters); +} + +void +Triangulation::triangulate (const db::Region ®ion, const TriangulationParameters ¶meters, const db::CplxTrans &trans) +{ + tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate"); + + create_constrained_delaunay (region, trans); + refine (parameters); +} + +void +Triangulation::triangulate (const db::Polygon &poly, const TriangulationParameters ¶meters, double dbu) +{ + triangulate (poly, std::vector (), parameters, dbu); +} + +void +Triangulation::triangulate (const db::Polygon &poly, const std::vector &vertexes, const TriangulationParameters ¶meters, double dbu) +{ + tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate"); + + create_constrained_delaunay (poly, vertexes, db::CplxTrans (dbu)); + refine (parameters); +} + +void +Triangulation::triangulate (const db::Polygon &poly, const TriangulationParameters ¶meters, const db::CplxTrans &trans) +{ + triangulate (poly, std::vector (), parameters, trans); +} + +void +Triangulation::triangulate (const db::Polygon &poly, const std::vector &vertexes, const TriangulationParameters ¶meters, const db::CplxTrans &trans) +{ + tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate"); + + create_constrained_delaunay (poly, vertexes, trans); + refine (parameters); +} + +void +Triangulation::triangulate (const db::DPolygon &poly, const TriangulationParameters ¶meters, const DCplxTrans &trans) +{ + triangulate (poly, std::vector (), parameters, trans); +} + +void +Triangulation::triangulate (const db::DPolygon &poly, const std::vector &vertexes, const TriangulationParameters ¶meters, const DCplxTrans &trans) +{ + tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate"); + + create_constrained_delaunay (poly, vertexes, trans); + refine (parameters); +} + +void +Triangulation::refine (const TriangulationParameters ¶meters) +{ + if (parameters.min_b < db::epsilon && parameters.max_area < db::epsilon && parameters.max_area_border < db::epsilon) { + + // no refinement requested - we're done. + remove_outside_triangles (); + return; + + } + + unsigned int nloop = 0; + std::list > new_triangles; + for (auto t = mp_graph->polygons ().begin (); t != mp_graph->polygons ().end (); ++t) { + new_triangles.push_back (t.operator-> ()); + } + + // TODO: break if iteration gets stuck + while (nloop < parameters.max_iterations) { + + ++nloop; + if (tl::verbosity () >= parameters.base_verbosity + 10) { + tl::info << "Iteration " << nloop << " .."; + } + + std::list > to_consider; + for (auto t = new_triangles.begin (); t != new_triangles.end (); ++t) { + if (t->get () && ! (*t)->is_outside () && is_invalid (t->get (), parameters)) { + to_consider.push_back (*t); + } + } + + if (to_consider.empty ()) { + break; + } + + if (tl::verbosity () >= parameters.base_verbosity + 10) { + tl::info << to_consider.size() << " triangles to consider"; + } + + new_triangles.clear (); + + for (auto t = to_consider.begin (); t != to_consider.end (); ++t) { + + if (! t->get ()) { + // triangle got removed during loop + continue; + } + + auto cr = (*t)->circumcircle(); + auto center = cr.first; + + int s = (*t)->contains (center); + if (s >= 0) { + + if (s > 0) { + + double snap = 1e-3; + + // Snap the center to a segment center if "close" to it. + // This avoids generating very skinny triangles that can't be fixed as the + // segment cannot be flipped. This a part of the issue #1996 problem. + for (unsigned int i = 0; i < 3; ++i) { + if ((*t)->edge (i)->is_segment ()) { + auto e = (*t)->edge (i)->edge (); + auto c = e.p1 () + e.d () * 0.5; + if (c.distance (center) < e.length () * 0.5 * snap - db::epsilon) { + center = c; + break; + } + } + } + + } + + if (tl::verbosity () >= parameters.base_verbosity + 20) { + tl::info << "Inserting in-triangle center " << center.to_string () << " of " << (*t)->to_string (true); + } + insert_point (center, &new_triangles); + + } else { + + Vertex *vstart = 0; + for (unsigned int i = 0; i < 3; ++i) { + Edge *edge = (*t)->edge (i); + vstart = (*t)->opposite (edge); + if (edge->side_of (*vstart) * edge->side_of (center) < 0) { + break; + } + } + + Edge *edge = find_closest_edge (center, vstart, true /*inside only*/); + tl_assert (edge != 0); + + if (! edge->is_segment () || edge->side_of (*vstart) * edge->side_of (center) >= 0) { + + if (tl::verbosity () >= parameters.base_verbosity + 20) { + tl::info << "Inserting out-of-triangle center " << center << " of " << (*t)->to_string (true); + } + insert_point (center, &new_triangles); + + } else { + + double sr = edge->d ().length () * 0.5; + if (sr >= parameters.min_length) { + + db::DPoint pnew = *edge->v1 () + edge->d () * 0.5; + + if (tl::verbosity () >= parameters.base_verbosity + 20) { + tl::info << "Split edge " << edge->to_string (true) << " at " << pnew.to_string (); + } + Vertex *vnew = insert_point (pnew, &new_triangles); + auto vertexes_in_diametral_circle = find_points_around (vnew, sr); + + std::vector to_delete; + for (auto v = vertexes_in_diametral_circle.begin (); v != vertexes_in_diametral_circle.end (); ++v) { + bool has_segment = false; + for (auto e = (*v)->begin_edges (); e != (*v)->end_edges () && ! has_segment; ++e) { + has_segment = (*e)->is_segment (); + } + if (! has_segment && ! (*v)->is_precious ()) { + to_delete.push_back (*v); + } + } + + if (tl::verbosity () >= parameters.base_verbosity + 20) { + tl::info << " -> found " << to_delete.size () << " vertexes to remove"; + } + for (auto v = to_delete.begin (); v != to_delete.end (); ++v) { + remove (*v, &new_triangles); + } + + } + + } + + } + + } + + } + + if (tl::verbosity () >= parameters.base_verbosity + 20) { + tl::info << "Finishing .."; + } + + if (parameters.mark_triangles) { + + for (auto t = mp_graph->begin (); t != mp_graph->end (); ++t) { + + size_t id = 0; + + if (! t->is_outside ()) { + + if (is_skinny (t.operator-> (), parameters)) { + id |= 1; + } + if (is_invalid (t.operator-> (), parameters)) { + id |= 2; + } + auto cp = t->circumcircle (); + auto vi = find_inside_circle (cp.first, cp.second); + if (! vi.empty ()) { + id |= 4; + } + + } + + (const_cast (t.operator->()))->set_id (id); + + } + + } + + remove_outside_triangles (); +} + +} // namespace plc + +} // namespace db diff --git a/src/db/db/dbPLCTriangulation.h b/src/db/db/dbPLCTriangulation.h new file mode 100644 index 000000000..b3e941f89 --- /dev/null +++ b/src/db/db/dbPLCTriangulation.h @@ -0,0 +1,306 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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_dbPLCTriangulation +#define HDR_dbPLCTriangulation + +#include "dbCommon.h" +#include "dbPLC.h" + +#include +#include +#include +#include + +namespace db +{ + +namespace plc +{ + +struct DB_PUBLIC TriangulationParameters +{ + TriangulationParameters () + : min_b (1.0), + min_length (0.0), + max_area (0.0), + max_area_border (0.0), + max_iterations (std::numeric_limits::max ()), + base_verbosity (30), + mark_triangles (false) + { } + + /** + * @brief Min. readius-to-shortest edge ratio + */ + double min_b; + + /** + * @brief Min. edge length + * + * This parameter does not provide a guarantee about a minimume edge length, but + * helps avoiding ever-reducing triangle splits in acute corners of the input polygon. + * Splitting of edges stops when the edge is less than the min length. + */ + double min_length; + + /** + * @brief Max area or zero for "no constraint" + */ + double max_area; + + /** + * @brief Max area for border triangles or zero for "use max_area" + */ + double max_area_border; + + /** + * @brief Max number of iterations + */ + size_t max_iterations; + + /** + * @brief The verbosity level above which triangulation reports details + */ + int base_verbosity; + + /** + * @brief If true, final triangles are marked using the "id" integer as a bit field + * + * This provides information about the result quality. + * + * Bit 0: skinny triangle + * Bit 1: bad-quality (skinny or area too large) + * Bit 2: non-Delaunay (in the strict sense) + */ + bool mark_triangles; +}; + +/** + * @brief A Triangulation algorithm + * + * This class implements a constrained refined Delaunay triangulation using Chew's algorithm. + */ +class DB_PUBLIC Triangulation +{ +public: + /** + * @brief The constructor + * + * The graph will be one filled by the triangulation. + */ + Triangulation (Graph *graph); + + /** + * @brief Clears the triangulation + */ + void clear (); + + /** + * @brief Initializes the triangle collection with a box + * Two triangles will be created. + */ + void init_box (const db::DBox &box); + + /** + * @brief Creates a refined Delaunay triangulation for the given region + * + * The database unit should be chosen in a way that target area values are "in the order of 1". + * For inputs featuring acute angles (angles < ~25 degree), the parameters should defined a min + * edge length ("min_length"). + * "min_length" should be at least 1e-4. If a min edge length is given, the max area constaints + * may not be satisfied. + * + * Edges in the input should not be shorter than 1e-4. + */ + void triangulate (const db::Region ®ion, const TriangulationParameters ¶meters, double dbu = 1.0); + + // more versions + void triangulate (const db::Region ®ion, const TriangulationParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ()); + void triangulate (const db::Polygon &poly, const TriangulationParameters ¶meters, double dbu = 1.0); + void triangulate (const db::Polygon &poly, const std::vector &vertexes, const TriangulationParameters ¶meters, double dbu = 1.0); + void triangulate (const db::Polygon &poly, const TriangulationParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ()); + void triangulate (const db::Polygon &poly, const std::vector &vertexes, const TriangulationParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ()); + + /** + * @brief Triangulates a floating-point polygon + */ + void triangulate (const db::DPolygon &poly, const TriangulationParameters ¶meters, const db::DCplxTrans &trans = db::DCplxTrans ()); + void triangulate (const db::DPolygon &poly, const std::vector &vertexes, const TriangulationParameters ¶meters, const db::DCplxTrans &trans = db::DCplxTrans ()); + + /** + * @brief Inserts a new vertex as the given point + * + * If "new_triangles" is not null, it will receive the list of new triangles created during + * the remove step. + * + * This method can be called after "triangulate" to add new points and adjust the triangulation. + * Inserting new points will maintain the (constrained) Delaunay condition. + */ + Vertex *insert_point (const db::DPoint &point, std::list > *new_triangles = 0); + + /** + * @brief Statistics: number of flips (fixing) + */ + size_t flips () const + { + return m_flips; + } + + /** + * @brief Statistics: number of hops (searching) + */ + size_t hops () const + { + return m_hops; + } + +protected: + /** + * @brief Checks the polygon graph for consistency + * This method is for testing purposes mainly. + */ + bool check (bool check_delaunay = true) const; + + /** + * @brief Finds the points within (not "on") a circle of radius "radius" around the given vertex. + */ + std::vector find_points_around (Vertex *vertex, double radius); + + /** + * @brief Inserts a new vertex as the given point + * + * If "new_triangles" is not null, it will receive the list of new triangles created during + * the remove step. + */ + Vertex *insert_point (db::DCoord x, db::DCoord y, std::list > *new_triangles = 0); + + /** + * @brief Removes the given vertex + * + * If "new_triangles" is not null, it will receive the list of new triangles created during + * the remove step. + */ + void remove (Vertex *vertex, std::list > *new_triangles = 0); + + /** + * @brief Flips the given edge + */ + std::pair, Edge *> flip (Edge *edge); + + /** + * @brief Finds all edges that cross the given one for a convex triangulation + * + * Requirements: + * * self must be a convex triangulation + * * edge must not contain another vertex from the triangulation except p1 and p2 + */ + std::vector search_edges_crossing (Vertex *from, Vertex *to); + + /** + * @brief Finds the edge for two given points + */ + Edge *find_edge_for_points (const db::DPoint &p1, const db::DPoint &p2); + + /** + * @brief Finds the vertex for a point + */ + Vertex *find_vertex_for_point (const db::DPoint &pt); + + /** + * @brief Ensures all points between from an to are connected by edges and makes these segments + */ + std::vector ensure_edge (Vertex *from, Vertex *to); + + /** + * @brief Given a set of contours with edges, mark outer triangles + * + * The edges must be made from existing vertexes. Edge orientation is + * clockwise. + * + * This will also mark triangles as outside ones. + */ + void constrain (const std::vector > &contours); + + /** + * @brief Removes the outside triangles. + */ + void remove_outside_triangles (); + + /** + * @brief Creates a constrained Delaunay triangulation from the given Region + */ + void create_constrained_delaunay (const db::Region ®ion, const db::CplxTrans &trans = db::CplxTrans ()); + + /** + * @brief Creates a constrained Delaunay triangulation from the given Polygon + */ + void create_constrained_delaunay (const db::Polygon &poly, const std::vector &vertexes, const db::CplxTrans &trans = db::CplxTrans ()); + + /** + * @brief Creates a constrained Delaunay triangulation from the given DPolygon + */ + void create_constrained_delaunay (const db::DPolygon &poly, const std::vector &vertexes, const DCplxTrans &trans); + + /** + * @brief Returns a value indicating whether the edge is "illegal" (violates the Delaunay criterion) + */ + static bool is_illegal_edge (Edge *edge); + + // NOTE: these functions are SLOW and intended to test purposes only + std::vector find_touching (const db::DBox &box) const; + std::vector find_inside_circle (const db::DPoint ¢er, double radius) const; + +private: + Graph *mp_graph; + bool m_is_constrained; + size_t m_level; + size_t m_id; + size_t m_flips, m_hops; + + template void make_contours (const Poly &poly, const Trans &trans, std::vector > &contours); + + void remove_outside_vertex (Vertex *vertex, std::list > *new_triangles = 0); + void remove_inside_vertex (Vertex *vertex, std::list > *new_triangles_out = 0); + std::vector fill_concave_corners (const std::vector &edges); + void fix_triangles (const std::vector &tris, const std::vector &fixed_edges, std::list > *new_triangles); + std::vector find_triangle_for_point (const db::DPoint &point); + Edge *find_closest_edge (const db::DPoint &p, Vertex *vstart = 0, bool inside_only = false); + Vertex *insert (Vertex *vertex, std::list > *new_triangles = 0); + void split_triangle (Polygon *t, Vertex *vertex, std::list > *new_triangles_out); + void split_triangles_on_edge (Vertex *vertex, Edge *split_edge, std::list > *new_triangles_out); + void add_more_triangles (std::vector &new_triangles, + Edge *incoming_edge, + Vertex *from_vertex, Vertex *to_vertex, + Edge *conn_edge); + void insert_new_vertex(Vertex *vertex, std::list > *new_triangles_out); + std::vector ensure_edge_inner (Vertex *from, Vertex *to); + void join_edges (std::vector &edges); + void refine (const TriangulationParameters ¶m); +}; + +} // namespace plc + +} // namespace db + +#endif + diff --git a/src/db/unit_tests/dbPolygonGraphTests.cc b/src/db/unit_tests/dbPLCGraphTest.cc similarity index 76% rename from src/db/unit_tests/dbPolygonGraphTests.cc rename to src/db/unit_tests/dbPLCGraphTest.cc index edf152942..e02449401 100644 --- a/src/db/unit_tests/dbPolygonGraphTests.cc +++ b/src/db/unit_tests/dbPLCGraphTest.cc @@ -21,31 +21,21 @@ */ -#include "dbPolygonGraph.h" +#include "dbPLC.h" #include "tlUnitTest.h" #include #include -class TestablePolygonGraph - : public db::PolygonGraph -{ -public: - using db::PolygonGraph::PolygonGraph; - // @@@ using db::PolygonGraph::check; - using db::PolygonGraph::dump; -}; - - TEST(basic) { db::DBox box (0, 0, 100.0, 200.0); - TestablePolygonGraph pg; + db::plc::Graph plc; // @@@ pg.insert_polygon (db::DSimplePolygon (box)); // @@@ - tl::info << pg.to_string (); - pg.dump ("debug.gds"); // @@@ + tl::info << plc.to_string (); + plc.dump ("debug.gds"); // @@@ } diff --git a/src/db/unit_tests/dbPLCTriangulationTests.cc b/src/db/unit_tests/dbPLCTriangulationTests.cc new file mode 100644 index 000000000..0d58ffc04 --- /dev/null +++ b/src/db/unit_tests/dbPLCTriangulationTests.cc @@ -0,0 +1,1117 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbPLCTriangulation.h" +#include "dbWriter.h" +#include "dbRegionProcessors.h" +#include "tlUnitTest.h" +#include "tlStream.h" +#include "tlFileUtils.h" + +#include +#include +#include +#include + +class TestableTriangulation + : public db::plc::Triangulation +{ +public: + using db::plc::Triangulation::Triangulation; + using db::plc::Triangulation::check; + using db::plc::Triangulation::flip; + using db::plc::Triangulation::insert_point; + using db::plc::Triangulation::search_edges_crossing; + using db::plc::Triangulation::find_edge_for_points; + using db::plc::Triangulation::find_points_around; + using db::plc::Triangulation::find_inside_circle; + using db::plc::Triangulation::create_constrained_delaunay; + using db::plc::Triangulation::is_illegal_edge; + using db::plc::Triangulation::find_vertex_for_point; + using db::plc::Triangulation::remove; + using db::plc::Triangulation::ensure_edge; + using db::plc::Triangulation::constrain; + using db::plc::Triangulation::remove_outside_triangles; +}; + +class TestableGraph + : public db::plc::Graph +{ +public: + using db::plc::Graph::Graph; + using db::plc::Graph::create_vertex; + using db::plc::Graph::create_edge; + using db::plc::Graph::create_triangle; +}; + +TEST(basic) +{ + db::plc::Graph plc; + TestableTriangulation tris (&plc); + tris.init_box (db::DBox (1, 0, 5, 4)); + + EXPECT_EQ (plc.bbox ().to_string (), "(1,0;5,4)"); + EXPECT_EQ (plc.to_string (), "((1, 0), (1, 4), (5, 0)), ((1, 4), (5, 4), (5, 0))"); + + EXPECT_EQ (tris.check (), true); + + tris.clear (); + + EXPECT_EQ (plc.bbox ().to_string (), "()"); + EXPECT_EQ (plc.to_string (), ""); + + EXPECT_EQ (tris.check (), true); +} + +TEST(flip) +{ + db::plc::Graph plc; + TestableTriangulation tris (&plc); + tris.init_box (db::DBox (0, 0, 1, 1)); + EXPECT_EQ (plc.to_string (), "((0, 0), (0, 1), (1, 0)), ((0, 1), (1, 1), (1, 0))"); + + EXPECT_EQ (plc.num_polygons (), size_t (2)); + EXPECT_EQ (tris.check (), true); + + const db::plc::Polygon &t1 = *plc.begin (); + db::plc::Edge *diag_segment; + for (int i = 0; i < 3; ++i) { + diag_segment = t1.edge (i); + if (diag_segment->side_of (db::DPoint (0.5, 0.5)) == 0) { + break; + } + } + tris.flip (diag_segment); + EXPECT_EQ (plc.to_string (), "((1, 1), (0, 0), (0, 1)), ((1, 1), (1, 0), (0, 0))"); + EXPECT_EQ (tris.check (), true); +} + +TEST(insert) +{ + db::plc::Graph plc; + TestableTriangulation tris (&plc); + tris.init_box (db::DBox (0, 0, 1, 1)); + + tris.insert_point (0.2, 0.2); + EXPECT_EQ (plc.to_string (), "((0, 0), (0, 1), (0.2, 0.2)), ((1, 0), (0, 0), (0.2, 0.2)), ((1, 1), (0.2, 0.2), (0, 1)), ((1, 1), (1, 0), (0.2, 0.2))"); + EXPECT_EQ (tris.check (), true); +} + +TEST(split_segment) +{ + db::plc::Graph plc; + TestableTriangulation tris (&plc); + tris.init_box (db::DBox (0, 0, 1, 1)); + + tris.insert_point (0.5, 0.5); + EXPECT_EQ (plc.to_string (), "((1, 1), (1, 0), (0.5, 0.5)), ((1, 1), (0.5, 0.5), (0, 1)), ((0, 0), (0, 1), (0.5, 0.5)), ((0, 0), (0.5, 0.5), (1, 0))"); + EXPECT_EQ (tris.check(), true); +} + +TEST(insert_vertex_twice) +{ + db::plc::Graph plc; + TestableTriangulation tris (&plc); + tris.init_box (db::DBox (0, 0, 1, 1)); + + tris.insert_point (0.5, 0.5); + // inserted a vertex twice does not change anything + tris.insert_point (0.5, 0.5); + EXPECT_EQ (plc.to_string (), "((1, 1), (1, 0), (0.5, 0.5)), ((1, 1), (0.5, 0.5), (0, 1)), ((0, 0), (0, 1), (0.5, 0.5)), ((0, 0), (0.5, 0.5), (1, 0))"); + EXPECT_EQ (tris.check(), true); +} + +TEST(insert_vertex_convex) +{ + db::plc::Graph plc; + TestableTriangulation tris (&plc); + tris.insert_point (0.2, 0.2); + tris.insert_point (0.2, 0.8); + tris.insert_point (0.6, 0.5); + tris.insert_point (0.7, 0.5); + tris.insert_point (0.6, 0.4); + EXPECT_EQ (plc.to_string (), "((0.2, 0.2), (0.2, 0.8), (0.6, 0.5)), ((0.2, 0.8), (0.7, 0.5), (0.6, 0.5)), ((0.6, 0.4), (0.6, 0.5), (0.7, 0.5)), ((0.6, 0.4), (0.2, 0.2), (0.6, 0.5))"); + EXPECT_EQ (tris.check(), true); +} + +TEST(insert_vertex_convex2) +{ + db::plc::Graph plc; + TestableTriangulation tris (&plc); + tris.insert_point (0.25, 0.1); + tris.insert_point (0.1, 0.4); + tris.insert_point (0.4, 0.15); + tris.insert_point (1, 0.7); + EXPECT_EQ (plc.to_string (), "((0.25, 0.1), (0.1, 0.4), (0.4, 0.15)), ((1, 0.7), (0.4, 0.15), (0.1, 0.4))"); + EXPECT_EQ (tris.check(), true); +} + +TEST(insert_vertex_convex3) +{ + db::plc::Graph plc; + TestableTriangulation tris (&plc); + tris.insert_point (0.25, 0.5); + tris.insert_point (0.25, 0.55); + tris.insert_point (0.15, 0.8); + tris.insert_point (1, 0.4); + EXPECT_EQ (plc.to_string (), "((0.25, 0.5), (0.15, 0.8), (0.25, 0.55)), ((1, 0.4), (0.25, 0.5), (0.25, 0.55)), ((0.15, 0.8), (1, 0.4), (0.25, 0.55))"); + EXPECT_EQ (tris.check(), true); +} + +TEST(search_edges_crossing) +{ + db::plc::Graph plc; + TestableTriangulation tris (&plc); + db::plc::Vertex *v1 = tris.insert_point (0.2, 0.2); + db::plc::Vertex *v2 = tris.insert_point (0.2, 0.8); + db::plc::Vertex *v3 = tris.insert_point (0.6, 0.5); + /*db::plc::Vertex *v4 =*/ tris.insert_point (0.7, 0.5); + db::plc::Vertex *v5 = tris.insert_point (0.6, 0.4); + db::plc::Vertex *v6 = tris.insert_point (0.7, 0.2); + EXPECT_EQ (tris.check(), true); + + auto xedges = tris.search_edges_crossing (v2, v6); + + EXPECT_EQ (xedges.size (), size_t (2)); + auto s1 = tris.find_edge_for_points (*v1, *v3); + auto s2 = tris.find_edge_for_points (*v1, *v5); + EXPECT_EQ (std::find (xedges.begin (), xedges.end (), s1) != xedges.end (), true); + EXPECT_EQ (std::find (xedges.begin (), xedges.end (), s2) != xedges.end (), true); +} + +TEST(illegal_edge1) +{ + TestableGraph plc; + + db::plc::Vertex *v1 = plc.create_vertex (0, 0); + db::plc::Vertex *v2 = plc.create_vertex (1.6, 1.6); + db::plc::Vertex *v3 = plc.create_vertex (1, 2); + db::plc::Vertex *v4 = plc.create_vertex (2, 1); + + { + db::plc::Edge *e1 = plc.create_edge (v1, v3); + db::plc::Edge *e2 = plc.create_edge (v3, v4); + db::plc::Edge *e3 = plc.create_edge (v4, v1); + + plc.create_triangle (e1, e2, e3); + + db::plc::Edge *ee1 = plc.create_edge (v2, v3); + db::plc::Edge *ee2 = plc.create_edge (v4, v2); + + plc.create_triangle (ee1, e2, ee2); + + EXPECT_EQ (TestableTriangulation::is_illegal_edge (e2), true); + } + + { + // flipped + db::plc::Edge *e1 = plc.create_edge (v1, v2); + db::plc::Edge *e2 = plc.create_edge (v2, v3); + db::plc::Edge *e3 = plc.create_edge (v3, v1); + + plc.create_triangle (e1, e2, e3); + + db::plc::Edge *ee1 = plc.create_edge (v1, v4); + db::plc::Edge *ee2 = plc.create_edge (v4, v2); + + plc.create_triangle (ee1, ee2, e1); + + EXPECT_EQ (TestableTriangulation::is_illegal_edge (e2), false); + } +} + +TEST(illegal_edge2) +{ + TestableGraph plc; + + // numerical border case + db::plc::Vertex *v1 = plc.create_vertex (773.94756216690905, 114.45875269431208); + db::plc::Vertex *v2 = plc.create_vertex (773.29574734131643, 113.47402096138073); + db::plc::Vertex *v3 = plc.create_vertex (773.10652961562653, 114.25497975904504); + db::plc::Vertex *v4 = plc.create_vertex (774.08856345337881, 113.60495072750861); + + { + db::plc::Edge *e1 = plc.create_edge (v1, v2); + db::plc::Edge *e2 = plc.create_edge (v2, v4); + db::plc::Edge *e3 = plc.create_edge (v4, v1); + + plc.create_triangle (e1, e2, e3); + + db::plc::Edge *ee1 = plc.create_edge (v2, v3); + db::plc::Edge *ee2 = plc.create_edge (v3, v4); + + plc.create_triangle (ee1, ee2, e2); + + EXPECT_EQ (TestableTriangulation::is_illegal_edge (e2), false); + } + + { + // flipped + db::plc::Edge *e1 = plc.create_edge (v1, v2); + db::plc::Edge *e2 = plc.create_edge (v2, v3); + db::plc::Edge *e3 = plc.create_edge (v3, v1); + + plc.create_triangle (e1, e2, e3); + + db::plc::Edge *ee1 = plc.create_edge (v1, v4); + db::plc::Edge *ee2 = plc.create_edge (v4, v2); + + plc.create_triangle (ee1, ee2, e1); + + EXPECT_EQ (TestableTriangulation::is_illegal_edge (e1), false); + } +} + +// Returns a random float number between 0.0 and 1.0 +inline double flt_rand () +{ + return rand () * (1.0 / double (RAND_MAX)); +} + +namespace { + struct PointLessOp + { + bool operator() (const db::DPoint &a, const db::DPoint &b) const + { + return a.less (b); + } + }; +} + +TEST(insert_many) +{ + srand (0); + + db::plc::Graph plc; + TestableTriangulation tris (&plc); + double res = 65536.0; + + db::DBox bbox; + + unsigned int n = 200000; + for (unsigned int i = 0; i < n; ++i) { + double x = round (flt_rand () * res) * 0.0001; + double y = round (flt_rand () * res) * 0.0001; + tris.insert_point (x, y); + } + + // slow: EXPECT_EQ (tris.check (), true); + EXPECT_LT (double (tris.flips ()) / double (n), 3.1); + EXPECT_LT (double (tris.hops ()) / double (n), 23.0); +} + +TEST(heavy_insert) +{ + tl::info << "Running test_heavy_insert " << tl::noendl; + + for (unsigned int l = 0; l < 100; ++l) { + + srand (l); + tl::info << "." << tl::noendl; + + db::plc::Graph plc; + TestableTriangulation tris (&plc); + double res = 128.0; + + unsigned int n = rand () % 190 + 10; + + db::DBox bbox; + std::map vmap; + + for (unsigned int i = 0; i < n; ++i) { + double x = round (flt_rand () * res) * (1.0 / res); + double y = round (flt_rand () * res) * (1.0 / res); + db::plc::Vertex *v = tris.insert_point (x, y); + bbox += db::DPoint (x, y); + vmap.insert (std::make_pair (*v, false)); + } + + // not strictly true, but very likely with at least 10 vertexes: + EXPECT_GT (plc.num_polygons (), size_t (0)); + EXPECT_EQ (plc.bbox ().to_string (), bbox.to_string ()); + + bool ok = true; + for (auto t = plc.begin (); t != plc.end (); ++t) { + for (int i = 0; i < 3; ++i) { + auto f = vmap.find (*t->vertex (i)); + if (f == vmap.end ()) { + tl::error << "Could not identify triangle vertex " << t->vertex (i)->to_string () << " as inserted vertex"; + ok = false; + } else { + f->second = true; + } + } + } + for (auto m = vmap.begin (); m != vmap.end (); ++m) { + if (!m->second) { + tl::error << "Could not identify vertex " << m->first.to_string () << " with a triangle"; + ok = false; + } + } + EXPECT_EQ (ok, true); + + EXPECT_EQ (tris.check(), true); + + } + + tl::info << tl::endl << "done."; +} + +TEST(heavy_remove) +{ + tl::info << "Running test_heavy_remove " << tl::noendl; + + for (unsigned int l = 0; l < 100; ++l) { + + srand (l); + tl::info << "." << tl::noendl; + + db::plc::Graph plc; + TestableTriangulation tris (&plc); + double res = 128.0; + + unsigned int n = rand () % 190 + 10; + + for (unsigned int i = 0; i < n; ++i) { + double x = round (flt_rand () * res) * (1.0 / res); + double y = round (flt_rand () * res) * (1.0 / res); + tris.insert_point (x, y); + } + + EXPECT_EQ (tris.check(), true); + + std::set vset; + std::vector vertexes; + for (auto t = plc.begin (); t != plc.end (); ++t) { + for (int i = 0; i < 3; ++i) { + db::plc::Vertex *v = t->vertex (i); + if (vset.insert (v).second) { + vertexes.push_back (v); + } + } + } + + while (! vertexes.empty ()) { + + unsigned int n = rand () % (unsigned int) vertexes.size (); + db::plc::Vertex *v = vertexes [n]; + tris.remove (v); + vertexes.erase (vertexes.begin () + n); + + // just a few times as it wastes time otherwise + if (vertexes.size () % 10 == 0) { + EXPECT_EQ (tris.check (), true); + } + + } + + EXPECT_EQ (plc.num_polygons (), size_t (0)); + + } + + tl::info << tl::endl << "done."; +} + +TEST(ensure_edge) +{ + srand (0); + + db::plc::Graph plc; + TestableTriangulation tris (&plc); + double res = 128.0; + + db::DEdge ee[] = { + db::DEdge (0.25, 0.25, 0.25, 0.75), + db::DEdge (0.25, 0.75, 0.75, 0.75), + db::DEdge (0.75, 0.75, 0.75, 0.25), + db::DEdge (0.75, 0.25, 0.25, 0.25) + }; + + for (unsigned int i = 0; i < 200; ++i) { + double x = round (flt_rand () * res) * (1.0 / res); + double y = round (flt_rand () * res) * (1.0 / res); + bool ok = true; + for (unsigned int j = 0; j < sizeof (ee) / sizeof (ee[0]); ++j) { + if (ee[j].side_of (db::DPoint (x, y)) == 0) { + --i; + ok = false; + } + } + if (ok) { + tris.insert_point (x, y); + } + } + + for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { + tris.insert_point (ee[i].p1 ()); + } + + EXPECT_EQ (tris.check (), true); + + for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { + tris.ensure_edge (tris.find_vertex_for_point (ee[i].p1 ()), tris.find_vertex_for_point (ee[i].p2 ())); + } + + EXPECT_EQ (tris.check (false), true); + + double area_in = 0.0; + db::DBox clip_box; + for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { + clip_box += ee[i].p1 (); + } + for (auto t = plc.begin (); t != plc.end (); ++t) { + if (clip_box.overlaps (t->bbox ())) { + EXPECT_EQ (t->bbox ().inside (clip_box), true); + area_in += t->area (); + } + } + + EXPECT_EQ (tl::to_string (area_in), "0.25"); +} + +static bool safe_inside (const db::DBox &b1, const db::DBox &b2) +{ + typedef db::coord_traits ct; + + return (ct::less (b2.left (), b1.left ()) || ct::equal (b2.left (), b1.left ())) && + (ct::less (b1.right (), b2.right ()) || ct::equal (b1.right (), b2.right ())) && + (ct::less (b2.bottom (), b1.bottom ()) || ct::equal (b2.bottom (), b1.bottom ())) && + (ct::less (b1.top (), b2.top ()) || ct::equal (b1.top (), b2.top ())); +} + +TEST(constrain) +{ + srand (0); + + db::plc::Graph plc; + TestableTriangulation tris (&plc); + double res = 128.0; + + db::DEdge ee[] = { + db::DEdge (0.25, 0.25, 0.25, 0.75), + db::DEdge (0.25, 0.75, 0.75, 0.75), + db::DEdge (0.75, 0.75, 0.75, 0.25), + db::DEdge (0.75, 0.25, 0.25, 0.25) + }; + + for (unsigned int i = 0; i < 200; ++i) { + double x = round (flt_rand () * res) * (1.0 / res); + double y = round (flt_rand () * res) * (1.0 / res); + bool ok = true; + for (unsigned int j = 0; j < sizeof (ee) / sizeof (ee[0]); ++j) { + if (ee[j].side_of (db::DPoint (x, y)) == 0) { + --i; + ok = false; + } + } + if (ok) { + tris.insert_point (x, y); + } + } + + std::vector contour; + for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { + contour.push_back (tris.insert_point (ee[i].p1 ())); + } + std::vector > contours; + contours.push_back (contour); + + EXPECT_EQ (tris.check (), true); + + tris.constrain (contours); + EXPECT_EQ (tris.check (false), true); + + tris.remove_outside_triangles (); + + EXPECT_EQ (tris.check (), true); + + double area_in = 0.0; + db::DBox clip_box; + for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { + clip_box += ee[i].p1 (); + } + for (auto t = plc.begin (); t != plc.end (); ++t) { + EXPECT_EQ (clip_box.overlaps (t->bbox ()), true); + EXPECT_EQ (safe_inside (t->bbox (), clip_box), true); + area_in += t->area (); + } + + EXPECT_EQ (tl::to_string (area_in), "0.25"); +} + +TEST(heavy_constrain) +{ + tl::info << "Running test_heavy_constrain " << tl::noendl; + + for (unsigned int l = 0; l < 100; ++l) { + + srand (l); + tl::info << "." << tl::noendl; + + db::plc::Graph plc; + TestableTriangulation tris (&plc); + double res = 128.0; + + db::DEdge ee[] = { + db::DEdge (0.25, 0.25, 0.25, 0.75), + db::DEdge (0.25, 0.75, 0.75, 0.75), + db::DEdge (0.75, 0.75, 0.75, 0.25), + db::DEdge (0.75, 0.25, 0.25, 0.25) + }; + + unsigned int n = rand () % 150 + 50; + + for (unsigned int i = 0; i < n; ++i) { + double x = round (flt_rand () * res) * (1.0 / res); + double y = round (flt_rand () * res) * (1.0 / res); + bool ok = true; + for (unsigned int j = 0; j < sizeof (ee) / sizeof (ee[0]); ++j) { + if (ee[j].side_of (db::DPoint (x, y)) == 0) { + --i; + ok = false; + } + } + if (ok) { + tris.insert_point (x, y); + } + } + + std::vector contour; + for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { + contour.push_back (tris.insert_point (ee[i].p1 ())); + } + std::vector > contours; + contours.push_back (contour); + + EXPECT_EQ (tris.check (), true); + + tris.constrain (contours); + EXPECT_EQ (tris.check (false), true); + + tris.remove_outside_triangles (); + + EXPECT_EQ (tris.check (), true); + + double area_in = 0.0; + db::DBox clip_box; + for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { + clip_box += ee[i].p1 (); + } + for (auto t = plc.begin (); t != plc.end (); ++t) { + EXPECT_EQ (clip_box.overlaps (t->bbox ()), true); + EXPECT_EQ (safe_inside (t->bbox (), clip_box), true); + area_in += t->area (); + } + + EXPECT_EQ (tl::to_string (area_in), "0.25"); + + } + + tl::info << tl::endl << "done."; +} + +TEST(heavy_find_point_around) +{ + tl::info << "Running Triangle_test_heavy_find_point_around " << tl::noendl; + + for (unsigned int l = 0; l < 100; ++l) { + + srand (l); + tl::info << "." << tl::noendl; + + db::plc::Graph plc; + TestableTriangulation tris (&plc); + double res = 128.0; + + unsigned int n = rand () % 190 + 10; + + std::vector vertexes; + + for (unsigned int i = 0; i < n; ++i) { + double x = round (flt_rand () * res) * (1.0 / res); + double y = round (flt_rand () * res) * (1.0 / res); + vertexes.push_back (tris.insert_point (x, y)); + } + + EXPECT_EQ (tris.check(), true); + + for (int i = 0; i < 100; ++i) { + + unsigned int nv = rand () % (unsigned int) vertexes.size (); + auto vertex = vertexes [nv]; + + double r = round (flt_rand () * res) * (1.0 / res); + auto p1 = tris.find_points_around (vertex, r); + auto p2 = tris.find_inside_circle (*vertex, r); + + std::set sp1 (p1.begin (), p1.end ()); + std::set sp2 (p2.begin (), p2.end ()); + sp2.erase (vertex); + + EXPECT_EQ (sp1 == sp2, true); + + } + + } + + tl::info << tl::endl << "done."; +} + +TEST(create_constrained_delaunay) +{ + db::Region r; + r.insert (db::Box (0, 0, 1000, 1000)); + + db::Region r2; + r2.insert (db::Box (200, 200, 800, 800)); + + r -= r2; + + db::plc::Graph plc; + TestableTriangulation tris (&plc); + tris.create_constrained_delaunay (r); + tris.remove_outside_triangles (); + + EXPECT_EQ (tris.check (), true); + + EXPECT_EQ (plc.to_string (), + "((1000, 0), (0, 0), (200, 200)), " + "((0, 1000), (200, 200), (0, 0)), " + "((1000, 0), (200, 200), (800, 200)), " + "((1000, 0), (800, 200), (1000, 1000)), " + "((800, 200), (800, 800), (1000, 1000)), " + "((0, 1000), (1000, 1000), (800, 800)), " + "((0, 1000), (800, 800), (200, 800)), " + "((0, 1000), (200, 800), (200, 200))"); +} + +TEST(triangulate_basic) +{ + db::Region r; + r.insert (db::Box (0, 0, 10000, 10000)); + + db::Region r2; + r2.insert (db::Box (2000, 2000, 8000, 8000)); + + r -= r2; + + db::plc::TriangulationParameters param; + param.min_b = 1.2; + param.max_area = 1.0; + + db::plc::Graph plc; + TestableTriangulation tri (&plc); + tri.triangulate (r, param, 0.001); + + EXPECT_EQ (tri.check (), true); + + for (auto t = plc.begin (); t != plc.end (); ++t) { + EXPECT_LE (t->area (), param.max_area); + EXPECT_GE (t->b (), param.min_b); + } + + EXPECT_GT (plc.num_polygons (), size_t (100)); + EXPECT_LT (plc.num_polygons (), size_t (150)); + + // for debugging: + // tri.dump ("debug.gds"); + + param.min_b = 1.0; + param.max_area = 0.1; + + tri.triangulate (r, param, 0.001); + + EXPECT_EQ (tri.check (), true); + + for (auto t = plc.begin (); t != plc.end (); ++t) { + EXPECT_LE (t->area (), param.max_area); + EXPECT_GE (t->b (), param.min_b); + } + + EXPECT_GT (plc.num_polygons (), size_t (900)); + EXPECT_LT (plc.num_polygons (), size_t (1000)); +} + +static void read_polygons (const std::string &path, db::Region ®ion, double dbu) +{ + tl::InputStream is (path); + tl::TextInputStream ti (is); + + unsigned int nvert = 0, nedges = 0; + + { + tl::Extractor ex (ti.get_line ().c_str ()); + ex.read (nvert); + ex.read (nedges); + } + + std::vector v; + auto dbu_trans = db::CplxTrans (dbu).inverted (); + for (unsigned int i = 0; i < nvert; ++i) { + double x = 0, y = 0; + tl::Extractor ex (ti.get_line ().c_str ()); + ex.read (x); + ex.read (y); + v.push_back (dbu_trans * db::DPoint (x, y)); + } + + unsigned int nstart = 0; + bool new_contour = true; + std::vector contour; + + for (unsigned int i = 0; i < nedges; ++i) { + + unsigned int n1 = 0, n2 = 0; + + tl::Extractor ex (ti.get_line ().c_str ()); + ex.read (n1); + ex.read (n2); + + if (new_contour) { + nstart = n1; + new_contour = false; + } + + contour.push_back (v[n1]); + + if (n2 == nstart) { + // finish contour + db::SimplePolygon sp; + sp.assign_hull (contour.begin (), contour.end ()); + region.insert (sp); + new_contour = true; + contour.clear (); + } else if (n2 <= n1) { + tl::error << "Invalid polygon wrap in line " << ti.line_number (); + tl_assert (false); + } + + } +} + +TEST(triangulate_geo) +{ + double dbu = 0.001; + + db::Region r; + read_polygons (tl::combine_path (tl::testsrc (), "testdata/algo/triangles1.txt"), r, dbu); + + // for debugging purposes dump the inputs + if (false) { + + db::Layout layout = db::Layout (); + layout.dbu (dbu); + db::Cell &top = layout.cell (layout.add_cell ("DUMP")); + unsigned int l1 = layout.insert_layer (db::LayerProperties (1, 0)); + r.insert_into (&layout, top.cell_index (), l1); + + { + tl::OutputStream stream ("input.gds"); + db::SaveLayoutOptions opt; + db::Writer writer (opt); + writer.write (layout, stream); + } + + } + + db::plc::TriangulationParameters param; + param.min_b = 1.0; + param.max_area = 0.1; + param.min_length = 0.001; + + db::plc::Graph plc; + TestableTriangulation tri (&plc); + tri.triangulate (r, param, dbu); + + EXPECT_EQ (tri.check (false), true); + + // for debugging: + // tri.dump ("debug.gds"); + + size_t n_skinny = 0; + for (auto t = plc.begin (); t != plc.end (); ++t) { + EXPECT_LE (t->area (), param.max_area); + if (t->b () < param.min_b) { + ++n_skinny; + } + } + + EXPECT_LT (n_skinny, size_t (20)); + EXPECT_GT (plc.num_polygons (), size_t (29000)); + EXPECT_LT (plc.num_polygons (), size_t (30000)); +} + +TEST(triangulate_analytic) +{ + double dbu = 0.0001; + + double star1 = 9.0, star2 = 5.0; + double r = 1.0; + int n = 100; + + auto dbu_trans = db::CplxTrans (dbu).inverted (); + + std::vector contour1, contour2; + for (int i = 0; i < n; ++i) { + double a = -M_PI * 2.0 * double (i) / double (n); // "-" for clockwise orientation + double rr, x, y; + rr = r * (1.0 + 0.4 * cos (star1 * a)); + x = rr * cos (a); + y = rr * sin (a); + contour1.push_back (dbu_trans * db::DPoint (x, y)); + rr = r * (0.1 + 0.03 * cos (star2 * a)); + x = rr * cos (a); + y = rr * sin (a); + contour2.push_back (dbu_trans * db::DPoint (x, y)); + } + + db::Region rg; + + db::SimplePolygon sp1; + sp1.assign_hull (contour1.begin (), contour1.end ()); + db::SimplePolygon sp2; + sp2.assign_hull (contour2.begin (), contour2.end ()); + + rg = db::Region (sp1) - db::Region (sp2); + + db::plc::TriangulationParameters param; + param.min_b = 1.0; + param.max_area = 0.01; + + db::plc::Graph plc; + TestableTriangulation tri (&plc); + tri.triangulate (rg, param, dbu); + + EXPECT_EQ (tri.check (false), true); + + // for debugging: + // tri.dump ("debug.gds"); + + for (auto t = plc.begin (); t != plc.end (); ++t) { + EXPECT_LE (t->area (), param.max_area); + EXPECT_GE (t->b (), param.min_b); + } + + EXPECT_GT (plc.num_polygons (), size_t (1250)); + EXPECT_LT (plc.num_polygons (), size_t (1300)); +} + +TEST(triangulate_problematic) +{ + db::DPoint contour[] = { + db::DPoint (129145.00000, -30060.80000), + db::DPoint (129145.00000, -28769.50000), + db::DPoint (129159.50000, -28754.90000), // this is a very short edge <-- from here. + db::DPoint (129159.60000, -28754.80000), // <-- to here. + db::DPoint (129159.50000, -28754.70000), + db::DPoint (129366.32200, -28547.90000), + db::DPoint (130958.54600, -26955.84600), + db::DPoint (131046.25000, -27043.55000), + db::DPoint (130152.15000, -27937.65000), + db::DPoint (130152.15000, -30060.80000) + }; + + db::DPolygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + + db::plc::TriangulationParameters param; + param.min_b = 1.0; + param.max_area = 100000.0; + param.min_length = 0.002; + + db::plc::Graph plc; + TestableTriangulation tri (&plc); + tri.triangulate (poly, param); + + EXPECT_EQ (tri.check (false), true); + + // for debugging: + // tri.dump ("debug.gds"); + + for (auto t = plc.begin (); t != plc.end (); ++t) { + EXPECT_LE (t->area (), param.max_area); + EXPECT_GE (t->b (), param.min_b); + } + + EXPECT_GT (plc.num_polygons (), size_t (540)); + EXPECT_LT (plc.num_polygons (), size_t (560)); +} + +TEST(triangulate_thin) +{ + db::DPoint contour[] = { + db::DPoint (18790, 58090), + db::DPoint (18790, 58940), + db::DPoint (29290, 58940), + db::DPoint (29290, 58090) + }; + + db::DPoint hole[] = { + db::DPoint (18791, 58091), + db::DPoint (29289, 58091), + db::DPoint (29289, 58939), + db::DPoint (18791, 58939) + }; + + db::DPolygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + poly.insert_hole (hole + 0, hole + sizeof (hole) / sizeof (hole[0])); + + double dbu = 0.001; + + db::plc::TriangulationParameters param; + param.min_b = 0.5; + param.max_area = 0.0; + param.min_length = 2 * dbu; + + db::plc::Graph plc; + TestableTriangulation tri (&plc); + db::DCplxTrans trans = db::DCplxTrans (dbu) * db::DCplxTrans (db::DTrans (db::DPoint () - poly.box ().center ())); + tri.triangulate (trans * poly, param); + + EXPECT_EQ (tri.check (false), true); + + // for debugging: + // tri.dump ("debug.gds"); + + for (auto t = plc.begin (); t != plc.end (); ++t) { + EXPECT_GE (t->b (), param.min_b); + } + + EXPECT_GT (plc.num_polygons (), size_t (13000)); + EXPECT_LT (plc.num_polygons (), size_t (13200)); +} + +TEST(triangulate_issue1996) +{ + db::DPoint contour[] = { + db::DPoint (-8000, -8075), + db::DPoint (-8000, 8075), + db::DPoint (18000, 8075), + db::DPoint (18000, -8075) + }; + + db::DPolygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + + double dbu = 0.001; + + db::plc::TriangulationParameters param; + param.min_b = 0.5; + param.max_area = 5000.0 * dbu * dbu; + + db::plc::Graph plc; + TestableTriangulation tri (&plc); + db::DCplxTrans trans = db::DCplxTrans (dbu) * db::DCplxTrans (db::DTrans (db::DPoint () - poly.box ().center ())); + tri.triangulate (trans * poly, param); + + EXPECT_EQ (tri.check (false), true); + + // for debugging: + // tri.dump ("debug.gds"); + + for (auto t = plc.begin (); t != plc.end (); ++t) { + EXPECT_LE (t->area (), param.max_area); + EXPECT_GE (t->b (), param.min_b); + } + + EXPECT_GT (plc.num_polygons (), size_t (128000)); + EXPECT_LT (plc.num_polygons (), size_t (132000)); +} + +TEST(triangulate_with_vertexes) +{ + db::Point contour[] = { + db::Point (0, 0), + db::Point (0, 100), + db::Point (1000, 100), + db::Point (1000, 0) + }; + + db::Polygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + + double dbu = 0.001; + + db::plc::TriangulationParameters param; + param.min_b = 0.0; + param.max_area = 0.0; + + std::vector vertexes; + + db::plc::Graph plc; + TestableTriangulation tri (&plc); + db::CplxTrans trans = db::DCplxTrans (dbu) * db::CplxTrans (db::Trans (db::Point () - poly.box ().center ())); + tri.triangulate (poly, param, trans); + + EXPECT_EQ (plc.to_string (), "((-0.5, -0.05), (-0.5, 0.05), (0.5, 0.05)), ((0.5, -0.05), (-0.5, -0.05), (0.5, 0.05))"); + + vertexes.clear (); + + // outside vertexes are ignored, but lead to a different triangulation + vertexes.push_back (db::Point (50, 150)); + tri.triangulate (poly, vertexes, param, trans); + + EXPECT_EQ (plc.to_string (), "((-0.5, -0.05), (-0.133333333333, 0.05), (0.5, -0.05)), ((0.5, 0.05), (0.5, -0.05), (-0.133333333333, 0.05)), ((-0.133333333333, 0.05), (-0.5, -0.05), (-0.5, 0.05))"); + + for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { + auto *vp = tri.find_vertex_for_point (trans * *v); + EXPECT_EQ (vp, 0); + } + + vertexes.clear (); + vertexes.push_back (db::Point (50, 50)); + tri.triangulate (poly, vertexes, param, trans); + + EXPECT_EQ (plc.to_string (), "((-0.45, 0), (-0.5, -0.05), (-0.5, 0.05)), ((0.5, 0.05), (-0.45, 0), (-0.5, 0.05)), ((-0.45, 0), (0.5, -0.05), (-0.5, -0.05)), ((-0.45, 0), (0.5, 0.05), (0.5, -0.05))"); + + for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { + auto *vp = tri.find_vertex_for_point (trans * *v); + if (! vp) { + tl::warn << "Vertex not present in output: " << v->to_string (); + EXPECT_EQ (1, 0); + } + } + + // aggressive triangulation + param.min_b = 1.0; + param.max_area = 20 * 20 * dbu * dbu; + + tri.triangulate (poly, vertexes, param, trans); + + EXPECT_GT (plc.num_polygons (), size_t (380)); + EXPECT_LT (plc.num_polygons (), size_t (400)); + + for (auto t = plc.begin (); t != plc.end (); ++t) { + EXPECT_LE (t->area (), param.max_area); + EXPECT_GE (t->b (), param.min_b); + } + + for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { + auto *vp = tri.find_vertex_for_point (trans * *v); + if (! vp) { + tl::warn << "Vertex not present in output: " << v->to_string (); + EXPECT_EQ (1, 0); + } + } +} diff --git a/src/db/unit_tests/dbTrianglesTests.cc b/src/db/unit_tests/dbTrianglesTests.cc index 138248694..cd5779f2d 100644 --- a/src/db/unit_tests/dbTrianglesTests.cc +++ b/src/db/unit_tests/dbTrianglesTests.cc @@ -461,7 +461,7 @@ TEST(ensure_edge) EXPECT_EQ (tl::to_string (area_in), "0.25"); } -bool safe_inside (const db::DBox &b1, const db::DBox &b2) +static bool safe_inside (const db::DBox &b1, const db::DBox &b2) { typedef db::coord_traits ct; diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 047e46ea6..ad9f9e382 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -12,7 +12,8 @@ SOURCES = \ dbFillToolTests.cc \ dbLogTests.cc \ dbObjectWithPropertiesTests.cc \ - dbPolygonGraphTests.cc \ + dbPLCGraphTest.cc \ + dbPLCTriangulationTests.cc \ dbPolygonNeighborhoodTests.cc \ dbPropertiesFilterTests.cc \ dbQuadTreeTests.cc \ From 802cf995217976b4033382fd177059f60b5a2518 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 13 Apr 2025 22:58:37 +0200 Subject: [PATCH 137/392] Porting dbTriangleTests --- src/db/db/dbPLC.h | 29 +- .../{dbPLCGraphTest.cc => dbPLCGraphTests.cc} | 0 src/db/unit_tests/dbPLCTests.cc | 582 ++++++++++++++++++ src/db/unit_tests/dbTriangleTests.cc | 130 ++-- src/db/unit_tests/unit_tests.pro | 3 +- 5 files changed, 665 insertions(+), 79 deletions(-) rename src/db/unit_tests/{dbPLCGraphTest.cc => dbPLCGraphTests.cc} (100%) create mode 100644 src/db/unit_tests/dbPLCTests.cc diff --git a/src/db/db/dbPLC.h b/src/db/db/dbPLC.h index 8d410d0b8..9d7b35e43 100644 --- a/src/db/db/dbPLC.h +++ b/src/db/db/dbPLC.h @@ -157,15 +157,16 @@ public: return in_circle (*this, center, radius); } -private: - friend class Edge; - friend class Graph; - +protected: Vertex (Graph *graph); Vertex (Graph *graph, const DPoint &p); Vertex (Graph *graph, const Vertex &v); Vertex (Graph *graph, db::DCoord x, db::DCoord y); +private: + friend class Edge; + friend class Graph; + void remove_edge (const edges_iterator_non_const &ec) { mp_edges.erase (ec); @@ -491,21 +492,20 @@ protected: void unlink (); void link (); -private: - friend class Polygon; - friend class Graph; - friend class Triangulation; - void set_level (size_t l) { m_level = l; } size_t level () const { return m_level; } void set_id (size_t id) { m_id = id; } - void set_is_segment (bool is_seg) { m_is_segment = is_seg; } Edge (Graph *graph); Edge (Graph *graph, Vertex *v1, Vertex *v2); +private: + friend class Polygon; + friend class Graph; + friend class Triangulation; + Graph *mp_graph; Vertex *mp_v1, *mp_v2; Polygon *mp_left, *mp_right; @@ -538,9 +538,6 @@ class DB_PUBLIC Polygon : public tl::list_node, public tl::Object { public: - Polygon (Graph *graph); - Polygon (Graph *graph, Edge *e1, Edge *e2, Edge *e3); - template Polygon (Graph *graph, Iter from, Iter to) : mp_graph (graph), mp_e (from, to) @@ -695,7 +692,13 @@ public: */ unsigned int num_segments () const; +protected: + Polygon (Graph *graph); + Polygon (Graph *graph, Edge *e1, Edge *e2, Edge *e3); + private: + friend class Graph; + Graph *mp_graph; bool m_is_outside; std::vector mp_e; diff --git a/src/db/unit_tests/dbPLCGraphTest.cc b/src/db/unit_tests/dbPLCGraphTests.cc similarity index 100% rename from src/db/unit_tests/dbPLCGraphTest.cc rename to src/db/unit_tests/dbPLCGraphTests.cc diff --git a/src/db/unit_tests/dbPLCTests.cc b/src/db/unit_tests/dbPLCTests.cc new file mode 100644 index 000000000..f93c5d373 --- /dev/null +++ b/src/db/unit_tests/dbPLCTests.cc @@ -0,0 +1,582 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbPLC.h" +#include "tlUnitTest.h" + +#include +#include + +class TestableEdge + : public db::plc::Edge +{ +public: + using db::plc::Edge::Edge; + using db::plc::Edge::link; + using db::plc::Edge::unlink; + using db::plc::Edge::set_is_segment; + using db::plc::Edge::set_level; + using db::plc::Edge::level; + + TestableEdge (db::plc::Vertex *v1, db::plc::Vertex *v2) + : db::plc::Edge (0, v1, v2) + { } +}; + +class TestableVertex + : public db::plc::Vertex +{ +public: + TestableVertex () + : db::plc::Vertex (0) + { } + + TestableVertex (double x, double y) + : db::plc::Vertex (0, x, y) + { } + + TestableVertex (const db::DPoint &pt) + : db::plc::Vertex (0, pt) + { } +}; + +class TestablePolygon + : public db::plc::Polygon +{ +public: + TestablePolygon () + : db::plc::Polygon (0) + { } + + TestablePolygon (db::plc::Edge *e1, db::plc::Edge *e2, db::plc::Edge *e3) + : db::plc::Polygon (0, e1, e2, e3) + { } +}; + +// Tests for Vertex class + +TEST(Vertex_basic) +{ + TestableVertex v; + + v.set_x (1.5); + v.set_y (0.5); + EXPECT_EQ (v.to_string (), "(1.5, 0.5)"); + EXPECT_EQ (v.x (), 1.5); + EXPECT_EQ (v.y (), 0.5); + + v = TestableVertex (db::DPoint (2, 3)); + EXPECT_EQ (v.to_string (), "(2, 3)"); +} + +static std::string edges_from_vertex (const TestableVertex &v) +{ + std::string res; + for (auto e = v.begin_edges (); e != v.end_edges (); ++e) { + if (! res.empty ()) { + res += ", "; + } + res += (*e)->to_string (); + } + return res; +} + +static std::string triangles_from_vertex (const TestableVertex &v) +{ + auto tri = v.polygons (); + std::string res; + for (auto t = tri.begin (); t != tri.end (); ++t) { + if (! res.empty ()) { + res += ", "; + } + res += (*t)->to_string (); + } + return res; +} + +TEST(Vertex_edge_registration) +{ + TestableVertex v1 (0, 0); + TestableVertex v2 (1, 2); + TestableVertex v3 (2, 1); + + std::unique_ptr e1 (new TestableEdge (&v1, &v2)); + e1->link (); + EXPECT_EQ (edges_from_vertex (v1), "((0, 0), (1, 2))"); + EXPECT_EQ (edges_from_vertex (v2), "((0, 0), (1, 2))"); + EXPECT_EQ (edges_from_vertex (v3), ""); + + std::unique_ptr e2 (new TestableEdge (&v2, &v3)); + e2->link (); + EXPECT_EQ (edges_from_vertex (v1), "((0, 0), (1, 2))"); + EXPECT_EQ (edges_from_vertex (v2), "((0, 0), (1, 2)), ((1, 2), (2, 1))"); + EXPECT_EQ (edges_from_vertex (v3), "((1, 2), (2, 1))"); + + e2->unlink (); + e2.reset (0); + EXPECT_EQ (edges_from_vertex (v1), "((0, 0), (1, 2))"); + EXPECT_EQ (edges_from_vertex (v2), "((0, 0), (1, 2))"); + EXPECT_EQ (edges_from_vertex (v3), ""); + + e1->unlink (); + e1.reset (0); + EXPECT_EQ (edges_from_vertex (v1), ""); + EXPECT_EQ (edges_from_vertex (v2), ""); + EXPECT_EQ (edges_from_vertex (v3), ""); +} + +TEST(Vertex_triangles) +{ + TestableVertex v1 (0, 0); + TestableVertex v2 (1, 2); + TestableVertex v3 (2, 1); + TestableVertex v4 (-1, 2); + EXPECT_EQ (triangles_from_vertex (v1), ""); + + std::unique_ptr e1 (new TestableEdge (&v1, &v2)); + e1->link (); + std::unique_ptr e2 (new TestableEdge (&v2, &v3)); + e2->link (); + std::unique_ptr e3 (new TestableEdge (&v3, &v1)); + e3->link (); + + std::unique_ptr tri (new TestablePolygon (e1.get (), e2.get (), e3.get ())); + EXPECT_EQ (triangles_from_vertex (v1), "((0, 0), (1, 2), (2, 1))"); + EXPECT_EQ (triangles_from_vertex (v2), "((0, 0), (1, 2), (2, 1))"); + EXPECT_EQ (triangles_from_vertex (v3), "((0, 0), (1, 2), (2, 1))"); + + std::unique_ptr e4 (new TestableEdge (&v1, &v4)); + e4->link (); + std::unique_ptr e5 (new TestableEdge (&v2, &v4)); + e5->link (); + std::unique_ptr tri2 (new TestablePolygon (e1.get (), e4.get (), e5.get ())); + EXPECT_EQ (triangles_from_vertex (v1), "((0, 0), (-1, 2), (1, 2)), ((0, 0), (1, 2), (2, 1))"); + EXPECT_EQ (triangles_from_vertex (v2), "((0, 0), (-1, 2), (1, 2)), ((0, 0), (1, 2), (2, 1))"); + EXPECT_EQ (triangles_from_vertex (v3), "((0, 0), (1, 2), (2, 1))"); + EXPECT_EQ (triangles_from_vertex (v4), "((0, 0), (-1, 2), (1, 2))"); + + tri->unlink (); + EXPECT_EQ (triangles_from_vertex (v1), "((0, 0), (-1, 2), (1, 2))"); + + tri2->unlink (); + EXPECT_EQ (triangles_from_vertex (v1), ""); +} + +// Tests for Triangle class + +TEST(Triangle_basic) +{ + TestableVertex v1; + TestableVertex v2 (1, 2); + TestableVertex v3 (2, 1); + + TestableEdge s1 (&v1, &v2); + TestableEdge s2 (&v2, &v3); + TestableEdge s3 (&v3, &v1); + + EXPECT_EQ (s1.v1 () == &v1, true); + EXPECT_EQ (s2.v2 () == &v3, true); + + TestablePolygon tri (&s1, &s2, &s3); + EXPECT_EQ (tri.to_string (), "((0, 0), (1, 2), (2, 1))"); + EXPECT_EQ (tri.edge (-1) == &s3, true); + EXPECT_EQ (tri.edge (0) == &s1, true); + EXPECT_EQ (tri.edge (1) == &s2, true); + EXPECT_EQ (tri.edge (3) == &s1, true); + + // ordering + TestableEdge s11 (&v1, &v2); + TestableEdge s12 (&v3, &v2); + TestableEdge s13 (&v1, &v3); + + TestablePolygon tri2 (&s11, &s12, &s13); + EXPECT_EQ (tri2.to_string (), "((0, 0), (1, 2), (2, 1))"); + + // triangle registration + EXPECT_EQ (s11.right () == &tri2, true); + EXPECT_EQ (s11.left () == 0, true); + EXPECT_EQ (s12.left () == &tri2, true); + EXPECT_EQ (s12.right () == 0, true); + EXPECT_EQ (s13.left () == &tri2, true); + EXPECT_EQ (s13.right () == 0, true); + + EXPECT_EQ (s13.to_string (), "((0, 0), (2, 1))"); + s13.reverse (); + EXPECT_EQ (s13.to_string (), "((2, 1), (0, 0))"); + EXPECT_EQ (s13.right () == &tri2, true); + EXPECT_EQ (s13.left () == 0, true); + + // flags + EXPECT_EQ (tri.is_outside (), false); + tri.set_outside (true); + EXPECT_EQ (tri.is_outside (), true); +} + +TEST(Triangle_find_segment_with) +{ + TestableVertex v1; + TestableVertex v2 (1, 2); + TestableVertex v3 (2, 1); + + TestableEdge s1 (&v1, &v2); + TestableEdge s2 (&v2, &v3); + TestableEdge s3 (&v3, &v1); + + TestablePolygon tri (&s1, &s2, &s3); + + EXPECT_EQ (tri.find_edge_with (&v1, &v2)->to_string (), "((0, 0), (1, 2))"); + EXPECT_EQ (tri.find_edge_with (&v2, &v1)->to_string (), "((0, 0), (1, 2))"); +} + +TEST(Triangle_ext_vertex) +{ + TestableVertex v1; + TestableVertex v2 (1, 2); + TestableVertex v3 (2, 1); + + TestableEdge s1 (&v1, &v2); + TestableEdge s2 (&v2, &v3); + TestableEdge s3 (&v3, &v1); + + TestablePolygon tri (&s1, &s2, &s3); + + EXPECT_EQ (tri.opposite (&s1)->to_string (), "(2, 1)"); + EXPECT_EQ (tri.opposite (&s3)->to_string (), "(1, 2)"); +} + +TEST(Triangle_opposite_vertex) +{ + TestableVertex v1; + TestableVertex v2 (1, 2); + TestableVertex v3 (2, 1); + + TestableEdge s1 (&v1, &v2); + TestableEdge s2 (&v2, &v3); + TestableEdge s3 (&v3, &v1); + + TestablePolygon tri (&s1, &s2, &s3); + + EXPECT_EQ (tri.opposite (&s1)->to_string (), "(2, 1)"); + EXPECT_EQ (tri.opposite (&s3)->to_string (), "(1, 2)"); +} + +TEST(Triangle_opposite_edge) +{ + TestableVertex v1; + TestableVertex v2 (1, 2); + TestableVertex v3 (2, 1); + + TestableEdge s1 (&v1, &v2); + TestableEdge s2 (&v2, &v3); + TestableEdge s3 (&v3, &v1); + + TestablePolygon tri (&s1, &s2, &s3); + + EXPECT_EQ (tri.opposite (&v1)->to_string (), "((1, 2), (2, 1))"); + EXPECT_EQ (tri.opposite (&v3)->to_string (), "((0, 0), (1, 2))"); +} + +TEST(Triangle_contains) +{ + TestableVertex v1; + TestableVertex v2 (1, 2); + TestableVertex v3 (2, 1); + + TestableEdge s1 (&v1, &v2); + TestableEdge s2 (&v2, &v3); + TestableEdge s3 (&v3, &v1); + + { + TestablePolygon tri (&s1, &s2, &s3); + EXPECT_EQ (tri.contains (db::DPoint (0, 0)), 0); + EXPECT_EQ (tri.contains (db::DPoint (-1, -2)), -1); + EXPECT_EQ (tri.contains (db::DPoint (0.5, 1)), 0); + EXPECT_EQ (tri.contains (db::DPoint (0.5, 2)), -1); + EXPECT_EQ (tri.contains (db::DPoint (2.5, 1)), -1); + EXPECT_EQ (tri.contains (db::DPoint (1, -1)), -1); + EXPECT_EQ (tri.contains (db::DPoint (1, 1)), 1); + } + + s1.reverse (); + s2.reverse (); + s3.reverse (); + + { + TestablePolygon tri2 (&s3, &s2, &s1); + EXPECT_EQ (tri2.contains(db::DPoint(0, 0)), 0); + EXPECT_EQ (tri2.contains(db::DPoint(0.5, 1)), 0); + EXPECT_EQ (tri2.contains(db::DPoint(0.5, 2)), -1); + EXPECT_EQ (tri2.contains(db::DPoint(2.5, 1)), -1); + EXPECT_EQ (tri2.contains(db::DPoint(1, -1)), -1); + EXPECT_EQ (tri2.contains(db::DPoint(1, 1)), 1); + } +} + +TEST(Triangle_contains_small) +{ + TestableVertex v1; + TestableVertex v2 (0.001, 0.002); + TestableVertex v3 (0.002, 0.001); + + TestableEdge s1 (&v1, &v2); + TestableEdge s2 (&v2, &v3); + TestableEdge s3 (&v3, &v1); + + { + TestablePolygon tri (&s1, &s2, &s3); + EXPECT_EQ (tri.contains (db::DPoint (0, 0)), 0); + EXPECT_EQ (tri.contains (db::DPoint (-0.001, -0.002)), -1); + EXPECT_EQ (tri.contains (db::DPoint (0.0005, 0.001)), 0); + EXPECT_EQ (tri.contains (db::DPoint (0.0005, 0.002)), -1); + EXPECT_EQ (tri.contains (db::DPoint (0.0025, 0.001)), -1); + EXPECT_EQ (tri.contains (db::DPoint (0.001, -0.001)), -1); + EXPECT_EQ (tri.contains (db::DPoint (0.001, 0.001)), 1); + } + + s1.reverse (); + s2.reverse (); + s3.reverse (); + + { + TestablePolygon tri2 (&s3, &s2, &s1); + EXPECT_EQ (tri2.contains(db::DPoint(0, 0)), 0); + EXPECT_EQ (tri2.contains(db::DPoint(0.0005, 0.001)), 0); + EXPECT_EQ (tri2.contains(db::DPoint(0.0005, 0.002)), -1); + EXPECT_EQ (tri2.contains(db::DPoint(0.0025, 0.001)), -1); + EXPECT_EQ (tri2.contains(db::DPoint(0.001, -0.001)), -1); + EXPECT_EQ (tri2.contains(db::DPoint(0.001, 0.001)), 1); + } +} + +TEST(Triangle_circumcircle) +{ + TestableVertex v1; + TestableVertex v2 (1, 2); + TestableVertex v3 (2, 1); + + TestableEdge s1 (&v1, &v2); + TestableEdge s2 (&v2, &v3); + TestableEdge s3 (&v3, &v1); + + TestablePolygon tri (&s1, &s2, &s3); + + auto cc = tri.circumcircle (); + auto center = cc.first; + auto radius = cc.second; + + EXPECT_EQ (tl::to_string (center), "0.833333333333,0.833333333333"); + EXPECT_EQ (tl::to_string (radius), "1.17851130198"); + + EXPECT_EQ (TestableVertex::in_circle (center, center, radius), 1); + EXPECT_EQ (TestableVertex::in_circle (db::DPoint (-1, -1), center, radius), -1); + EXPECT_EQ (v1.in_circle (center, radius), 0); + EXPECT_EQ (v2.in_circle (center, radius), 0); + EXPECT_EQ (v3.in_circle (center, radius), 0); +} + +// Tests for TriangleEdge class + +TEST(TriangleEdge_basic) +{ + TestableVertex v1; + TestableVertex v2 (1, 0.5); + + TestableEdge edge (&v1, &v2); + EXPECT_EQ (edge.to_string (), "((0, 0), (1, 0.5))"); + + EXPECT_EQ (edge.is_segment (), false); + edge.set_is_segment (true); + EXPECT_EQ (edge.is_segment (), true); + + EXPECT_EQ (edge.level (), size_t (0)); + edge.set_level (42); + EXPECT_EQ (edge.level (), size_t (42)); + + EXPECT_EQ (edge.other (&v1) == &v2, true); + EXPECT_EQ (edge.other (&v2) == &v1, true); +} + +TEST(TriangleEdge_triangles) +{ + TestableVertex v1 (0, 0); + TestableVertex v2 (1, 2); + TestableVertex v3 (2, 1); + TestableVertex v4 (-1, 2); + + std::unique_ptr e1 (new TestableEdge (&v1, &v2)); + std::unique_ptr e2 (new TestableEdge (&v2, &v3)); + std::unique_ptr e3 (new TestableEdge (&v3, &v1)); + + std::unique_ptr tri (new TestablePolygon (e1.get (), e2.get (), e3.get ())); + + std::unique_ptr e4 (new TestableEdge (&v1, &v4)); + std::unique_ptr e5 (new TestableEdge (&v2, &v4)); + std::unique_ptr tri2 (new TestablePolygon (e1.get (), e4.get (), e5.get ())); + + EXPECT_EQ (e1->is_outside (), false); + EXPECT_EQ (e2->is_outside (), true); + EXPECT_EQ (e4->is_outside (), true); + + EXPECT_EQ (e1->is_for_outside_triangles (), false); + tri->set_outside (true); + EXPECT_EQ (e1->is_for_outside_triangles (), true); + + EXPECT_EQ (e1->has_polygon (tri.get ()), true); + EXPECT_EQ (e1->has_polygon (tri2.get ()), true); + EXPECT_EQ (e4->has_polygon (tri.get ()), false); + EXPECT_EQ (e4->has_polygon (tri2.get ()), true); + + EXPECT_EQ (e1->other (tri.get ()) == tri2.get (), true); + EXPECT_EQ (e1->other (tri2.get ()) == tri.get (), true); + + EXPECT_EQ (e1->common_vertex (e2.get ()) == &v2, true); + EXPECT_EQ (e2->common_vertex (e4.get ()) == 0, true); + + tri->unlink (); + EXPECT_EQ (e1->has_polygon (tri.get ()), false); + EXPECT_EQ (e1->has_polygon (tri2.get ()), true); +} + +TEST(TriangleEdge_side_of) +{ + TestableVertex v1; + TestableVertex v2 (1, 0.5); + + TestableEdge edge (&v1, &v2); + EXPECT_EQ (edge.to_string (), "((0, 0), (1, 0.5))"); + + EXPECT_EQ (edge.side_of (TestableVertex (0, 0)), 0) + EXPECT_EQ (edge.side_of (TestableVertex (0.5, 0.25)), 0) + EXPECT_EQ (edge.side_of (TestableVertex (0, 1)), -1) + EXPECT_EQ (edge.side_of (TestableVertex (0, -1)), 1) + EXPECT_EQ (edge.side_of (TestableVertex (0.5, 0.5)), -1) + EXPECT_EQ (edge.side_of (TestableVertex (0.5, 0)), 1) + + TestableVertex v3 (1, 0); + TestableVertex v4 (0, 1); + TestableEdge edge2 (&v3, &v4); + + EXPECT_EQ (edge2.side_of (TestableVertex(0.2, 0.2)), -1); +} + +namespace { + class VertexHeap + { + public: + TestableVertex *make_vertex (double x, double y) + { + m_heap.push_back (TestableVertex (x, y)); + return &m_heap.back (); + } + private: + std::list m_heap; + }; +} + +TEST(TriangleEdge_crosses) +{ + VertexHeap heap; + + TestableEdge s1 (heap.make_vertex (0, 0), heap.make_vertex (1, 0.5)); + EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, -0.5), heap.make_vertex(1, -0.5))), false); + EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0), heap.make_vertex(1, 0))), false); // only cuts + EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0.5), heap.make_vertex(1, 0.5))), false); + EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0.5), heap.make_vertex(2, 0.5))), false); + EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0.25), heap.make_vertex(2, 0.25))), true); + EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0.5), heap.make_vertex(-0.1, 0.5))), false); + EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0.6), heap.make_vertex(0, 0.6))), false); + EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 1), heap.make_vertex(1, 1))), false); + + EXPECT_EQ (s1.crosses_including (TestableEdge (heap.make_vertex (-1, -0.5), heap.make_vertex(1, -0.5))), false); + EXPECT_EQ (s1.crosses_including (TestableEdge (heap.make_vertex (-1, 0), heap.make_vertex(1, 0))), true); // only cuts + EXPECT_EQ (s1.crosses_including (TestableEdge (heap.make_vertex (-1, 0.25), heap.make_vertex(2, 0.25))), true); +} + +TEST(TriangleEdge_point_on) +{ + VertexHeap heap; + + TestableEdge s1 (heap.make_vertex (0, 0), heap.make_vertex (1, 0.5)); + EXPECT_EQ (s1.point_on (db::DPoint (0, 0)), false); // endpoints are not "on" + EXPECT_EQ (s1.point_on (db::DPoint (0, -0.5)), false); + EXPECT_EQ (s1.point_on (db::DPoint (0.5, 0)), false); + EXPECT_EQ (s1.point_on (db::DPoint (0.5, 0.25)), true); + EXPECT_EQ (s1.point_on (db::DPoint (1, 0.5)), false); // endpoints are not "on" + EXPECT_EQ (s1.point_on (db::DPoint (1, 1)), false); + EXPECT_EQ (s1.point_on (db::DPoint (2, 1)), false); +} + +TEST(TriangleEdge_intersection_point) +{ + VertexHeap heap; + + TestableEdge s1 (heap.make_vertex (0, 0), heap.make_vertex (1, 0.5)); + EXPECT_EQ (s1.intersection_point (TestableEdge (heap.make_vertex (-1, 0.25), heap.make_vertex (2, 0.25))).to_string (), "0.5,0.25"); +} + +TEST(TriangleEdge_can_flip) +{ + TestableVertex v1 (2, -1); + TestableVertex v2 (0, 0); + TestableVertex v3 (1, 0); + TestableVertex v4 (0.5, 1); + TestableEdge s1 (&v1, &v2); + TestableEdge s2 (&v1, &v3); + TestableEdge s3 (&v2, &v3); + TestableEdge s4 (&v2, &v4); + TestableEdge s5 (&v3, &v4); + TestablePolygon t1 (&s1, &s2, &s3); + TestablePolygon t2 (&s3, &s4, &s5); + EXPECT_EQ (s3.left () == &t2, true); + EXPECT_EQ (s3.right () == &t1, true); + EXPECT_EQ (s3.can_flip(), false); + v1.set_x (0.5); + EXPECT_EQ (s3.can_flip(), true); + v1.set_x (-0.25); + EXPECT_EQ (s3.can_flip(), true); + v1.set_x (-0.5); + EXPECT_EQ (s3.can_flip(), false); + v1.set_x (-1.0); + EXPECT_EQ (s3.can_flip(), false); +} + +TEST(TriangleEdge_distance) +{ + TestableVertex v1 (0, 0); + TestableVertex v2 (1, 0); + + TestableEdge seg (&v1, &v2); + EXPECT_EQ (seg.distance (db::DPoint (0, 0)), 0); + EXPECT_EQ (seg.distance (db::DPoint (0, 1)), 1); + EXPECT_EQ (seg.distance (db::DPoint (1, 2)), 2); + EXPECT_EQ (seg.distance (db::DPoint (1, -1)), 1); + EXPECT_EQ (seg.distance (db::DPoint (2, 0)), 1); + EXPECT_EQ (seg.distance (db::DPoint (-2, 0)), 2); + seg.reverse (); + EXPECT_EQ (seg.distance (db::DPoint (0, 0)), 0); + EXPECT_EQ (seg.distance (db::DPoint (0, 1)), 1); + EXPECT_EQ (seg.distance (db::DPoint (1, 2)), 2); + EXPECT_EQ (seg.distance (db::DPoint (1, -1)), 1); + EXPECT_EQ (seg.distance (db::DPoint (2, 0)), 1); + EXPECT_EQ (seg.distance (db::DPoint (-2, 0)), 2); +} diff --git a/src/db/unit_tests/dbTriangleTests.cc b/src/db/unit_tests/dbTriangleTests.cc index 4d0eb9e78..b460f96ec 100644 --- a/src/db/unit_tests/dbTriangleTests.cc +++ b/src/db/unit_tests/dbTriangleTests.cc @@ -27,7 +27,7 @@ #include #include -class TestableTriangleEdge +class TestableEdge : public db::TriangleEdge { public: @@ -35,7 +35,7 @@ public: using db::TriangleEdge::link; using db::TriangleEdge::unlink; - TestableTriangleEdge (db::Vertex *v1, db::Vertex *v2) + TestableEdge (db::Vertex *v1, db::Vertex *v2) : db::TriangleEdge (v1, v2) { } }; @@ -87,13 +87,13 @@ TEST(Vertex_edge_registration) db::Vertex v2 (1, 2); db::Vertex v3 (2, 1); - std::unique_ptr e1 (new TestableTriangleEdge (&v1, &v2)); + std::unique_ptr e1 (new TestableEdge (&v1, &v2)); e1->link (); EXPECT_EQ (edges_from_vertex (v1), "((0, 0), (1, 2))"); EXPECT_EQ (edges_from_vertex (v2), "((0, 0), (1, 2))"); EXPECT_EQ (edges_from_vertex (v3), ""); - std::unique_ptr e2 (new TestableTriangleEdge (&v2, &v3)); + std::unique_ptr e2 (new TestableEdge (&v2, &v3)); e2->link (); EXPECT_EQ (edges_from_vertex (v1), "((0, 0), (1, 2))"); EXPECT_EQ (edges_from_vertex (v2), "((0, 0), (1, 2)), ((1, 2), (2, 1))"); @@ -120,11 +120,11 @@ TEST(Vertex_triangles) db::Vertex v4 (-1, 2); EXPECT_EQ (triangles_from_vertex (v1), ""); - std::unique_ptr e1 (new TestableTriangleEdge (&v1, &v2)); + std::unique_ptr e1 (new TestableEdge (&v1, &v2)); e1->link (); - std::unique_ptr e2 (new TestableTriangleEdge (&v2, &v3)); + std::unique_ptr e2 (new TestableEdge (&v2, &v3)); e2->link (); - std::unique_ptr e3 (new TestableTriangleEdge (&v3, &v1)); + std::unique_ptr e3 (new TestableEdge (&v3, &v1)); e3->link (); std::unique_ptr tri (new db::Triangle (e1.get (), e2.get (), e3.get ())); @@ -132,9 +132,9 @@ TEST(Vertex_triangles) EXPECT_EQ (triangles_from_vertex (v2), "((0, 0), (1, 2), (2, 1))"); EXPECT_EQ (triangles_from_vertex (v3), "((0, 0), (1, 2), (2, 1))"); - std::unique_ptr e4 (new TestableTriangleEdge (&v1, &v4)); + std::unique_ptr e4 (new TestableEdge (&v1, &v4)); e4->link (); - std::unique_ptr e5 (new TestableTriangleEdge (&v2, &v4)); + std::unique_ptr e5 (new TestableEdge (&v2, &v4)); e5->link (); std::unique_ptr tri2 (new db::Triangle (e1.get (), e4.get (), e5.get ())); EXPECT_EQ (triangles_from_vertex (v1), "((0, 0), (-1, 2), (1, 2)), ((0, 0), (1, 2), (2, 1))"); @@ -157,9 +157,9 @@ TEST(Triangle_basic) db::Vertex v2 (1, 2); db::Vertex v3 (2, 1); - TestableTriangleEdge s1 (&v1, &v2); - TestableTriangleEdge s2 (&v2, &v3); - TestableTriangleEdge s3 (&v3, &v1); + TestableEdge s1 (&v1, &v2); + TestableEdge s2 (&v2, &v3); + TestableEdge s3 (&v3, &v1); EXPECT_EQ (s1.v1 () == &v1, true); EXPECT_EQ (s2.v2 () == &v3, true); @@ -172,9 +172,9 @@ TEST(Triangle_basic) EXPECT_EQ (tri.edge (3) == &s1, true); // ordering - TestableTriangleEdge s11 (&v1, &v2); - TestableTriangleEdge s12 (&v3, &v2); - TestableTriangleEdge s13 (&v1, &v3); + TestableEdge s11 (&v1, &v2); + TestableEdge s12 (&v3, &v2); + TestableEdge s13 (&v1, &v3); db::Triangle tri2 (&s11, &s12, &s13); EXPECT_EQ (tri2.to_string (), "((0, 0), (1, 2), (2, 1))"); @@ -205,9 +205,9 @@ TEST(Triangle_find_segment_with) db::Vertex v2 (1, 2); db::Vertex v3 (2, 1); - TestableTriangleEdge s1 (&v1, &v2); - TestableTriangleEdge s2 (&v2, &v3); - TestableTriangleEdge s3 (&v3, &v1); + TestableEdge s1 (&v1, &v2); + TestableEdge s2 (&v2, &v3); + TestableEdge s3 (&v3, &v1); db::Triangle tri (&s1, &s2, &s3); @@ -221,9 +221,9 @@ TEST(Triangle_ext_vertex) db::Vertex v2 (1, 2); db::Vertex v3 (2, 1); - TestableTriangleEdge s1 (&v1, &v2); - TestableTriangleEdge s2 (&v2, &v3); - TestableTriangleEdge s3 (&v3, &v1); + TestableEdge s1 (&v1, &v2); + TestableEdge s2 (&v2, &v3); + TestableEdge s3 (&v3, &v1); db::Triangle tri (&s1, &s2, &s3); @@ -237,9 +237,9 @@ TEST(Triangle_opposite_vertex) db::Vertex v2 (1, 2); db::Vertex v3 (2, 1); - TestableTriangleEdge s1 (&v1, &v2); - TestableTriangleEdge s2 (&v2, &v3); - TestableTriangleEdge s3 (&v3, &v1); + TestableEdge s1 (&v1, &v2); + TestableEdge s2 (&v2, &v3); + TestableEdge s3 (&v3, &v1); db::Triangle tri (&s1, &s2, &s3); @@ -253,9 +253,9 @@ TEST(Triangle_opposite_edge) db::Vertex v2 (1, 2); db::Vertex v3 (2, 1); - TestableTriangleEdge s1 (&v1, &v2); - TestableTriangleEdge s2 (&v2, &v3); - TestableTriangleEdge s3 (&v3, &v1); + TestableEdge s1 (&v1, &v2); + TestableEdge s2 (&v2, &v3); + TestableEdge s3 (&v3, &v1); db::Triangle tri (&s1, &s2, &s3); @@ -269,9 +269,9 @@ TEST(Triangle_contains) db::Vertex v2 (1, 2); db::Vertex v3 (2, 1); - TestableTriangleEdge s1 (&v1, &v2); - TestableTriangleEdge s2 (&v2, &v3); - TestableTriangleEdge s3 (&v3, &v1); + TestableEdge s1 (&v1, &v2); + TestableEdge s2 (&v2, &v3); + TestableEdge s3 (&v3, &v1); { db::Triangle tri (&s1, &s2, &s3); @@ -305,9 +305,9 @@ TEST(Triangle_contains_small) db::Vertex v2 (0.001, 0.002); db::Vertex v3 (0.002, 0.001); - TestableTriangleEdge s1 (&v1, &v2); - TestableTriangleEdge s2 (&v2, &v3); - TestableTriangleEdge s3 (&v3, &v1); + TestableEdge s1 (&v1, &v2); + TestableEdge s2 (&v2, &v3); + TestableEdge s3 (&v3, &v1); { db::Triangle tri (&s1, &s2, &s3); @@ -341,9 +341,9 @@ TEST(Triangle_circumcircle) db::Vertex v2 (1, 2); db::Vertex v3 (2, 1); - TestableTriangleEdge s1 (&v1, &v2); - TestableTriangleEdge s2 (&v2, &v3); - TestableTriangleEdge s3 (&v3, &v1); + TestableEdge s1 (&v1, &v2); + TestableEdge s2 (&v2, &v3); + TestableEdge s3 (&v3, &v1); db::Triangle tri (&s1, &s2, &s3); @@ -368,7 +368,7 @@ TEST(TriangleEdge_basic) db::Vertex v1; db::Vertex v2 (1, 0.5); - TestableTriangleEdge edge (&v1, &v2); + TestableEdge edge (&v1, &v2); EXPECT_EQ (edge.to_string (), "((0, 0), (1, 0.5))"); EXPECT_EQ (edge.is_segment (), false); @@ -390,14 +390,14 @@ TEST(TriangleEdge_triangles) db::Vertex v3 (2, 1); db::Vertex v4 (-1, 2); - std::unique_ptr e1 (new TestableTriangleEdge (&v1, &v2)); - std::unique_ptr e2 (new TestableTriangleEdge (&v2, &v3)); - std::unique_ptr e3 (new TestableTriangleEdge (&v3, &v1)); + std::unique_ptr e1 (new TestableEdge (&v1, &v2)); + std::unique_ptr e2 (new TestableEdge (&v2, &v3)); + std::unique_ptr e3 (new TestableEdge (&v3, &v1)); std::unique_ptr tri (new db::Triangle (e1.get (), e2.get (), e3.get ())); - std::unique_ptr e4 (new TestableTriangleEdge (&v1, &v4)); - std::unique_ptr e5 (new TestableTriangleEdge (&v2, &v4)); + std::unique_ptr e4 (new TestableEdge (&v1, &v4)); + std::unique_ptr e5 (new TestableEdge (&v2, &v4)); std::unique_ptr tri2 (new db::Triangle (e1.get (), e4.get (), e5.get ())); EXPECT_EQ (e1->is_outside (), false); @@ -429,7 +429,7 @@ TEST(TriangleEdge_side_of) db::Vertex v1; db::Vertex v2 (1, 0.5); - TestableTriangleEdge edge (&v1, &v2); + TestableEdge edge (&v1, &v2); EXPECT_EQ (edge.to_string (), "((0, 0), (1, 0.5))"); EXPECT_EQ (edge.side_of (db::Vertex (0, 0)), 0) @@ -441,7 +441,7 @@ TEST(TriangleEdge_side_of) db::Vertex v3 (1, 0); db::Vertex v4 (0, 1); - TestableTriangleEdge edge2 (&v3, &v4); + TestableEdge edge2 (&v3, &v4); EXPECT_EQ (edge2.side_of (db::Vertex(0.2, 0.2)), -1); } @@ -464,26 +464,26 @@ TEST(TriangleEdge_crosses) { VertexHeap heap; - TestableTriangleEdge s1 (heap.make_vertex (0, 0), heap.make_vertex (1, 0.5)); - EXPECT_EQ (s1.crosses (TestableTriangleEdge (heap.make_vertex (-1, -0.5), heap.make_vertex(1, -0.5))), false); - EXPECT_EQ (s1.crosses (TestableTriangleEdge (heap.make_vertex (-1, 0), heap.make_vertex(1, 0))), false); // only cuts - EXPECT_EQ (s1.crosses (TestableTriangleEdge (heap.make_vertex (-1, 0.5), heap.make_vertex(1, 0.5))), false); - EXPECT_EQ (s1.crosses (TestableTriangleEdge (heap.make_vertex (-1, 0.5), heap.make_vertex(2, 0.5))), false); - EXPECT_EQ (s1.crosses (TestableTriangleEdge (heap.make_vertex (-1, 0.25), heap.make_vertex(2, 0.25))), true); - EXPECT_EQ (s1.crosses (TestableTriangleEdge (heap.make_vertex (-1, 0.5), heap.make_vertex(-0.1, 0.5))), false); - EXPECT_EQ (s1.crosses (TestableTriangleEdge (heap.make_vertex (-1, 0.6), heap.make_vertex(0, 0.6))), false); - EXPECT_EQ (s1.crosses (TestableTriangleEdge (heap.make_vertex (-1, 1), heap.make_vertex(1, 1))), false); + TestableEdge s1 (heap.make_vertex (0, 0), heap.make_vertex (1, 0.5)); + EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, -0.5), heap.make_vertex(1, -0.5))), false); + EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0), heap.make_vertex(1, 0))), false); // only cuts + EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0.5), heap.make_vertex(1, 0.5))), false); + EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0.5), heap.make_vertex(2, 0.5))), false); + EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0.25), heap.make_vertex(2, 0.25))), true); + EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0.5), heap.make_vertex(-0.1, 0.5))), false); + EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0.6), heap.make_vertex(0, 0.6))), false); + EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 1), heap.make_vertex(1, 1))), false); - EXPECT_EQ (s1.crosses_including (TestableTriangleEdge (heap.make_vertex (-1, -0.5), heap.make_vertex(1, -0.5))), false); - EXPECT_EQ (s1.crosses_including (TestableTriangleEdge (heap.make_vertex (-1, 0), heap.make_vertex(1, 0))), true); // only cuts - EXPECT_EQ (s1.crosses_including (TestableTriangleEdge (heap.make_vertex (-1, 0.25), heap.make_vertex(2, 0.25))), true); + EXPECT_EQ (s1.crosses_including (TestableEdge (heap.make_vertex (-1, -0.5), heap.make_vertex(1, -0.5))), false); + EXPECT_EQ (s1.crosses_including (TestableEdge (heap.make_vertex (-1, 0), heap.make_vertex(1, 0))), true); // only cuts + EXPECT_EQ (s1.crosses_including (TestableEdge (heap.make_vertex (-1, 0.25), heap.make_vertex(2, 0.25))), true); } TEST(TriangleEdge_point_on) { VertexHeap heap; - TestableTriangleEdge s1 (heap.make_vertex (0, 0), heap.make_vertex (1, 0.5)); + TestableEdge s1 (heap.make_vertex (0, 0), heap.make_vertex (1, 0.5)); EXPECT_EQ (s1.point_on (db::DPoint (0, 0)), false); // endpoints are not "on" EXPECT_EQ (s1.point_on (db::DPoint (0, -0.5)), false); EXPECT_EQ (s1.point_on (db::DPoint (0.5, 0)), false); @@ -497,8 +497,8 @@ TEST(TriangleEdge_intersection_point) { VertexHeap heap; - TestableTriangleEdge s1 (heap.make_vertex (0, 0), heap.make_vertex (1, 0.5)); - EXPECT_EQ (s1.intersection_point (TestableTriangleEdge (heap.make_vertex (-1, 0.25), heap.make_vertex (2, 0.25))).to_string (), "0.5,0.25"); + TestableEdge s1 (heap.make_vertex (0, 0), heap.make_vertex (1, 0.5)); + EXPECT_EQ (s1.intersection_point (TestableEdge (heap.make_vertex (-1, 0.25), heap.make_vertex (2, 0.25))).to_string (), "0.5,0.25"); } TEST(TriangleEdge_can_flip) @@ -507,11 +507,11 @@ TEST(TriangleEdge_can_flip) db::Vertex v2 (0, 0); db::Vertex v3 (1, 0); db::Vertex v4 (0.5, 1); - TestableTriangleEdge s1 (&v1, &v2); - TestableTriangleEdge s2 (&v1, &v3); - TestableTriangleEdge s3 (&v2, &v3); - TestableTriangleEdge s4 (&v2, &v4); - TestableTriangleEdge s5 (&v3, &v4); + TestableEdge s1 (&v1, &v2); + TestableEdge s2 (&v1, &v3); + TestableEdge s3 (&v2, &v3); + TestableEdge s4 (&v2, &v4); + TestableEdge s5 (&v3, &v4); db::Triangle t1 (&s1, &s2, &s3); db::Triangle t2 (&s3, &s4, &s5); EXPECT_EQ (s3.left () == &t2, true); @@ -532,7 +532,7 @@ TEST(TriangleEdge_distance) db::Vertex v1 (0, 0); db::Vertex v2 (1, 0); - TestableTriangleEdge seg (&v1, &v2); + TestableEdge seg (&v1, &v2); EXPECT_EQ (seg.distance (db::DPoint (0, 0)), 0); EXPECT_EQ (seg.distance (db::DPoint (0, 1)), 1); EXPECT_EQ (seg.distance (db::DPoint (1, 2)), 2); diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index ad9f9e382..f16d29b44 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -12,7 +12,8 @@ SOURCES = \ dbFillToolTests.cc \ dbLogTests.cc \ dbObjectWithPropertiesTests.cc \ - dbPLCGraphTest.cc \ + dbPLCGraphTests.cc \ + dbPLCTests.cc \ dbPLCTriangulationTests.cc \ dbPolygonNeighborhoodTests.cc \ dbPropertiesFilterTests.cc \ From 76e039bd2a04a196c78c6574d3033e91e013bfe3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 13 Apr 2025 23:06:49 +0200 Subject: [PATCH 138/392] Removing obsolete dbTriangle/dbTriangles --- src/db/db/db.pro | 4 - src/db/db/dbPLC.h | 1 - src/db/db/dbRegionProcessors.cc | 8 +- src/db/db/dbRegionProcessors.h | 4 +- src/db/db/dbTriangle.cc | 607 --------- src/db/db/dbTriangle.h | 579 -------- src/db/db/dbTriangles.cc | 1784 ------------------------- src/db/db/dbTriangles.h | 360 ----- src/db/db/gsiDeclDbPolygon.cc | 36 +- src/db/unit_tests/dbTriangleTests.cc | 549 -------- src/db/unit_tests/dbTrianglesTests.cc | 1538 --------------------- src/db/unit_tests/unit_tests.pro | 2 - 12 files changed, 26 insertions(+), 5446 deletions(-) delete mode 100644 src/db/db/dbTriangle.cc delete mode 100644 src/db/db/dbTriangle.h delete mode 100644 src/db/db/dbTriangles.cc delete mode 100644 src/db/db/dbTriangles.h delete mode 100644 src/db/unit_tests/dbTriangleTests.cc delete mode 100644 src/db/unit_tests/dbTrianglesTests.cc diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 888a22dcc..93795a97d 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -107,8 +107,6 @@ SOURCES = \ dbTextWriter.cc \ dbTilingProcessor.cc \ dbTrans.cc \ - dbTriangle.cc \ - dbTriangles.cc \ dbUserObject.cc \ dbUtils.cc \ dbVector.cc \ @@ -347,8 +345,6 @@ HEADERS = \ dbTextWriter.h \ dbTilingProcessor.h \ dbTrans.h \ - dbTriangle.h \ - dbTriangles.h \ dbTypes.h \ dbUserObject.h \ dbUtils.h \ diff --git a/src/db/db/dbPLC.h b/src/db/db/dbPLC.h index 9d7b35e43..14ac8d01c 100644 --- a/src/db/db/dbPLC.h +++ b/src/db/db/dbPLC.h @@ -24,7 +24,6 @@ #define HDR_dbPLC #include "dbCommon.h" -#include "dbTriangle.h" #include "dbBox.h" #include "dbRegion.h" diff --git a/src/db/db/dbRegionProcessors.cc b/src/db/db/dbRegionProcessors.cc index 3c3449107..730f6ea7a 100644 --- a/src/db/db/dbRegionProcessors.cc +++ b/src/db/db/dbRegionProcessors.cc @@ -453,8 +453,8 @@ TriangulationProcessor::process (const db::Polygon &poly, std::vector - -namespace db -{ - -// ------------------------------------------------------------------------------------- -// Vertex implementation - -Vertex::Vertex () - : DPoint (), m_is_precious (false) -{ - // .. nothing yet .. -} - -Vertex::Vertex (const db::DPoint &p) - : DPoint (p), m_is_precious (false) -{ - // .. nothing yet .. -} - -Vertex::Vertex (const Vertex &v) - : DPoint (), m_is_precious (false) -{ - operator= (v); -} - -Vertex &Vertex::operator= (const Vertex &v) -{ - if (this != &v) { - // NOTE: edges are not copied! - db::DPoint::operator= (v); - m_is_precious = v.m_is_precious; - } - return *this; -} - -Vertex::Vertex (db::DCoord x, db::DCoord y) - : DPoint (x, y), m_is_precious (false) -{ - // .. nothing yet .. -} - -bool -Vertex::is_outside () const -{ - for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) { - if ((*e)->is_outside ()) { - return true; - } - } - return false; -} - -std::vector -Vertex::triangles () const -{ - std::set seen; - std::vector res; - for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) { - for (auto t = (*e)->begin_triangles (); t != (*e)->end_triangles (); ++t) { - if (seen.insert (t.operator-> ()).second) { - res.push_back (t.operator-> ()); - } - } - } - return res; -} - -bool -Vertex::has_edge (const TriangleEdge *edge) const -{ - for (auto e = mp_edges.begin (); e != mp_edges.end (); ++e) { - if (*e == edge) { - return true; - } - } - return false; -} - -size_t -Vertex::num_edges (int max_count) const -{ - if (max_count < 0) { - // NOTE: this can be slow for a std::list, so we have max_count to limit this effort - return mp_edges.size (); - } else { - size_t n = 0; - for (auto i = mp_edges.begin (); i != mp_edges.end () && --max_count >= 0; ++i) { - ++n; - } - return n; - } -} - -std::string -Vertex::to_string (bool with_id) const -{ - std::string res = tl::sprintf ("(%.12g, %.12g)", x (), y()); - if (with_id) { - res += tl::sprintf ("[%x]", (size_t)this); - } - return res; -} - -int -Vertex::in_circle (const DPoint &point, const DPoint ¢er, double radius) -{ - double dx = point.x () - center.x (); - double dy = point.y () - center.y (); - double d2 = dx * dx + dy * dy; - double r2 = radius * radius; - double delta = fabs (d2 + r2) * db::epsilon; - if (d2 < r2 - delta) { - return 1; - } else if (d2 < r2 + delta) { - return 0; - } else { - return -1; - } -} - -// ------------------------------------------------------------------------------------- -// TriangleEdge implementation - -TriangleEdge::TriangleEdge () - : mp_v1 (0), mp_v2 (0), mp_left (), mp_right (), m_level (0), m_id (0), m_is_segment (false) -{ - // .. nothing yet .. -} - -TriangleEdge::TriangleEdge (Vertex *v1, Vertex *v2) - : mp_v1 (v1), mp_v2 (v2), mp_left (), mp_right (), m_level (0), m_id (0), m_is_segment (false) -{ - // .. nothing yet .. -} - -void -TriangleEdge::set_left (Triangle *t) -{ - mp_left = t; -} - -void -TriangleEdge::set_right (Triangle *t) -{ - mp_right = t; -} - -void -TriangleEdge::link () -{ - mp_v1->mp_edges.push_back (this); - m_ec_v1 = --mp_v1->mp_edges.end (); - - mp_v2->mp_edges.push_back (this); - m_ec_v2 = --mp_v2->mp_edges.end (); -} - -void -TriangleEdge::unlink () -{ - if (mp_v1) { - mp_v1->remove_edge (m_ec_v1); - } - if (mp_v2) { - mp_v2->remove_edge (m_ec_v2); - } - mp_v1 = mp_v2 = 0; -} - -Triangle * -TriangleEdge::other (const Triangle *t) const -{ - if (t == mp_left) { - return mp_right; - } - if (t == mp_right) { - return mp_left; - } - tl_assert (false); - return 0; -} - -Vertex * -TriangleEdge::other (const Vertex *t) const -{ - if (t == mp_v1) { - return mp_v2; - } - if (t == mp_v2) { - return mp_v1; - } - tl_assert (false); - return 0; -} - -bool -TriangleEdge::has_vertex (const Vertex *v) const -{ - return mp_v1 == v || mp_v2 == v; -} - -Vertex * -TriangleEdge::common_vertex (const TriangleEdge *other) const -{ - if (has_vertex (other->v1 ())) { - return (other->v1 ()); - } - if (has_vertex (other->v2 ())) { - return (other->v2 ()); - } - return 0; -} - -std::string -TriangleEdge::to_string (bool with_id) const -{ - std::string res = std::string ("(") + mp_v1->to_string (with_id) + ", " + mp_v2->to_string (with_id) + ")"; - if (with_id) { - res += tl::sprintf ("[%x]", (size_t)this); - } - return res; -} - -double -TriangleEdge::distance (const db::DEdge &e, const db::DPoint &p) -{ - double l = db::sprod (p - e.p1 (), e.d ()) / e.d ().sq_length (); - db::DPoint pp; - if (l <= 0.0) { - pp = e.p1 (); - } else if (l >= 1.0) { - pp = e.p2 (); - } else { - pp = e.p1 () + e.d () * l; - } - return (p - pp).length (); -} - -bool -TriangleEdge::crosses (const db::DEdge &e, const db::DEdge &other) -{ - return e.side_of (other.p1 ()) * e.side_of (other.p2 ()) < 0 && - other.side_of (e.p1 ()) * other.side_of (e.p2 ()) < 0; -} - -bool -TriangleEdge::crosses_including (const db::DEdge &e, const db::DEdge &other) -{ - return e.side_of (other.p1 ()) * e.side_of (other.p2 ()) <= 0 && - other.side_of (e.p1 ()) * other.side_of (e.p2 ()) <= 0; -} - -db::DPoint -TriangleEdge::intersection_point (const db::DEdge &e, const db::DEdge &other) -{ - return e.intersect_point (other).second; -} - -bool -TriangleEdge::point_on (const db::DEdge &edge, const db::DPoint &point) -{ - if (edge.side_of (point) != 0) { - return false; - } else { - return db::sprod_sign (point - edge.p1 (), edge.d ()) * db::sprod_sign(point - edge.p2 (), edge.d ()) < 0; - } -} - -bool -TriangleEdge::can_flip () const -{ - if (! left () || ! right ()) { - return false; - } - - const db::Vertex *v1 = left ()->opposite (this); - const db::Vertex *v2 = right ()->opposite (this); - return crosses (db::DEdge (*v1, *v2)); -} - -bool -TriangleEdge::can_join_via (const Vertex *vertex) const -{ - if (! left () || ! right ()) { - return false; - } - - tl_assert (has_vertex (vertex)); - const db::Vertex *v1 = left ()->opposite (this); - const db::Vertex *v2 = right ()->opposite (this); - return db::DEdge (*v1, *v2).side_of (*vertex) == 0; -} - -bool -TriangleEdge::is_outside () const -{ - return left () == 0 || right () == 0; -} - -bool -TriangleEdge::is_for_outside_triangles () const -{ - return (left () && left ()->is_outside ()) || (right () && right ()->is_outside ()); -} - -bool -TriangleEdge::has_triangle (const Triangle *t) const -{ - return t != 0 && (left () == t || right () == t); -} - -// ------------------------------------------------------------------------------------- -// Triangle implementation - -Triangle::Triangle () - : m_is_outside (false), m_id (0) -{ - for (int i = 0; i < 3; ++i) { - mp_v[i] = 0; - mp_e[i] = 0; - } -} - -Triangle::Triangle (TriangleEdge *e1, TriangleEdge *e2, TriangleEdge *e3) - : m_is_outside (false), m_id (0) -{ - mp_e[0] = e1; - mp_v[0] = e1->v1 (); - mp_v[1] = e1->v2 (); - - if (e2->has_vertex (mp_v[1])) { - mp_e[1] = e2; - mp_e[2] = e3; - } else { - mp_e[1] = e3; - mp_e[2] = e2; - } - mp_v[2] = mp_e[1]->other (mp_v[1]); - - // enforce clockwise orientation - int s = db::vprod_sign (*mp_v[2] - *mp_v[0], *mp_v[1] - *mp_v[0]); - if (s < 0) { - std::swap (mp_v[2], mp_v[1]); - } else if (s == 0) { - // Triangle is not orientable - tl_assert (false); - } - - // establish link to edges - for (int i = 0; i < 3; ++i) { - - TriangleEdge *e = mp_e[i]; - - unsigned int i1 = 0; - for ( ; e->v1 () != mp_v[i1] && i1 < 3; ++i1) - ; - unsigned int i2 = 0; - for ( ; e->v2 () != mp_v[i2] && i2 < 3; ++i2) - ; - - if ((i1 + 1) % 3 == i2) { - e->set_right (this); - } else { - e->set_left (this); - } - - } -} - -Triangle::~Triangle () -{ - unlink (); -} - -void -Triangle::unlink () -{ - for (int i = 0; i != 3; ++i) { - db::TriangleEdge *e = mp_e[i]; - if (e->left () == this) { - e->set_left (0); - } - if (e->right () == this) { - e->set_right (0); - } - } -} - -std::string -Triangle::to_string (bool with_id) const -{ - std::string res = "("; - for (int i = 0; i < 3; ++i) { - if (i > 0) { - res += ", "; - } - if (vertex (i)) { - res += vertex (i)->to_string (with_id); - } else { - res += "(null)"; - } - } - res += ")"; - return res; -} - -double -Triangle::area () const -{ - return fabs (db::vprod (mp_e[0]->d (), mp_e[1]->d ())) * 0.5; -} - -db::DBox -Triangle::bbox () const -{ - db::DBox box; - for (int i = 0; i < 3; ++i) { - box += *mp_v[i]; - } - return box; -} - - -std::pair -Triangle::circumcircle (bool *ok) const -{ - // see https://en.wikipedia.org/wiki/Circumcircle - // we set A=(0,0), so the formulas simplify - - if (ok) { - *ok = true; - } - - db::DVector b = *mp_v[1] - *mp_v[0]; - db::DVector c = *mp_v[2] - *mp_v[0]; - - double b2 = b.sq_length (); - double c2 = c.sq_length (); - - double sx = 0.5 * (b2 * c.y () - c2 * b.y ()); - double sy = 0.5 * (b.x () * c2 - c.x() * b2); - - double a1 = b.x() * c.y(); - double a2 = c.x() * b.y(); - double a = a1 - a2; - double a_abs = std::abs (a); - - if (a_abs < (std::abs (a1) + std::abs (a2)) * db::epsilon) { - if (ok) { - *ok = false; - return std::make_pair (db::DPoint (), 0.0); - } else { - tl_assert (false); - } - } - - double radius = sqrt (sx * sx + sy * sy) / a_abs; - db::DPoint center = *mp_v[0] + db::DVector (sx / a, sy / a); - - return std::make_pair (center, radius); -} - -Vertex * -Triangle::opposite (const TriangleEdge *edge) const -{ - for (int i = 0; i < 3; ++i) { - Vertex *v = mp_v[i]; - if (! edge->has_vertex (v)) { - return v; - } - } - tl_assert (false); -} - -TriangleEdge * -Triangle::opposite (const Vertex *vertex) const -{ - for (int i = 0; i < 3; ++i) { - TriangleEdge *e = mp_e[i]; - if (! e->has_vertex (vertex)) { - return e; - } - } - tl_assert (false); -} - -TriangleEdge * -Triangle::find_edge_with (const Vertex *v1, const Vertex *v2) const -{ - for (int i = 0; i < 3; ++i) { - TriangleEdge *e = mp_e[i]; - if (e->has_vertex (v1) && e->has_vertex (v2)) { - return e; - } - } - tl_assert (false); -} - -TriangleEdge * -Triangle::common_edge (const Triangle *other) const -{ - for (int i = 0; i < 3; ++i) { - TriangleEdge *e = mp_e[i];; - if (e->other (this) == other) { - return e; - } - } - return 0; -} - -int -Triangle::contains (const db::DPoint &point) const -{ - auto c = *mp_v[2] - *mp_v[0]; - auto b = *mp_v[1] - *mp_v[0]; - - int vps = db::vprod_sign (c, b); - if (vps == 0) { - return db::vprod_sign (point - *mp_v[0], b) == 0 && db::vprod_sign (point - *mp_v[0], c) == 0 ? 0 : -1; - } - - int res = 1; - - const Vertex *vl = mp_v[2]; - for (int i = 0; i < 3; ++i) { - const Vertex *v = mp_v[i]; - int n = db::vprod_sign (point - *vl, *v - *vl) * vps; - if (n < 0) { - return -1; - } else if (n == 0) { - res = 0; - } - vl = v; - } - - return res; -} - -double -Triangle::min_edge_length () const -{ - double lmin = mp_e[0]->d ().length (); - for (int i = 1; i < 3; ++i) { - lmin = std::min (lmin, mp_e[i]->d ().length ()); - } - return lmin; -} - -double -Triangle::b () const -{ - double lmin = min_edge_length (); - bool ok = false; - auto cr = circumcircle (&ok); - return ok ? lmin / cr.second : 0.0; -} - -bool -Triangle::has_segment () const -{ - for (int i = 0; i < 3; ++i) { - if (mp_e[i]->is_segment ()) { - return true; - } - } - return false; -} - -unsigned int -Triangle::num_segments () const -{ - unsigned int n = 0; - for (int i = 0; i < 3; ++i) { - if (mp_e[i]->is_segment ()) { - ++n; - } - } - return n; -} - -} diff --git a/src/db/db/dbTriangle.h b/src/db/db/dbTriangle.h deleted file mode 100644 index 332d1c695..000000000 --- a/src/db/db/dbTriangle.h +++ /dev/null @@ -1,579 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2025 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_dbTriangle -#define HDR_dbTriangle - -#include "dbCommon.h" -#include "dbPoint.h" -#include "dbEdge.h" - -#include "tlObjectCollection.h" -#include "tlList.h" - -#include -#include -#include -#include - -namespace db -{ - -class Triangle; -class TriangleEdge; - -/** - * @brief A class representing a vertex in a Delaunay triangulation graph - * - * The vertex carries information about the connected edges and - * an integer value that can be used in traversal algorithms - * ("level") - */ -class DB_PUBLIC Vertex - : public db::DPoint -{ -public: - typedef std::list edges_type; - typedef edges_type::const_iterator edges_iterator; - typedef edges_type::iterator edges_iterator_non_const; - - Vertex (); - Vertex (const DPoint &p); - Vertex (const Vertex &v); - Vertex (db::DCoord x, db::DCoord y); - - Vertex &operator= (const Vertex &v); - - bool is_outside () const; - std::vector triangles () const; - - edges_iterator begin_edges () const { return mp_edges.begin (); } - edges_iterator end_edges () const { return mp_edges.end (); } - size_t num_edges (int max_count = -1) const; - - bool has_edge (const TriangleEdge *edge) const; - - void set_is_precious (bool f) { m_is_precious = f; } - bool is_precious () const { return m_is_precious; } - - std::string to_string (bool with_id = false) const; - - /** - * @brief Returns 1 is the point is inside the circle, 0 if on the circle and -1 if outside - * TODO: Move to db::DPoint - */ - static int in_circle (const db::DPoint &point, const db::DPoint ¢er, double radius); - - /** - * @brief Returns 1 is this point is inside the circle, 0 if on the circle and -1 if outside - */ - int in_circle (const db::DPoint ¢er, double radius) const - { - return in_circle (*this, center, radius); - } - -private: - friend class TriangleEdge; - - void remove_edge (const edges_iterator_non_const &ec) - { - mp_edges.erase (ec); - } - - edges_type mp_edges; - bool m_is_precious; -}; - -/** - * @brief A class representing an edge in the Delaunay triangulation graph - */ -class DB_PUBLIC TriangleEdge -{ -public: - class TriangleIterator - { - public: - typedef Triangle value_type; - typedef Triangle &reference; - typedef Triangle *pointer; - - reference operator*() const - { - return *operator-> (); - } - - pointer operator->() const - { - return m_index ? mp_edge->right () : mp_edge->left (); - } - - bool operator== (const TriangleIterator &other) const - { - return m_index == other.m_index; - } - - bool operator!= (const TriangleIterator &other) const - { - return !operator== (other); - } - - TriangleIterator &operator++ () - { - while (++m_index < 2 && operator-> () == 0) - ; - return *this; - } - - private: - friend class TriangleEdge; - - TriangleIterator (const TriangleEdge *edge) - : mp_edge (edge), m_index (0) - { - if (! edge) { - m_index = 2; - } else { - --m_index; - operator++ (); - } - } - - const TriangleEdge *mp_edge; - unsigned int m_index; - }; - - TriangleEdge (); - TriangleEdge (Vertex *v1, Vertex *v2); - - Vertex *v1 () const { return mp_v1; } - Vertex *v2 () const { return mp_v2; } - - void reverse () - { - std::swap (mp_v1, mp_v2); - std::swap (mp_left, mp_right); - } - - Triangle *left () const { return mp_left; } - Triangle *right () const { return mp_right; } - - TriangleIterator begin_triangles () const - { - return TriangleIterator (this); - } - - TriangleIterator end_triangles () const - { - return TriangleIterator (0); - } - - void set_level (size_t l) { m_level = l; } - size_t level () const { return m_level; } - - void set_id (size_t id) { m_id = id; } - size_t id () const { return m_id; } - - void set_is_segment (bool is_seg) { m_is_segment = is_seg; } - bool is_segment () const { return m_is_segment; } - - std::string to_string (bool with_id = false) const; - - /** - * @brief Converts to an db::DEdge - */ - db::DEdge edge () const - { - return db::DEdge (*mp_v1, *mp_v2); - } - - /** - * @brief Returns the distance of the given point to the edge - * - * The distance is the minimum distance of the point to one point from the edge. - * TODO: Move to db::DEdge - */ - static double distance (const db::DEdge &e, const db::DPoint &p); - - /** - * @brief Returns the distance of the given point to the edge - * - * The distance is the minimum distance of the point to one point from the edge. - */ - double distance (const db::DPoint &p) const - { - return distance (edge (), p); - } - - /** - * @brief Returns a value indicating whether this edge crosses the other one - * - * "crosses" is true, if both edges share at least one point which is not an endpoint - * of one of the edges. - * TODO: Move to db::DEdge - */ - static bool crosses (const db::DEdge &e, const db::DEdge &other); - - /** - * @brief Returns a value indicating whether this edge crosses the other one - * - * "crosses" is true, if both edges share at least one point which is not an endpoint - * of one of the edges. - */ - bool crosses (const db::DEdge &other) const - { - return crosses (edge (), other); - } - - /** - * @brief Returns a value indicating whether this edge crosses the other one - * - * "crosses" is true, if both edges share at least one point which is not an endpoint - * of one of the edges. - */ - bool crosses (const db::TriangleEdge &other) const - { - return crosses (edge (), other.edge ()); - } - - /** - * @brief Returns a value indicating whether this edge crosses the other one - * "crosses" is true, if both edges share at least one point. - * TODO: Move to db::DEdge - */ - static bool crosses_including (const db::DEdge &e, const db::DEdge &other); - - /** - * @brief Returns a value indicating whether this edge crosses the other one - * "crosses" is true, if both edges share at least one point. - */ - bool crosses_including (const db::DEdge &other) const - { - return crosses_including (edge (), other); - } - - /** - * @brief Returns a value indicating whether this edge crosses the other one - * "crosses" is true, if both edges share at least one point. - */ - bool crosses_including (const db::TriangleEdge &other) const - { - return crosses_including (edge (), other.edge ()); - } - - /** - * @brief Gets the intersection point - * TODO: Move to db::DEdge - */ - static db::DPoint intersection_point (const db::DEdge &e, const DEdge &other); - - /** - * @brief Gets the intersection point - */ - db::DPoint intersection_point (const db::DEdge &other) const - { - return intersection_point (edge (), other); - } - - /** - * @brief Gets the intersection point - */ - db::DPoint intersection_point (const TriangleEdge &other) const - { - return intersection_point (edge (), other.edge ()); - } - - /** - * @brief Returns a value indicating whether the point is on the edge - * TODO: Move to db::DEdge - */ - static bool point_on (const db::DEdge &edge, const db::DPoint &point); - - /** - * @brief Returns a value indicating whether the point is on the edge - */ - bool point_on (const db::DPoint &point) const - { - return point_on (edge (), point); - } - - /** - * @brief Gets the side the point is on - * - * -1 is for "left", 0 is "on" and +1 is "right" - * TODO: correct to same definition as db::Edge (negative) - */ - static int side_of (const db::DEdge &e, const db::DPoint &point) - { - return -e.side_of (point); - } - - /** - * @brief Gets the side the point is on - * - * -1 is for "left", 0 is "on" and +1 is "right" - * TODO: correct to same definition as db::Edge (negative) - */ - int side_of (const db::DPoint &p) const - { - return -edge ().side_of (p); - } - - /** - * @brief Gets the distance vector - */ - db::DVector d () const - { - return *mp_v2 - *mp_v1; - } - - /** - * @brief Gets the other triangle for the given one - */ - Triangle *other (const Triangle *) const; - - /** - * @brief Gets the other vertex for the given one - */ - Vertex *other (const Vertex *) const; - - /** - * @brief Gets a value indicating whether the edge has the given vertex - */ - bool has_vertex (const Vertex *) const; - - /** - * @brief Gets the common vertex of the other edge and this edge or null if there is no common vertex - */ - Vertex *common_vertex (const TriangleEdge *other) const; - - /** - * @brief Returns a value indicating whether this edge can be flipped - */ - bool can_flip () const; - - /** - * @brief Returns a value indicating whether the edge separates two triangles that can be joined into one (via the given vertex) - */ - bool can_join_via (const Vertex *vertex) const; - - /** - * @brief Returns a value indicating whether this edge is an outside edge (no other triangles) - */ - bool is_outside () const; - - /** - * @brief Returns a value indicating whether this edge belongs to outside triangles - */ - bool is_for_outside_triangles () const; - - /** - * @brief Returns a value indicating whether t is attached to this edge - */ - bool has_triangle (const Triangle *t) const; - -protected: - void unlink (); - void link (); - -private: - friend class Triangle; - friend class Triangles; - - Vertex *mp_v1, *mp_v2; - Triangle *mp_left, *mp_right; - Vertex::edges_iterator_non_const m_ec_v1, m_ec_v2; - size_t m_level; - size_t m_id; - bool m_is_segment; - - void set_left (Triangle *t); - void set_right (Triangle *t); -}; - -/** - * @brief A compare function that compares triangles by ID - * - * The ID acts as a more predicable unique ID for the object in sets and maps. - */ -struct TriangleEdgeLessFunc -{ - bool operator () (TriangleEdge *a, TriangleEdge *b) const - { - return a->id () < b->id (); - } -}; - -/** - * @brief A class representing a triangle - */ -class DB_PUBLIC Triangle - : public tl::list_node, public tl::Object -{ -public: - Triangle (); - Triangle (TriangleEdge *e1, TriangleEdge *e2, TriangleEdge *e3); - - ~Triangle (); - - void unlink (); - - void set_id (size_t id) { m_id = id; } - size_t id () const { return m_id; } - - bool is_outside () const { return m_is_outside; } - void set_outside (bool o) { m_is_outside = o; } - - std::string to_string (bool with_id = false) const; - - /** - * @brief Gets the nth vertex (n wraps around and can be negative) - * The vertexes are oriented clockwise. - */ - inline Vertex *vertex (int n) const - { - if (n >= 0 && n < 3) { - return mp_v[n]; - } else { - return mp_v[(n + 3) % 3]; - } - } - - /** - * @brief Gets the nth edge (n wraps around and can be negative) - */ - inline TriangleEdge *edge (int n) const - { - if (n >= 0 && n < 3) { - return mp_e[n]; - } else { - return mp_e[(n + 3) % 3]; - } - } - - /** - * @brief Gets the area - */ - double area () const; - - /** - * @brief Returns the bounding box of the triangle - */ - db::DBox bbox () const; - - /** - * @brief Gets the center point and radius of the circumcircle - * If ok is non-null, it will receive a boolean value indicating whether the circumcircle is valid. - * An invalid circumcircle is an indicator for a degenerated triangle with area 0 (or close to). - */ - std::pair circumcircle (bool *ok = 0) const; - - /** - * @brief Gets the vertex opposite of the given edge - */ - Vertex *opposite (const TriangleEdge *edge) const; - - /** - * @brief Gets the edge opposite of the given vertex - */ - TriangleEdge *opposite (const Vertex *vertex) const; - - /** - * @brief Gets the edge with the given vertexes - */ - TriangleEdge *find_edge_with (const Vertex *v1, const Vertex *v2) const; - - /** - * @brief Finds the common edge for both triangles - */ - TriangleEdge *common_edge (const Triangle *other) const; - - /** - * @brief Returns a value indicating whether the point is inside (1), on the triangle (0) or outside (-1) - */ - int contains (const db::DPoint &point) const; - - /** - * @brief Gets a value indicating whether the triangle has the given vertex - */ - inline bool has_vertex (const db::Vertex *v) const - { - return mp_v[0] == v || mp_v[1] == v || mp_v[2] == v; - } - - /** - * @brief Gets a value indicating whether the triangle has the given edge - */ - inline bool has_edge (const db::TriangleEdge *e) const - { - return mp_e[0] == e || mp_e[1] == e || mp_e[2] == e; - } - - /** - * @brief Returns the minimum edge length - */ - double min_edge_length () const; - - /** - * @brief Returns the min edge length to circumcircle radius ratio - */ - double b () const; - - /** - * @brief Returns a value indicating whether the triangle borders to a segment - */ - bool has_segment () const; - - /** - * @brief Returns the number of segments the triangle borders to - */ - unsigned int num_segments () const; - -private: - bool m_is_outside; - TriangleEdge *mp_e[3]; - db::Vertex *mp_v[3]; - size_t m_id; - - // no copying - Triangle &operator= (const Triangle &); - Triangle (const Triangle &); -}; - -/** - * @brief A compare function that compares triangles by ID - * - * The ID acts as a more predicable unique ID for the object in sets and maps. - */ -struct TriangleLessFunc -{ - bool operator () (Triangle *a, Triangle *b) const - { - return a->id () < b->id (); - } -}; - -} - -#endif - diff --git a/src/db/db/dbTriangles.cc b/src/db/db/dbTriangles.cc deleted file mode 100644 index e712abaa2..000000000 --- a/src/db/db/dbTriangles.cc +++ /dev/null @@ -1,1784 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2025 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 "dbTriangles.h" -#include "dbLayout.h" -#include "dbWriter.h" -#include "tlStream.h" -#include "tlLog.h" -#include "tlTimer.h" - -#include -#include -#include -#include - -namespace db -{ - -static inline bool is_equal (const db::DPoint &a, const db::DPoint &b) -{ - return std::abs (a.x () - b.x ()) < std::max (1.0, (std::abs (a.x ()) + std::abs (b.x ()))) * db::epsilon && - std::abs (a.y () - b.y ()) < std::max (1.0, (std::abs (a.y ()) + std::abs (b.y ()))) * db::epsilon; -} - -Triangles::Triangles () - : m_is_constrained (false), m_level (0), m_id (0), m_flips (0), m_hops (0) -{ - // .. nothing yet .. -} - -Triangles::~Triangles () -{ - clear (); -} - -db::Vertex * -Triangles::create_vertex (double x, double y) -{ - m_vertex_heap.push_back (db::Vertex (x, y)); - return &m_vertex_heap.back (); -} - -db::Vertex * -Triangles::create_vertex (const db::DPoint &pt) -{ - m_vertex_heap.push_back (pt); - return &m_vertex_heap.back (); -} - -db::TriangleEdge * -Triangles::create_edge (db::Vertex *v1, db::Vertex *v2) -{ - db::TriangleEdge *edge = 0; - - if (! m_returned_edges.empty ()) { - edge = m_returned_edges.back (); - m_returned_edges.pop_back (); - *edge = db::TriangleEdge (v1, v2); - } else { - m_edges_heap.push_back (db::TriangleEdge (v1, v2)); - edge = &m_edges_heap.back (); - } - - edge->link (); - edge->set_id (++m_id); - return edge; -} - -db::Triangle * -Triangles::create_triangle (TriangleEdge *e1, TriangleEdge *e2, TriangleEdge *e3) -{ - db::Triangle *res = new db::Triangle (e1, e2, e3); - res->set_id (++m_id); - mp_triangles.push_back (res); - - return res; -} - -void -Triangles::remove_triangle (db::Triangle *tri) -{ - db::TriangleEdge *edges [3]; - for (int i = 0; i < 3; ++i) { - edges [i] = tri->edge (i); - } - - delete tri; - - // clean up edges we do no longer need - for (int i = 0; i < 3; ++i) { - db::TriangleEdge *e = edges [i]; - if (e && e->left () == 0 && e->right () == 0 && e->v1 ()) { - e->unlink (); - m_returned_edges.push_back (e); - } - } -} - -void -Triangles::init_box (const db::DBox &box) -{ - double xmin = box.left (), xmax = box.right (); - double ymin = box.bottom (), ymax = box.top (); - - db::Vertex *vbl = create_vertex (xmin, ymin); - db::Vertex *vtl = create_vertex (xmin, ymax); - db::Vertex *vbr = create_vertex (xmax, ymin); - db::Vertex *vtr = create_vertex (xmax, ymax); - - db::TriangleEdge *sl = create_edge (vbl, vtl); - db::TriangleEdge *sd = create_edge (vtl, vbr); - db::TriangleEdge *sb = create_edge (vbr, vbl); - - db::TriangleEdge *sr = create_edge (vbr, vtr); - db::TriangleEdge *st = create_edge (vtr, vtl); - - create_triangle (sl, sd, sb); - create_triangle (sd, sr, st); -} - -std::string -Triangles::to_string () -{ - std::string res; - for (auto t = mp_triangles.begin (); t != mp_triangles.end (); ++t) { - if (! res.empty ()) { - res += ", "; - } - res += t->to_string (); - } - return res; -} - -db::DBox -Triangles::bbox () const -{ - db::DBox box; - for (auto t = mp_triangles.begin (); t != mp_triangles.end (); ++t) { - box += t->bbox (); - } - return box; -} - -bool -Triangles::check (bool check_delaunay) const -{ - bool res = true; - - if (check_delaunay) { - for (auto t = mp_triangles.begin (); t != mp_triangles.end (); ++t) { - auto cp = t->circumcircle (); - auto vi = find_inside_circle (cp.first, cp.second); - if (! vi.empty ()) { - res = false; - tl::error << "(check error) triangle does not meet Delaunay criterion: " << t->to_string (); - for (auto v = vi.begin (); v != vi.end (); ++v) { - tl::error << " vertex inside circumcircle: " << (*v)->to_string (true); - } - } - } - } - - for (auto t = mp_triangles.begin (); t != mp_triangles.end (); ++t) { - for (int i = 0; i < 3; ++i) { - if (! t->edge (i)->has_triangle (t.operator-> ())) { - tl::error << "(check error) edges " << t->edge (i)->to_string (true) - << " attached to triangle " << t->to_string (true) << " does not refer to this triangle"; - res = false; - } - } - } - - for (auto e = m_edges_heap.begin (); e != m_edges_heap.end (); ++e) { - - if (!e->left () && !e->right ()) { - continue; - } - - if (e->left () && e->right ()) { - if (e->left ()->is_outside () != e->right ()->is_outside () && ! e->is_segment ()) { - tl::error << "(check error) edge " << e->to_string (true) << " splits an outside and inside triangle, but is not a segment"; - res = false; - } - } - - for (auto t = e->begin_triangles (); t != e->end_triangles (); ++t) { - if (! t->has_edge (e.operator-> ())) { - tl::error << "(check error) edge " << e->to_string (true) << " not found in adjacent triangle " << t->to_string (true); - res = false; - } - if (! t->has_vertex (e->v1 ())) { - tl::error << "(check error) edges " << e->to_string (true) << " vertex 1 not found in adjacent triangle " << t->to_string (true); - res = false; - } - if (! t->has_vertex (e->v2 ())) { - tl::error << "(check error) edges " << e->to_string (true) << " vertex 2 not found in adjacent triangle " << t->to_string (true); - res = false; - } - db::Vertex *vopp = t->opposite (e.operator-> ()); - double sgn = (e->left () == t.operator-> ()) ? 1.0 : -1.0; - double vp = db::vprod (e->d(), *vopp - *e->v1 ()); // positive if on left side - if (vp * sgn <= 0.0) { - const char * side_str = sgn > 0.0 ? "left" : "right"; - tl::error << "(check error) external point " << vopp->to_string (true) << " not on " << side_str << " side of edge " << e->to_string (true); - res = false; - } - } - - if (! e->v1 ()->has_edge (e.operator-> ())) { - tl::error << "(check error) edge " << e->to_string (true) << " vertex 1 does not list this edge"; - res = false; - } - if (! e->v2 ()->has_edge (e.operator-> ())) { - tl::error << "(check error) edge " << e->to_string (true) << " vertex 2 does not list this edge"; - res = false; - } - - } - - for (auto v = m_vertex_heap.begin (); v != m_vertex_heap.end (); ++v) { - unsigned int num_outside_edges = 0; - for (auto e = v->begin_edges (); e != v->end_edges (); ++e) { - if ((*e)->is_outside ()) { - ++num_outside_edges; - } - } - if (num_outside_edges > 0 && num_outside_edges != 2) { - tl::error << "(check error) vertex " << v->to_string (true) << " has " << num_outside_edges << " outside edges (can only be 2)"; - res = false; - for (auto e = v->begin_edges (); e != v->end_edges (); ++e) { - if ((*e)->is_outside ()) { - tl::error << " Outside edge is " << (*e)->to_string (true); - } - } - } - } - - return res; -} - -db::Layout * -Triangles::to_layout (bool decompose_by_id) const -{ - db::Layout *layout = new db::Layout (); - layout->dbu (0.001); - - auto dbu_trans = db::CplxTrans (layout->dbu ()).inverted (); - - db::Cell &top = layout->cell (layout->add_cell ("DUMP")); - unsigned int l1 = layout->insert_layer (db::LayerProperties (1, 0)); - unsigned int l2 = layout->insert_layer (db::LayerProperties (2, 0)); - unsigned int l10 = layout->insert_layer (db::LayerProperties (10, 0)); - unsigned int l20 = layout->insert_layer (db::LayerProperties (20, 0)); - unsigned int l21 = layout->insert_layer (db::LayerProperties (21, 0)); - unsigned int l22 = layout->insert_layer (db::LayerProperties (22, 0)); - - for (auto t = mp_triangles.begin (); t != mp_triangles.end (); ++t) { - db::DPoint pts[3]; - for (int i = 0; i < 3; ++i) { - pts[i] = *t->vertex (i); - } - db::DPolygon poly; - poly.assign_hull (pts + 0, pts + 3); - top.shapes (t->is_outside () ? l2 : l1).insert (dbu_trans * poly); - if (decompose_by_id) { - if ((t->id () & 1) != 0) { - top.shapes (l20).insert (dbu_trans * poly); - } - if ((t->id () & 2) != 0) { - top.shapes (l21).insert (dbu_trans * poly); - } - if ((t->id () & 4) != 0) { - top.shapes (l22).insert (dbu_trans * poly); - } - } - } - - for (auto e = m_edges_heap.begin (); e != m_edges_heap.end (); ++e) { - if ((e->left () || e->right ()) && e->is_segment ()) { - top.shapes (l10).insert (dbu_trans * e->edge ()); - } - } - - return layout; -} - -void -Triangles::dump (const std::string &path, bool decompose_by_id) const -{ - std::unique_ptr ly (to_layout (decompose_by_id)); - - tl::OutputStream stream (path); - - db::SaveLayoutOptions opt; - db::Writer writer (opt); - writer.write (*ly, stream); - - tl::info << "Triangles written to " << path; -} - -std::vector -Triangles::find_points_around (db::Vertex *vertex, double radius) -{ - std::set seen; - seen.insert (vertex); - - std::vector res; - std::vector new_vertexes, next_vertexes; - new_vertexes.push_back (vertex); - - while (! new_vertexes.empty ()) { - next_vertexes.clear (); - for (auto v = new_vertexes.begin (); v != new_vertexes.end (); ++v) { - for (auto e = (*v)->begin_edges (); e != (*v)->end_edges (); ++e) { - db::Vertex *ov = (*e)->other (*v); - if (ov->in_circle (*vertex, radius) == 1 && seen.insert (ov).second) { - next_vertexes.push_back (ov); - res.push_back (ov); - } - } - } - new_vertexes.swap (next_vertexes); - } - - return res; -} - -db::Vertex * -Triangles::insert_point (const db::DPoint &point, std::list > *new_triangles) -{ - return insert (create_vertex (point), new_triangles); -} - -db::Vertex * -Triangles::insert_point (db::DCoord x, db::DCoord y, std::list > *new_triangles) -{ - return insert (create_vertex (x, y), new_triangles); -} - -db::Vertex * -Triangles::insert (db::Vertex *vertex, std::list > *new_triangles) -{ - std::vector tris = find_triangle_for_point (*vertex); - - // the new vertex is outside the domain - if (tris.empty ()) { - tl_assert (! m_is_constrained); - insert_new_vertex (vertex, new_triangles); - return vertex; - } - - // check, if the new vertex is on an edge (may be edge between triangles or edge on outside) - std::vector on_edges; - std::vector on_vertex; - for (int i = 0; i < 3; ++i) { - db::TriangleEdge *e = tris.front ()->edge (i); - if (e->side_of (*vertex) == 0) { - if (is_equal (*vertex, *e->v1 ()) || is_equal (*vertex, *e->v2 ())) { - on_vertex.push_back (e); - } else { - on_edges.push_back (e); - } - } - } - - if (! on_vertex.empty ()) { - - tl_assert (on_vertex.size () == size_t (2)); - return on_vertex.front ()->common_vertex (on_vertex [1]); - - } else if (! on_edges.empty ()) { - - tl_assert (on_edges.size () == size_t (1)); - split_triangles_on_edge (vertex, on_edges.front (), new_triangles); - return vertex; - - } else if (tris.size () == size_t (1)) { - - // the new vertex is inside one triangle - split_triangle (tris.front (), vertex, new_triangles); - return vertex; - - } - - tl_assert (false); -} - -std::vector -Triangles::find_triangle_for_point (const db::DPoint &point) -{ - db::TriangleEdge *edge = find_closest_edge (point); - - std::vector res; - if (edge) { - for (auto t = edge->begin_triangles (); t != edge->end_triangles (); ++t) { - if (t->contains (point) >= 0) { - res.push_back (t.operator-> ()); - } - } - } - - return res; -} - -db::TriangleEdge * -Triangles::find_closest_edge (const db::DPoint &p, db::Vertex *vstart, bool inside_only) -{ - if (!vstart) { - - if (! mp_triangles.empty ()) { - - unsigned int ls = 0; - size_t n = m_vertex_heap.size (); - size_t m = n; - - // A simple heuristics that takes a sqrt(N) sample from the - // vertexes to find a good starting point - - vstart = mp_triangles.begin ()->vertex (0); - double dmin = vstart->distance (p); - - while (ls * ls < m) { - m /= 2; - for (size_t i = m / 2; i < n; i += m) { - ++ls; - // NOTE: this assumes the heap is not too loaded with orphan vertexes - db::Vertex *v = (m_vertex_heap.begin () + i).operator-> (); - if (v->begin_edges () != v->end_edges ()) { - double d = v->distance (p); - if (d < dmin) { - vstart = v; - dmin = d; - } - } - } - } - - } else { - - return 0; - - } - - } - - db::DEdge line (*vstart, p); - - double d = -1.0; - db::TriangleEdge *edge = 0; - db::Vertex *v = vstart; - - while (v) { - - db::Vertex *vnext = 0; - - for (auto e = v->begin_edges (); e != v->end_edges (); ++e) { - - if (inside_only) { - // NOTE: in inside mode we stay on the line of sight as we don't - // want to walk around outside pockets. - if (! (*e)->is_segment () && (*e)->is_for_outside_triangles ()) { - continue; - } - if (! (*e)->crosses_including (line)) { - continue; - } - } - - double ds = (*e)->distance (p); - - if (d < 0.0) { - - d = ds; - edge = *e; - vnext = edge->other (v); - - } else if (fabs (ds - d) < std::max (1.0, fabs (ds) + fabs (d)) * db::epsilon) { - - // this differentiation selects the edge which bends further towards - // the target point if both edges share a common point and that - // is the one the determines the distance. - db::Vertex *cv = edge->common_vertex (*e); - if (cv) { - db::DVector edge_d = *edge->other (cv) - *cv; - db::DVector e_d = *(*e)->other(cv) - *cv; - db::DVector r = p - *cv; - double edge_sp = db::sprod (r, edge_d) / edge_d.length (); - double s_sp = db::sprod (r, e_d) / e_d.length (); - if (s_sp > edge_sp + db::epsilon) { - edge = *e; - vnext = edge->other (v); - } - } - - } else if (ds < d) { - - d = ds; - edge = *e; - vnext = edge->other (v); - - } - - } - - ++m_hops; - - v = vnext; - - } - - return edge; -} - -void -Triangles::insert_new_vertex (db::Vertex *vertex, std::list > *new_triangles_out) -{ - if (mp_triangles.empty ()) { - - tl_assert (m_vertex_heap.size () <= size_t (3)); // fails if vertexes were created but not inserted. - - if (m_vertex_heap.size () == 3) { - - std::vector vv; - for (auto v = m_vertex_heap.begin (); v != m_vertex_heap.end (); ++v) { - vv.push_back (v.operator-> ()); - } - - // form the first triangle - db::TriangleEdge *s1 = create_edge (vv[0], vv[1]); - db::TriangleEdge *s2 = create_edge (vv[1], vv[2]); - db::TriangleEdge *s3 = create_edge (vv[2], vv[0]); - - if (db::vprod_sign (s1->d (), s2->d ()) == 0) { - // avoid degenerate Triangles to happen here - tl_assert (false); - } else { - db::Triangle *t = create_triangle (s1, s2, s3); - if (new_triangles_out) { - new_triangles_out->push_back (t); - } - } - - } - - return; - - } - - std::vector new_triangles; - - // Find closest edge - db::TriangleEdge *closest_edge = find_closest_edge (*vertex); - tl_assert (closest_edge != 0); - - db::TriangleEdge *s1 = create_edge (vertex, closest_edge->v1 ()); - db::TriangleEdge *s2 = create_edge (vertex, closest_edge->v2 ()); - - db::Triangle *t = create_triangle (s1, closest_edge, s2); - new_triangles.push_back (t); - - add_more_triangles (new_triangles, closest_edge, closest_edge->v1 (), vertex, s1); - add_more_triangles (new_triangles, closest_edge, closest_edge->v2 (), vertex, s2); - - if (new_triangles_out) { - new_triangles_out->insert (new_triangles_out->end (), new_triangles.begin (), new_triangles.end ()); - } - - fix_triangles (new_triangles, std::vector (), new_triangles_out); -} - -void -Triangles::add_more_triangles (std::vector &new_triangles, - db::TriangleEdge *incoming_edge, - db::Vertex *from_vertex, db::Vertex *to_vertex, - db::TriangleEdge *conn_edge) -{ - while (true) { - - db::TriangleEdge *next_edge = 0; - - for (auto e = from_vertex->begin_edges (); e != from_vertex->end_edges (); ++e) { - if (! (*e)->has_vertex (to_vertex) && (*e)->is_outside ()) { - // TODO: remove and break - tl_assert (next_edge == 0); - next_edge = *e; - } - } - - tl_assert (next_edge != 0); - db::Vertex *next_vertex = next_edge->other (from_vertex); - - db::DVector d_from_to = *to_vertex - *from_vertex; - db::Vertex *incoming_vertex = incoming_edge->other (from_vertex); - if (db::vprod_sign(*from_vertex - *incoming_vertex, d_from_to) * db::vprod_sign(*from_vertex - *next_vertex, d_from_to) >= 0) { - return; - } - - db::TriangleEdge *next_conn_edge = create_edge (next_vertex, to_vertex); - db::Triangle *t = create_triangle (next_conn_edge, next_edge, conn_edge); - new_triangles.push_back (t); - - incoming_edge = next_edge; - conn_edge = next_conn_edge; - from_vertex = next_vertex; - - } -} - -void -Triangles::split_triangle (db::Triangle *t, db::Vertex *vertex, std::list > *new_triangles_out) -{ - t->unlink (); - - std::map v2new_edges; - std::vector new_edges; - for (int i = 0; i < 3; ++i) { - db::Vertex *v = t->vertex (i); - db::TriangleEdge *e = create_edge (v, vertex); - v2new_edges[v] = e; - new_edges.push_back (e); - } - - std::vector new_triangles; - for (int i = 0; i < 3; ++i) { - db::TriangleEdge *e = t->edge (i); - db::Triangle *new_triangle = create_triangle (e, v2new_edges[e->v1 ()], v2new_edges[e->v2 ()]); - if (new_triangles_out) { - new_triangles_out->push_back (new_triangle); - } - new_triangle->set_outside (t->is_outside ()); - new_triangles.push_back (new_triangle); - } - - remove_triangle (t); - - fix_triangles (new_triangles, new_edges, new_triangles_out); -} - -void -Triangles::split_triangles_on_edge (db::Vertex *vertex, db::TriangleEdge *split_edge, std::list > *new_triangles_out) -{ - TriangleEdge *s1 = create_edge (split_edge->v1 (), vertex); - TriangleEdge *s2 = create_edge (split_edge->v2 (), vertex); - s1->set_is_segment (split_edge->is_segment ()); - s2->set_is_segment (split_edge->is_segment ()); - - std::vector new_triangles; - - std::vector tris; - tris.reserve (2); - for (auto t = split_edge->begin_triangles (); t != split_edge->end_triangles (); ++t) { - tris.push_back (t.operator-> ()); - } - - for (auto t = tris.begin (); t != tris.end (); ++t) { - - (*t)->unlink (); - - db::Vertex *ext_vertex = (*t)->opposite (split_edge); - TriangleEdge *new_edge = create_edge (ext_vertex, vertex); - - for (int i = 0; i < 3; ++i) { - - db::TriangleEdge *e = (*t)->edge (i); - if (e->has_vertex (ext_vertex)) { - - TriangleEdge *partial = e->has_vertex (split_edge->v1 ()) ? s1 : s2; - db::Triangle *new_triangle = create_triangle (new_edge, partial, e); - - if (new_triangles_out) { - new_triangles_out->push_back (new_triangle); - } - new_triangle->set_outside ((*t)->is_outside ()); - new_triangles.push_back (new_triangle); - - } - - } - - } - - for (auto t = tris.begin (); t != tris.end (); ++t) { - remove_triangle (*t); - } - - std::vector fixed_edges; - fixed_edges.push_back (s1); - fixed_edges.push_back (s2); - fix_triangles (new_triangles, fixed_edges, new_triangles_out); -} - -std::vector -Triangles::find_touching (const db::DBox &box) const -{ - // NOTE: this is a naive, slow implementation for test purposes - std::vector res; - for (auto v = m_vertex_heap.begin (); v != m_vertex_heap.end (); ++v) { - if (v->begin_edges () != v->end_edges ()) { - if (box.contains (*v)) { - res.push_back (const_cast (v.operator-> ())); - } - } - } - return res; -} - -std::vector -Triangles::find_inside_circle (const db::DPoint ¢er, double radius) const -{ - // NOTE: this is a naive, slow implementation for test purposes - std::vector res; - for (auto v = m_vertex_heap.begin (); v != m_vertex_heap.end (); ++v) { - if (v->begin_edges () != v->end_edges ()) { - if (v->in_circle (center, radius) == 1) { - res.push_back (const_cast (v.operator-> ())); - } - } - } - return res; -} - -void -Triangles::remove (db::Vertex *vertex, std::list > *new_triangles) -{ - if (vertex->begin_edges () == vertex->end_edges ()) { - // removing an orphan vertex -> ignore - } else if (vertex->is_outside ()) { - remove_outside_vertex (vertex, new_triangles); - } else { - remove_inside_vertex (vertex, new_triangles); - } -} - -void -Triangles::remove_outside_vertex (db::Vertex *vertex, std::list > *new_triangles_out) -{ - auto to_remove = vertex->triangles (); - - std::vector outer_edges; - for (auto t = to_remove.begin (); t != to_remove.end (); ++t) { - outer_edges.push_back ((*t)->opposite (vertex)); - } - - for (auto t = to_remove.begin (); t != to_remove.end (); ++t) { - (*t)->unlink (); - } - - auto new_triangles = fill_concave_corners (outer_edges); - - for (auto t = to_remove.begin (); t != to_remove.end (); ++t) { - remove_triangle (*t); - } - - fix_triangles (new_triangles, std::vector (), new_triangles_out); -} - -void -Triangles::remove_inside_vertex (db::Vertex *vertex, std::list > *new_triangles_out) -{ - std::set triangles_to_fix; - - bool make_new_triangle = true; - - while (vertex->num_edges (4) > 3) { - - db::TriangleEdge *to_flip = 0; - for (auto e = vertex->begin_edges (); e != vertex->end_edges () && to_flip == 0; ++e) { - if ((*e)->can_flip ()) { - to_flip = *e; - } - } - if (! to_flip) { - break; - } - - // NOTE: in the "can_join" case zero-area triangles are created which we will sort out later - triangles_to_fix.erase (to_flip->left ()); - triangles_to_fix.erase (to_flip->right ()); - - auto pp = flip (to_flip); - triangles_to_fix.insert (pp.first.first); - triangles_to_fix.insert (pp.first.second); - - } - - if (vertex->num_edges (4) > 3) { - - tl_assert (vertex->num_edges (5) == 4); - - // This case can happen if two edges attached to the vertex are collinear - // in this case choose the "join" strategy - db::TriangleEdge *jseg = 0; - for (auto e = vertex->begin_edges (); e != vertex->end_edges () && !jseg; ++e) { - if ((*e)->can_join_via (vertex)) { - jseg = *e; - } - } - tl_assert (jseg != 0); - - db::Vertex *v1 = jseg->left ()->opposite (jseg); - db::TriangleEdge *s1 = jseg->left ()->opposite (vertex); - db::Vertex *v2 = jseg->right ()->opposite (jseg); - db::TriangleEdge *s2 = jseg->right ()->opposite (vertex); - - db::TriangleEdge *jseg_opp = 0; - for (auto e = vertex->begin_edges (); e != vertex->end_edges () && !jseg_opp; ++e) { - if (!(*e)->has_triangle (jseg->left ()) && !(*e)->has_triangle (jseg->right ())) { - jseg_opp = *e; - } - } - - db::TriangleEdge *s1opp = jseg_opp->left ()->opposite (vertex); - db::TriangleEdge *s2opp = jseg_opp->right ()->opposite (vertex); - - db::TriangleEdge *new_edge = create_edge (v1, v2); - db::Triangle *t1 = create_triangle (s1, s2, new_edge); - db::Triangle *t2 = create_triangle (s1opp, s2opp, new_edge); - - triangles_to_fix.insert (t1); - triangles_to_fix.insert (t2); - - make_new_triangle = false; - - } - - auto to_remove = vertex->triangles (); - - std::vector outer_edges; - for (auto t = to_remove.begin (); t != to_remove.end (); ++t) { - outer_edges.push_back ((*t)->opposite (vertex)); - } - - if (make_new_triangle) { - - tl_assert (outer_edges.size () == size_t (3)); - - db::Triangle *nt = create_triangle (outer_edges[0], outer_edges[1], outer_edges[2]); - triangles_to_fix.insert (nt); - - } - - for (auto t = to_remove.begin (); t != to_remove.end (); ++t) { - triangles_to_fix.erase (*t); - remove_triangle (*t); - } - - if (new_triangles_out) { - for (auto t = triangles_to_fix.begin (); t != triangles_to_fix.end (); ++t) { - new_triangles_out->push_back (*t); - } - } - - std::vector to_fix_a (triangles_to_fix.begin (), triangles_to_fix.end ()); - fix_triangles (to_fix_a, std::vector (), new_triangles_out); -} - -void -Triangles::fix_triangles (const std::vector &tris, const std::vector &fixed_edges, std::list > *new_triangles) -{ - m_level += 1; - for (auto e = fixed_edges.begin (); e != fixed_edges.end (); ++e) { - (*e)->set_level (m_level); - } - - std::set queue, todo; - - for (auto t = tris.begin (); t != tris.end (); ++t) { - for (int i = 0; i < 3; ++i) { - db::TriangleEdge *e = (*t)->edge (i); - if (e->level () < m_level && ! e->is_segment ()) { - queue.insert (e); - } - } - } - - while (! queue.empty ()) { - - todo.clear (); - todo.swap (queue); - - // NOTE: we cannot be sure that already treated edges will not become - // illegal by neighbor edges flipping .. - // for s in todo: - // s.level = self.level - - for (auto e = todo.begin (); e != todo.end (); ++e) { - - if (is_illegal_edge (*e)) { - - queue.erase (*e); - - auto pp = flip (*e); - auto t1 = pp.first.first; - auto t2 = pp.first.second; - auto s12 = pp.second; - - if (new_triangles) { - new_triangles->push_back (t1); - new_triangles->push_back (t2); - } - - ++m_flips; - tl_assert (! is_illegal_edge (s12)); // TODO: remove later! - - for (int i = 0; i < 3; ++i) { - db::TriangleEdge *s1 = t1->edge (i); - if (s1->level () < m_level && ! s1->is_segment ()) { - queue.insert (s1); - } - } - - for (int i = 0; i < 3; ++i) { - db::TriangleEdge *s2 = t2->edge (i); - if (s2->level () < m_level && ! s2->is_segment ()) { - queue.insert (s2); - } - } - - } - - } - - } -} - -bool -Triangles::is_illegal_edge (db::TriangleEdge *edge) -{ - db::Triangle *left = edge->left (); - db::Triangle *right = edge->right (); - if (!left || !right) { - return false; - } - - bool ok = false; - - auto lr = left->circumcircle (&ok); - if (! ok || right->opposite (edge)->in_circle (lr.first, lr.second) > 0) { - return true; - } - - auto rr = right->circumcircle(&ok); - if (! ok || left->opposite (edge)->in_circle (rr.first, rr.second) > 0) { - return true; - } - - return false; -} - -std::pair, TriangleEdge *> -Triangles::flip (TriangleEdge *edge) -{ - db::Triangle *t1 = edge->left (); - db::Triangle *t2 = edge->right (); - - bool outside = t1->is_outside (); - tl_assert (t1->is_outside () == outside); - - // prepare for the new triangle to replace this one - t1->unlink (); - t2->unlink (); - - db::Vertex *t1_vext = t1->opposite (edge); - db::TriangleEdge *t1_sext1 = t1->find_edge_with (t1_vext, edge->v1 ()); - db::TriangleEdge *t1_sext2 = t1->find_edge_with (t1_vext, edge->v2 ()); - - db::Vertex *t2_vext = t2->opposite (edge); - db::TriangleEdge *t2_sext1 = t2->find_edge_with (t2_vext, edge->v1 ()); - db::TriangleEdge *t2_sext2 = t2->find_edge_with (t2_vext, edge->v2 ()); - - db::TriangleEdge *s_new = create_edge (t1_vext, t2_vext); - - db::Triangle *t1_new = create_triangle (s_new, t1_sext1, t2_sext1); - t1_new->set_outside (outside); - db::Triangle *t2_new = create_triangle (s_new, t1_sext2, t2_sext2); - t2_new->set_outside (outside); - - remove_triangle (t1); - remove_triangle (t2); - - return std::make_pair (std::make_pair (t1_new, t2_new), s_new); -} - -std::vector -Triangles::fill_concave_corners (const std::vector &edges) -{ - std::vector res; - std::vector points, terminals; - - std::map > vertex2edge; - for (auto e = edges.begin (); e != edges.end (); ++e) { - - auto i = vertex2edge.insert (std::make_pair ((*e)->v1 (), std::vector ())); - if (i.second) { - points.push_back ((*e)->v1 ()); - } - i.first->second.push_back (*e); - - i = vertex2edge.insert (std::make_pair ((*e)->v2 (), std::vector ())); - if (i.second) { - points.push_back ((*e)->v2 ()); - } - i.first->second.push_back (*e); - - } - - while (points.size () > size_t (2)) { - - terminals.clear (); - for (auto p = points.begin (); p != points.end (); ++p) { - if (vertex2edge [*p].size () == 1) { - terminals.push_back (*p); - } - } - tl_assert (terminals.size () == size_t (2)); - db::Vertex *v = terminals[0]; - - bool any_connected = false; - db::Vertex *vp = 0; - - std::set to_remove; - - while (vertex2edge [v].size () >= size_t (2) || ! vp) { - - db::TriangleEdge *seg = 0; - std::vector &ee = vertex2edge [v]; - for (auto e = ee.begin (); e != ee.end (); ++e) { - if (! (*e)->has_vertex (vp)) { - seg = (*e); - break; - } - } - - tl_assert (seg != 0); - db::Triangle *tri = seg->left () ? seg->left () : seg->right (); - db::Vertex *vn = seg->other (v); - - std::vector &een = vertex2edge [vn]; - if (een.size () < size_t (2)) { - break; - } - tl_assert (een.size () == size_t (2)); - - db::TriangleEdge *segn = 0; - for (auto e = een.begin (); e != een.end (); ++e) { - if (! (*e)->has_vertex (v)) { - segn = (*e); - break; - } - } - - tl_assert (segn != 0); - db::Vertex *vnn = segn->other (vn); - std::vector &eenn = vertex2edge [vnn]; - - // NOTE: tri can be None in case a lonely edge stays after removing - // attached triangles - if (! tri || seg->side_of (*vnn) * seg->side_of (*tri->opposite (seg)) < 0) { - - // is concave - db::TriangleEdge *new_edge = create_edge (v, vnn); - for (auto e = ee.begin (); e != ee.end (); ++e) { - if (*e == seg) { - ee.erase (e); - break; - } - } - ee.push_back (new_edge); - - for (auto e = eenn.begin (); e != eenn.end (); ++e) { - if (*e == segn) { - eenn.erase (e); - break; - } - } - eenn.push_back (new_edge); - - vertex2edge.erase (vn); - to_remove.insert (vn); - - db::Triangle *new_triangle = create_triangle (seg, segn, new_edge); - res.push_back (new_triangle); - any_connected = true; - - } else { - - vp = v; - v = vn; - - } - - } - - if (! any_connected) { - break; - } - - std::vector::iterator wp = points.begin (); - for (auto p = points.begin (); p != points.end (); ++p) { - if (to_remove.find (*p) == to_remove.end ()) { - *wp++ = *p; - } - } - points.erase (wp, points.end ()); - - } - - return res; -} - -std::vector -Triangles::search_edges_crossing (Vertex *from, Vertex *to) -{ - db::Vertex *v = from; - db::Vertex *vv = to; - db::DEdge edge (*from, *to); - - db::Triangle *current_triangle = 0; - db::TriangleEdge *next_edge = 0; - - std::vector result; - - for (auto e = v->begin_edges (); e != v->end_edges () && ! next_edge; ++e) { - for (auto t = (*e)->begin_triangles (); t != (*e)->end_triangles (); ++t) { - db::TriangleEdge *os = t->opposite (v); - if (os->has_vertex (vv)) { - return result; - } - if (os->crosses (edge)) { - result.push_back (os); - current_triangle = t.operator-> (); - next_edge = os; - break; - } - } - } - - tl_assert (current_triangle != 0); - - while (true) { - - current_triangle = next_edge->other (current_triangle); - - // Note that we're convex, so there has to be a path across triangles - tl_assert (current_triangle != 0); - - db::TriangleEdge *cs = next_edge; - next_edge = 0; - for (int i = 0; i < 3; ++i) { - db::TriangleEdge *e = current_triangle->edge (i); - if (e != cs) { - if (e->has_vertex (vv)) { - return result; - } - if (e->crosses (edge)) { - result.push_back (e); - next_edge = e; - break; - } - } - } - - tl_assert (next_edge != 0); - - } -} - -db::Vertex * -Triangles::find_vertex_for_point (const db::DPoint &point) -{ - db::TriangleEdge *edge = find_closest_edge (point); - if (!edge) { - return 0; - } - db::Vertex *v = 0; - if (is_equal (*edge->v1 (), point)) { - v = edge->v1 (); - } else if (is_equal (*edge->v2 (), point)) { - v = edge->v2 (); - } - return v; -} - -db::TriangleEdge * -Triangles::find_edge_for_points (const db::DPoint &p1, const db::DPoint &p2) -{ - db::Vertex *v = find_vertex_for_point (p1); - if (!v) { - return 0; - } - for (auto e = v->begin_edges (); e != v->end_edges (); ++e) { - if (is_equal (*(*e)->other (v), p2)) { - return *e; - } - } - return 0; -} - -std::vector -Triangles::ensure_edge_inner (db::Vertex *from, db::Vertex *to) -{ - auto crossed_edges = search_edges_crossing (from, to); - std::vector result; - - if (crossed_edges.empty ()) { - - // no crossing edge - there should be a edge already - db::TriangleEdge *res = find_edge_for_points (*from, *to); - tl_assert (res != 0); - result.push_back (res); - - } else if (crossed_edges.size () == 1) { - - // can be solved by flipping - auto pp = flip (crossed_edges.front ()); - db::TriangleEdge *res = pp.second; - tl_assert (res->has_vertex (from) && res->has_vertex (to)); - result.push_back (res); - - } else { - - // split edge close to center - db::DPoint split_point; - double d = -1.0; - double l_half = 0.25 * (*to - *from).sq_length (); - for (auto e = crossed_edges.begin (); e != crossed_edges.end (); ++e) { - db::DPoint p = (*e)->intersection_point (db::DEdge (*from, *to)); - double dp = fabs ((p - *from).sq_length () - l_half); - if (d < 0.0 || dp < d) { - dp = d; - split_point = p; - } - } - - db::Vertex *split_vertex = insert_point (split_point); - - result = ensure_edge_inner (from, split_vertex); - - auto result2 = ensure_edge_inner (split_vertex, to); - result.insert (result.end (), result2.begin (), result2.end ()); - - } - - return result; -} - -std::vector -Triangles::ensure_edge (db::Vertex *from, db::Vertex *to) -{ -#if 0 - // NOTE: this should not be required if the original segments are non-overlapping - // TODO: this is inefficient - for v in self.vertexes: - if edge.point_on(v): - return self.ensure_edge(Edge(edge.p1, v)) + self.ensure_edge(Edge(v, edge.p2)) -#endif - - auto edges = ensure_edge_inner (from, to); - for (auto e = edges.begin (); e != edges.end (); ++e) { - // mark the edges as fixed "forever" so we don't modify them when we ensure other edges - (*e)->set_level (std::numeric_limits::max ()); - } - return edges; -} - -void -Triangles::join_edges (std::vector &edges) -{ - // edges are supposed to be ordered - for (size_t i = 1; i < edges.size (); ) { - - db::TriangleEdge *s1 = edges [i - 1]; - db::TriangleEdge *s2 = edges [i]; - tl_assert (s1->is_segment () == s2->is_segment ()); - db::Vertex *cp = s1->common_vertex (s2); - tl_assert (cp != 0); - - std::vector join_edges; - for (auto e = cp->begin_edges (); e != cp->end_edges (); ++e) { - if (*e != s1 && *e != s2) { - if ((*e)->can_join_via (cp)) { - join_edges.push_back (*e); - } else { - join_edges.clear (); - break; - } - } - } - - if (! join_edges.empty ()) { - - tl_assert (join_edges.size () <= 2); - - TriangleEdge *new_edge = create_edge (s1->other (cp), s2->other (cp)); - new_edge->set_is_segment (s1->is_segment ()); - - for (auto js = join_edges.begin (); js != join_edges.end (); ++js) { - - db::Triangle *t1 = (*js)->left (); - db::Triangle *t2 = (*js)->right (); - db::TriangleEdge *tedge1 = t1->opposite (cp); - db::TriangleEdge *tedge2 = t2->opposite (cp); - t1->unlink (); - t2->unlink (); - db::Triangle *tri = create_triangle (tedge1, tedge2, new_edge); - tri->set_outside (t1->is_outside ()); - remove_triangle (t1); - remove_triangle (t2); - } - - edges [i - 1] = new_edge; - edges.erase (edges.begin () + i); - - } else { - ++i; - } - - } -} - -void -Triangles::constrain (const std::vector > &contours) -{ - tl_assert (! m_is_constrained); - - std::vector > > resolved_edges; - - for (auto c = contours.begin (); c != contours.end (); ++c) { - for (auto v = c->begin (); v != c->end (); ++v) { - auto vv = v; - ++vv; - if (vv == c->end ()) { - vv = c->begin (); - } - db::DEdge e (**v, **vv); - resolved_edges.push_back (std::make_pair (e, std::vector ())); - resolved_edges.back ().second = ensure_edge (*v, *vv); - } - } - - for (auto tri = mp_triangles.begin (); tri != mp_triangles.end (); ++tri) { - tri->set_outside (false); - for (int i = 0; i < 3; ++i) { - tri->edge (i)->set_is_segment (false); - } - } - - std::set new_tri; - - for (auto re = resolved_edges.begin (); re != resolved_edges.end (); ++re) { - auto edge = re->first; - auto edges = re->second; - for (auto e = edges.begin (); e != edges.end (); ++e) { - (*e)->set_is_segment (true); - db::Triangle *outer_tri = 0; - int d = db::sprod_sign (edge.d (), (*e)->d ()); - if (d > 0) { - outer_tri = (*e)->left (); - } - if (d < 0) { - outer_tri = (*e)->right (); - } - if (outer_tri) { - new_tri.insert (outer_tri); - outer_tri->set_outside (true); - } - } - } - - while (! new_tri.empty ()) { - - std::set next_tris; - - for (auto tri = new_tri.begin (); tri != new_tri.end (); ++tri) { - for (int i = 0; i < 3; ++i) { - auto e = (*tri)->edge (i); - if (! e->is_segment ()) { - auto ot = e->other (*tri); - if (ot && ! ot->is_outside ()) { - next_tris.insert (ot); - ot->set_outside (true); - } - } - } - } - - new_tri.swap (next_tris); - - } - - // join edges where possible - for (auto re = resolved_edges.begin (); re != resolved_edges.end (); ++re) { - auto edges = re->second; - join_edges (edges); - } - - m_is_constrained = true; -} - -void -Triangles::remove_outside_triangles () -{ - tl_assert (m_is_constrained); - - // NOTE: don't remove while iterating - std::vector to_remove; - for (auto tri = begin (); tri != end (); ++tri) { - if (tri->is_outside ()) { - to_remove.push_back (const_cast (tri.operator-> ())); - } - } - - for (auto t = to_remove.begin (); t != to_remove.end (); ++t) { - remove_triangle (*t); - } -} - -void -Triangles::clear () -{ - mp_triangles.clear (); - m_edges_heap.clear (); - m_vertex_heap.clear (); - m_returned_edges.clear (); - m_is_constrained = false; - m_level = 0; - m_id = 0; -} - -template -void -Triangles::make_contours (const Poly &poly, const Trans &trans, std::vector > &edge_contours) -{ - edge_contours.push_back (std::vector ()); - for (auto pt = poly.begin_hull (); pt != poly.end_hull (); ++pt) { - edge_contours.back ().push_back (insert_point (trans * *pt)); - } - - for (unsigned int h = 0; h < poly.holes (); ++h) { - edge_contours.push_back (std::vector ()); - for (auto pt = poly.begin_hole (h); pt != poly.end_hole (h); ++pt) { - edge_contours.back ().push_back (insert_point (trans * *pt)); - } - } -} - -void -Triangles::create_constrained_delaunay (const db::Region ®ion, const CplxTrans &trans) -{ - clear (); - - std::vector > edge_contours; - - for (auto p = region.begin_merged (); ! p.at_end (); ++p) { - make_contours (*p, trans, edge_contours); - } - - constrain (edge_contours); -} - -void -Triangles::create_constrained_delaunay (const db::Polygon &p, const std::vector &vertexes, const CplxTrans &trans) -{ - clear (); - - for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { - insert_point (trans * *v)->set_is_precious (true); - } - - std::vector > edge_contours; - make_contours (p, trans, edge_contours); - - constrain (edge_contours); -} - -void -Triangles::create_constrained_delaunay (const db::DPolygon &p, const std::vector &vertexes, const DCplxTrans &trans) -{ - clear (); - - for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { - insert_point (trans * *v)->set_is_precious (true); - } - - std::vector > edge_contours; - make_contours (p, trans, edge_contours); - - constrain (edge_contours); -} - -static bool is_skinny (const db::Triangle *tri, const Triangles::TriangulateParameters ¶m) -{ - if (param.min_b < db::epsilon) { - return false; - } else { - double b = tri->b (); - double delta = (b + param.min_b) * db::epsilon; - return b < param.min_b - delta; - } -} - -static bool is_invalid (const db::Triangle *tri, const Triangles::TriangulateParameters ¶m) -{ - if (is_skinny (tri, param)) { - return true; - } - - double amax = param.max_area; - if (param.max_area_border > db::epsilon) { - if (tri->has_segment ()) { - amax = param.max_area_border; - } - } - - if (amax > db::epsilon) { - double a = tri->area (); - double delta = (a + amax) * db::epsilon; - return tri->area () > amax + delta; - } else { - return false; - } -} - -void -Triangles::triangulate (const db::Region ®ion, const TriangulateParameters ¶meters, double dbu) -{ - tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate"); - - create_constrained_delaunay (region, db::CplxTrans (dbu)); - refine (parameters); -} - -void -Triangles::triangulate (const db::Region ®ion, const TriangulateParameters ¶meters, const db::CplxTrans &trans) -{ - tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate"); - - create_constrained_delaunay (region, trans); - refine (parameters); -} - -void -Triangles::triangulate (const db::Polygon &poly, const TriangulateParameters ¶meters, double dbu) -{ - triangulate (poly, std::vector (), parameters, dbu); -} - -void -Triangles::triangulate (const db::Polygon &poly, const std::vector &vertexes, const TriangulateParameters ¶meters, double dbu) -{ - tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate"); - - create_constrained_delaunay (poly, vertexes, db::CplxTrans (dbu)); - refine (parameters); -} - -void -Triangles::triangulate (const db::Polygon &poly, const TriangulateParameters ¶meters, const db::CplxTrans &trans) -{ - triangulate (poly, std::vector (), parameters, trans); -} - -void -Triangles::triangulate (const db::Polygon &poly, const std::vector &vertexes, const TriangulateParameters ¶meters, const db::CplxTrans &trans) -{ - tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate"); - - create_constrained_delaunay (poly, vertexes, trans); - refine (parameters); -} - -void -Triangles::triangulate (const db::DPolygon &poly, const TriangulateParameters ¶meters, const DCplxTrans &trans) -{ - triangulate (poly, std::vector (), parameters, trans); -} - -void -Triangles::triangulate (const db::DPolygon &poly, const std::vector &vertexes, const TriangulateParameters ¶meters, const DCplxTrans &trans) -{ - tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate"); - - create_constrained_delaunay (poly, vertexes, trans); - refine (parameters); -} - -void -Triangles::refine (const TriangulateParameters ¶meters) -{ - if (parameters.min_b < db::epsilon && parameters.max_area < db::epsilon && parameters.max_area_border < db::epsilon) { - - // no refinement requested - we're done. - remove_outside_triangles (); - return; - - } - - unsigned int nloop = 0; - std::list > new_triangles; - for (auto t = mp_triangles.begin (); t != mp_triangles.end (); ++t) { - new_triangles.push_back (t.operator-> ()); - } - - // TODO: break if iteration gets stuck - while (nloop < parameters.max_iterations) { - - ++nloop; - if (tl::verbosity () >= parameters.base_verbosity + 10) { - tl::info << "Iteration " << nloop << " .."; - } - - std::list > to_consider; - for (auto t = new_triangles.begin (); t != new_triangles.end (); ++t) { - if (t->get () && ! (*t)->is_outside () && is_invalid (t->get (), parameters)) { - to_consider.push_back (*t); - } - } - - if (to_consider.empty ()) { - break; - } - - if (tl::verbosity () >= parameters.base_verbosity + 10) { - tl::info << to_consider.size() << " triangles to consider"; - } - - new_triangles.clear (); - - for (auto t = to_consider.begin (); t != to_consider.end (); ++t) { - - if (! t->get ()) { - // triangle got removed during loop - continue; - } - - auto cr = (*t)->circumcircle(); - auto center = cr.first; - - int s = (*t)->contains (center); - if (s >= 0) { - - if (s > 0) { - - double snap = 1e-3; - - // Snap the center to a segment center if "close" to it. - // This avoids generating very skinny triangles that can't be fixed as the - // segment cannot be flipped. This a part of the issue #1996 problem. - for (unsigned int i = 0; i < 3; ++i) { - if ((*t)->edge (i)->is_segment ()) { - auto e = (*t)->edge (i)->edge (); - auto c = e.p1 () + e.d () * 0.5; - if (c.distance (center) < e.length () * 0.5 * snap - db::epsilon) { - center = c; - break; - } - } - } - - } - - if (tl::verbosity () >= parameters.base_verbosity + 20) { - tl::info << "Inserting in-triangle center " << center.to_string () << " of " << (*t)->to_string (true); - } - insert_point (center, &new_triangles); - - } else { - - db::Vertex *vstart = 0; - for (unsigned int i = 0; i < 3; ++i) { - db::TriangleEdge *edge = (*t)->edge (i); - vstart = (*t)->opposite (edge); - if (edge->side_of (*vstart) * edge->side_of (center) < 0) { - break; - } - } - - db::TriangleEdge *edge = find_closest_edge (center, vstart, true /*inside only*/); - tl_assert (edge != 0); - - if (! edge->is_segment () || edge->side_of (*vstart) * edge->side_of (center) >= 0) { - - if (tl::verbosity () >= parameters.base_verbosity + 20) { - tl::info << "Inserting out-of-triangle center " << center << " of " << (*t)->to_string (true); - } - insert_point (center, &new_triangles); - - } else { - - double sr = edge->d ().length () * 0.5; - if (sr >= parameters.min_length) { - - db::DPoint pnew = *edge->v1 () + edge->d () * 0.5; - - if (tl::verbosity () >= parameters.base_verbosity + 20) { - tl::info << "Split edge " << edge->to_string (true) << " at " << pnew.to_string (); - } - db::Vertex *vnew = insert_point (pnew, &new_triangles); - auto vertexes_in_diametral_circle = find_points_around (vnew, sr); - - std::vector to_delete; - for (auto v = vertexes_in_diametral_circle.begin (); v != vertexes_in_diametral_circle.end (); ++v) { - bool has_segment = false; - for (auto e = (*v)->begin_edges (); e != (*v)->end_edges () && ! has_segment; ++e) { - has_segment = (*e)->is_segment (); - } - if (! has_segment && ! (*v)->is_precious ()) { - to_delete.push_back (*v); - } - } - - if (tl::verbosity () >= parameters.base_verbosity + 20) { - tl::info << " -> found " << to_delete.size () << " vertexes to remove"; - } - for (auto v = to_delete.begin (); v != to_delete.end (); ++v) { - remove (*v, &new_triangles); - } - - } - - } - - } - - } - - } - - if (tl::verbosity () >= parameters.base_verbosity + 20) { - tl::info << "Finishing .."; - } - - if (parameters.mark_triangles) { - - for (auto t = begin (); t != end (); ++t) { - - size_t id = 0; - - if (! t->is_outside ()) { - - if (is_skinny (t.operator-> (), parameters)) { - id |= 1; - } - if (is_invalid (t.operator-> (), parameters)) { - id |= 2; - } - auto cp = t->circumcircle (); - auto vi = find_inside_circle (cp.first, cp.second); - if (! vi.empty ()) { - id |= 4; - } - - } - - (const_cast (t.operator->()))->set_id (id); - - } - - } - - remove_outside_triangles (); -} - -} diff --git a/src/db/db/dbTriangles.h b/src/db/db/dbTriangles.h deleted file mode 100644 index 3bb34c87d..000000000 --- a/src/db/db/dbTriangles.h +++ /dev/null @@ -1,360 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2025 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_dbTriangles -#define HDR_dbTriangles - -#include "dbCommon.h" -#include "dbTriangle.h" -#include "dbBox.h" -#include "dbRegion.h" - -#include "tlObjectCollection.h" -#include "tlStableVector.h" - -#include -#include -#include -#include - -namespace db -{ - -class Layout; - -class DB_PUBLIC Triangles -{ -public: - struct TriangulateParameters - { - TriangulateParameters () - : min_b (1.0), - min_length (0.0), - max_area (0.0), - max_area_border (0.0), - max_iterations (std::numeric_limits::max ()), - base_verbosity (30), - mark_triangles (false) - { } - - /** - * @brief Min. readius-to-shortest edge ratio - */ - double min_b; - - /** - * @brief Min. edge length - * - * This parameter does not provide a guarantee about a minimume edge length, but - * helps avoiding ever-reducing triangle splits in acute corners of the input polygon. - * Splitting of edges stops when the edge is less than the min length. - */ - double min_length; - - /** - * @brief Max area or zero for "no constraint" - */ - double max_area; - - /** - * @brief Max area for border triangles or zero for "use max_area" - */ - double max_area_border; - - /** - * @brief Max number of iterations - */ - size_t max_iterations; - - /** - * @brief The verbosity level above which triangulation reports details - */ - int base_verbosity; - - /** - * @brief If true, final triangles are marked using the "id" integer as a bit field - * - * This provides information about the result quality. - * - * Bit 0: skinny triangle - * Bit 1: bad-quality (skinny or area too large) - * Bit 2: non-Delaunay (in the strict sense) - */ - bool mark_triangles; - }; - - typedef tl::list triangles_type; - typedef triangles_type::const_iterator triangle_iterator; - - Triangles (); - ~Triangles (); - - /** - * @brief Initializes the triangle collection with a box - * Two triangles will be created. - */ - void init_box (const db::DBox &box); - - /** - * @brief Returns a string representation of the triangle graph. - */ - std::string to_string (); - - /** - * @brief Returns the bounding box of the triangle graph. - */ - db::DBox bbox () const; - - /** - * @brief Iterates the triangles in the graph (begin iterator) - */ - triangle_iterator begin () const { return mp_triangles.begin (); } - - /** - * @brief Iterates the triangles in the graph (end iterator) - */ - triangle_iterator end () const { return mp_triangles.end (); } - - /** - * @brief Returns the number of triangles in the graph - */ - size_t num_triangles () const { return mp_triangles.size (); } - - /** - * @brief Clears the triangle set - */ - void clear (); - - /** - * @brief Creates a refined Delaunay triangulation for the given region - * - * The database unit should be chosen in a way that target area values are "in the order of 1". - * For inputs featuring acute angles (angles < ~25 degree), the parameters should defined a min - * edge length ("min_length"). - * "min_length" should be at least 1e-4. If a min edge length is given, the max area constaints - * may not be satisfied. - * - * Edges in the input should not be shorter than 1e-4. - */ - void triangulate (const db::Region ®ion, const TriangulateParameters ¶meters, double dbu = 1.0); - - // more versions - void triangulate (const db::Region ®ion, const TriangulateParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ()); - void triangulate (const db::Polygon &poly, const TriangulateParameters ¶meters, double dbu = 1.0); - void triangulate (const db::Polygon &poly, const std::vector &vertexes, const TriangulateParameters ¶meters, double dbu = 1.0); - void triangulate (const db::Polygon &poly, const TriangulateParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ()); - void triangulate (const db::Polygon &poly, const std::vector &vertexes, const TriangulateParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ()); - - /** - * @brief Triangulates a floating-point polygon - */ - void triangulate (const db::DPolygon &poly, const TriangulateParameters ¶meters, const db::DCplxTrans &trans = db::DCplxTrans ()); - void triangulate (const db::DPolygon &poly, const std::vector &vertexes, const TriangulateParameters ¶meters, const db::DCplxTrans &trans = db::DCplxTrans ()); - - /** - * @brief Inserts a new vertex as the given point - * - * If "new_triangles" is not null, it will receive the list of new triangles created during - * the remove step. - * - * This method can be called after "triangulate" to add new points and adjust the triangulation. - * Inserting new points will maintain the (constrained) Delaunay condition. - */ - db::Vertex *insert_point (const db::DPoint &point, std::list > *new_triangles = 0); - - /** - * @brief Statistics: number of flips (fixing) - */ - size_t flips () const - { - return m_flips; - } - - /** - * @brief Statistics: number of hops (searching) - */ - size_t hops () const - { - return m_hops; - } - -protected: - /** - * @brief Checks the triangle graph for consistency - * This method is for testing purposes mainly. - */ - bool check (bool check_delaunay = true) const; - - /** - * @brief Dumps the triangle graph to a GDS file at the given path - * This method is for testing purposes mainly. - * - * "decompose_id" will map triangles to layer 20, 21 and 22. - * according to bit 0, 1 and 2 of the ID (useful with the 'mark_triangles' - * flat in TriangulateParameters). - */ - void dump (const std::string &path, bool decompose_by_id = false) const; - - /** - * @brief Creates a new layout object representing the triangle graph - * This method is for testing purposes mainly. - */ - db::Layout *to_layout (bool decompose_by_id = false) const; - - /** - * @brief Finds the points within (not "on") a circle of radius "radius" around the given vertex. - */ - std::vector find_points_around (Vertex *vertex, double radius); - - /** - * @brief Inserts a new vertex as the given point - * - * If "new_triangles" is not null, it will receive the list of new triangles created during - * the remove step. - */ - db::Vertex *insert_point (db::DCoord x, db::DCoord y, std::list > *new_triangles = 0); - - /** - * @brief Removes the given vertex - * - * If "new_triangles" is not null, it will receive the list of new triangles created during - * the remove step. - */ - void remove (db::Vertex *vertex, std::list > *new_triangles = 0); - - /** - * @brief Flips the given edge - */ - std::pair, db::TriangleEdge *> flip (TriangleEdge *edge); - - /** - * @brief Finds all edges that cross the given one for a convex triangulation - * - * Requirements: - * * self must be a convex triangulation - * * edge must not contain another vertex from the triangulation except p1 and p2 - */ - std::vector search_edges_crossing (db::Vertex *from, db::Vertex *to); - - /** - * @brief Finds the edge for two given points - */ - db::TriangleEdge *find_edge_for_points (const db::DPoint &p1, const db::DPoint &p2); - - /** - * @brief Finds the vertex for a point - */ - db::Vertex *find_vertex_for_point (const db::DPoint &pt); - - /** - * @brief Ensures all points between from an to are connected by edges and makes these segments - */ - std::vector ensure_edge (db::Vertex *from, db::Vertex *to); - - /** - * @brief Given a set of contours with edges, mark outer triangles - * - * The edges must be made from existing vertexes. Edge orientation is - * clockwise. - * - * This will also mark triangles as outside ones. - */ - void constrain (const std::vector > &contours); - - /** - * @brief Removes the outside triangles. - */ - void remove_outside_triangles (); - - /** - * @brief Creates a constrained Delaunay triangulation from the given Region - */ - void create_constrained_delaunay (const db::Region ®ion, const db::CplxTrans &trans = db::CplxTrans ()); - - /** - * @brief Creates a constrained Delaunay triangulation from the given Polygon - */ - void create_constrained_delaunay (const db::Polygon &poly, const std::vector &vertexes, const db::CplxTrans &trans = db::CplxTrans ()); - - /** - * @brief Creates a constrained Delaunay triangulation from the given DPolygon - */ - void create_constrained_delaunay (const db::DPolygon &poly, const std::vector &vertexes, const DCplxTrans &trans); - - /** - * @brief Returns a value indicating whether the edge is "illegal" (violates the Delaunay criterion) - */ - static bool is_illegal_edge (db::TriangleEdge *edge); - - // NOTE: these functions are SLOW and intended to test purposes only - std::vector find_touching (const db::DBox &box) const; - std::vector find_inside_circle (const db::DPoint ¢er, double radius) const; - -private: - struct TriangleBoxConvert - { - typedef db::DBox box_type; - box_type operator() (db::Triangle *t) const - { - return t ? t->bbox () : box_type (); - } - }; - - tl::list mp_triangles; - tl::stable_vector m_edges_heap; - std::vector m_returned_edges; - tl::stable_vector m_vertex_heap; - bool m_is_constrained; - size_t m_level; - size_t m_id; - size_t m_flips, m_hops; - - db::Vertex *create_vertex (double x, double y); - db::Vertex *create_vertex (const db::DPoint &pt); - db::TriangleEdge *create_edge (db::Vertex *v1, db::Vertex *v2); - db::Triangle *create_triangle (db::TriangleEdge *e1, db::TriangleEdge *e2, db::TriangleEdge *e3); - void remove_triangle (db::Triangle *tri); - - void remove_outside_vertex (db::Vertex *vertex, std::list > *new_triangles = 0); - void remove_inside_vertex (db::Vertex *vertex, std::list > *new_triangles_out = 0); - std::vector fill_concave_corners (const std::vector &edges); - void fix_triangles (const std::vector &tris, const std::vector &fixed_edges, std::list > *new_triangles); - std::vector find_triangle_for_point (const db::DPoint &point); - db::TriangleEdge *find_closest_edge (const db::DPoint &p, db::Vertex *vstart = 0, bool inside_only = false); - db::Vertex *insert (db::Vertex *vertex, std::list > *new_triangles = 0); - void split_triangle (db::Triangle *t, db::Vertex *vertex, std::list > *new_triangles_out); - void split_triangles_on_edge (db::Vertex *vertex, db::TriangleEdge *split_edge, std::list > *new_triangles_out); - void add_more_triangles (std::vector &new_triangles, - db::TriangleEdge *incoming_edge, - db::Vertex *from_vertex, db::Vertex *to_vertex, - db::TriangleEdge *conn_edge); - void insert_new_vertex(db::Vertex *vertex, std::list > *new_triangles_out); - std::vector ensure_edge_inner (db::Vertex *from, db::Vertex *to); - void join_edges (std::vector &edges); - void refine (const TriangulateParameters ¶m); - template void make_contours (const Poly &poly, const Trans &trans, std::vector > &contours); -}; - -} - -#endif - diff --git a/src/db/db/gsiDeclDbPolygon.cc b/src/db/db/gsiDeclDbPolygon.cc index 8282198ab..1a4fa5335 100644 --- a/src/db/db/gsiDeclDbPolygon.cc +++ b/src/db/db/gsiDeclDbPolygon.cc @@ -28,13 +28,13 @@ #include "dbPolygonTools.h" #include "dbPolygonGenerators.h" #include "dbHash.h" -#include "dbTriangles.h" +#include "dbPLCTriangulation.h" namespace gsi { template -static db::Region region_from_triangles (const db::Triangles &tri, const T &trans) +static db::Region region_from_triangles (const db::plc::Graph &tri, const T &trans) { db::Region result; @@ -53,10 +53,10 @@ static db::Region region_from_triangles (const db::Triangles &tri, const T &tran } template -static std::vector

polygons_from_triangles (const db::Triangles &tri, const T &trans) +static std::vector

polygons_from_triangles (const db::plc::Graph &tri, const T &trans) { std::vector

result; - result.reserve (tri.num_triangles ()); + result.reserve (tri.num_polygons ()); typename P::point_type pts [3]; @@ -89,14 +89,15 @@ static db::polygon to_polygon (const db::polygon &p) template static db::Region triangulate_ipolygon (const P *p, double max_area = 0.0, double min_b = 0.0, double dbu = 0.001) { - db::Triangles tris; - db::Triangles::TriangulateParameters param; + db::plc::Graph tris; + db::plc::Triangulation triangulation (&tris); + db::plc::TriangulationParameters param; param.min_b = min_b; param.max_area = max_area * dbu * dbu; db::CplxTrans trans = db::CplxTrans (dbu) * db::ICplxTrans (db::Trans (db::Point () - p->box ().center ())); - tris.triangulate (to_polygon (*p), param, trans); + triangulation.triangulate (to_polygon (*p), param, trans); return region_from_triangles (tris, trans.inverted ()); } @@ -104,14 +105,15 @@ static db::Region triangulate_ipolygon (const P *p, double max_area = 0.0, doubl template static db::Region triangulate_ipolygon_v (const P *p, const std::vector &vertexes, double max_area = 0.0, double min_b = 0.0, double dbu = 0.001) { - db::Triangles tris; - db::Triangles::TriangulateParameters param; + db::plc::Graph tris; + db::plc::Triangulation triangulation (&tris); + db::plc::TriangulationParameters param; param.min_b = min_b; param.max_area = max_area * dbu * dbu; db::CplxTrans trans = db::CplxTrans (dbu) * db::ICplxTrans (db::Trans (db::Point () - p->box ().center ())); - tris.triangulate (to_polygon (*p), vertexes, param, trans); + triangulation.triangulate (to_polygon (*p), vertexes, param, trans); return region_from_triangles (tris, trans.inverted ()); } @@ -119,14 +121,15 @@ static db::Region triangulate_ipolygon_v (const P *p, const std::vector static std::vector

triangulate_dpolygon (const P *p, double max_area = 0.0, double min_b = 0.0) { - db::Triangles tris; - db::Triangles::TriangulateParameters param; + db::plc::Graph tris; + db::plc::Triangulation triangulation (&tris); + db::plc::TriangulationParameters param; param.min_b = min_b; param.max_area = max_area; db::DCplxTrans trans = db::DCplxTrans (db::DTrans (db::DPoint () - p->box ().center ())); - tris.triangulate (to_polygon (*p), param, trans); + triangulation.triangulate (to_polygon (*p), param, trans); return polygons_from_triangles (tris, trans.inverted ()); } @@ -134,14 +137,15 @@ static std::vector

triangulate_dpolygon (const P *p, double max_area = 0.0, d template static std::vector

triangulate_dpolygon_v (const P *p, const std::vector &vertexes, double max_area = 0.0, double min_b = 0.0) { - db::Triangles tris; - db::Triangles::TriangulateParameters param; + db::plc::Graph tris; + db::plc::Triangulation triangulation (&tris); + db::plc::TriangulationParameters param; param.min_b = min_b; param.max_area = max_area; db::DCplxTrans trans = db::DCplxTrans (db::DTrans (db::DPoint () - p->box ().center ())); - tris.triangulate (to_polygon (*p), vertexes, param, trans); + triangulation.triangulate (to_polygon (*p), vertexes, param, trans); return polygons_from_triangles (tris, trans.inverted ()); } diff --git a/src/db/unit_tests/dbTriangleTests.cc b/src/db/unit_tests/dbTriangleTests.cc deleted file mode 100644 index b460f96ec..000000000 --- a/src/db/unit_tests/dbTriangleTests.cc +++ /dev/null @@ -1,549 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2025 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 "dbTriangle.h" -#include "tlUnitTest.h" - -#include -#include - -class TestableEdge - : public db::TriangleEdge -{ -public: - using db::TriangleEdge::TriangleEdge; - using db::TriangleEdge::link; - using db::TriangleEdge::unlink; - - TestableEdge (db::Vertex *v1, db::Vertex *v2) - : db::TriangleEdge (v1, v2) - { } -}; - -// Tests for Vertex class - -TEST(Vertex_basic) -{ - db::Vertex v; - - v.set_x (1.5); - v.set_y (0.5); - EXPECT_EQ (v.to_string (), "(1.5, 0.5)"); - EXPECT_EQ (v.x (), 1.5); - EXPECT_EQ (v.y (), 0.5); - - v = db::Vertex (db::DPoint (2, 3)); - EXPECT_EQ (v.to_string (), "(2, 3)"); -} - -static std::string edges_from_vertex (const db::Vertex &v) -{ - std::string res; - for (auto e = v.begin_edges (); e != v.end_edges (); ++e) { - if (! res.empty ()) { - res += ", "; - } - res += (*e)->to_string (); - } - return res; -} - -static std::string triangles_from_vertex (const db::Vertex &v) -{ - auto tri = v.triangles (); - std::string res; - for (auto t = tri.begin (); t != tri.end (); ++t) { - if (! res.empty ()) { - res += ", "; - } - res += (*t)->to_string (); - } - return res; -} - -TEST(Vertex_edge_registration) -{ - db::Vertex v1 (0, 0); - db::Vertex v2 (1, 2); - db::Vertex v3 (2, 1); - - std::unique_ptr e1 (new TestableEdge (&v1, &v2)); - e1->link (); - EXPECT_EQ (edges_from_vertex (v1), "((0, 0), (1, 2))"); - EXPECT_EQ (edges_from_vertex (v2), "((0, 0), (1, 2))"); - EXPECT_EQ (edges_from_vertex (v3), ""); - - std::unique_ptr e2 (new TestableEdge (&v2, &v3)); - e2->link (); - EXPECT_EQ (edges_from_vertex (v1), "((0, 0), (1, 2))"); - EXPECT_EQ (edges_from_vertex (v2), "((0, 0), (1, 2)), ((1, 2), (2, 1))"); - EXPECT_EQ (edges_from_vertex (v3), "((1, 2), (2, 1))"); - - e2->unlink (); - e2.reset (0); - EXPECT_EQ (edges_from_vertex (v1), "((0, 0), (1, 2))"); - EXPECT_EQ (edges_from_vertex (v2), "((0, 0), (1, 2))"); - EXPECT_EQ (edges_from_vertex (v3), ""); - - e1->unlink (); - e1.reset (0); - EXPECT_EQ (edges_from_vertex (v1), ""); - EXPECT_EQ (edges_from_vertex (v2), ""); - EXPECT_EQ (edges_from_vertex (v3), ""); -} - -TEST(Vertex_triangles) -{ - db::Vertex v1 (0, 0); - db::Vertex v2 (1, 2); - db::Vertex v3 (2, 1); - db::Vertex v4 (-1, 2); - EXPECT_EQ (triangles_from_vertex (v1), ""); - - std::unique_ptr e1 (new TestableEdge (&v1, &v2)); - e1->link (); - std::unique_ptr e2 (new TestableEdge (&v2, &v3)); - e2->link (); - std::unique_ptr e3 (new TestableEdge (&v3, &v1)); - e3->link (); - - std::unique_ptr tri (new db::Triangle (e1.get (), e2.get (), e3.get ())); - EXPECT_EQ (triangles_from_vertex (v1), "((0, 0), (1, 2), (2, 1))"); - EXPECT_EQ (triangles_from_vertex (v2), "((0, 0), (1, 2), (2, 1))"); - EXPECT_EQ (triangles_from_vertex (v3), "((0, 0), (1, 2), (2, 1))"); - - std::unique_ptr e4 (new TestableEdge (&v1, &v4)); - e4->link (); - std::unique_ptr e5 (new TestableEdge (&v2, &v4)); - e5->link (); - std::unique_ptr tri2 (new db::Triangle (e1.get (), e4.get (), e5.get ())); - EXPECT_EQ (triangles_from_vertex (v1), "((0, 0), (-1, 2), (1, 2)), ((0, 0), (1, 2), (2, 1))"); - EXPECT_EQ (triangles_from_vertex (v2), "((0, 0), (-1, 2), (1, 2)), ((0, 0), (1, 2), (2, 1))"); - EXPECT_EQ (triangles_from_vertex (v3), "((0, 0), (1, 2), (2, 1))"); - EXPECT_EQ (triangles_from_vertex (v4), "((0, 0), (-1, 2), (1, 2))"); - - tri->unlink (); - EXPECT_EQ (triangles_from_vertex (v1), "((0, 0), (-1, 2), (1, 2))"); - - tri2->unlink (); - EXPECT_EQ (triangles_from_vertex (v1), ""); -} - -// Tests for Triangle class - -TEST(Triangle_basic) -{ - db::Vertex v1; - db::Vertex v2 (1, 2); - db::Vertex v3 (2, 1); - - TestableEdge s1 (&v1, &v2); - TestableEdge s2 (&v2, &v3); - TestableEdge s3 (&v3, &v1); - - EXPECT_EQ (s1.v1 () == &v1, true); - EXPECT_EQ (s2.v2 () == &v3, true); - - db::Triangle tri (&s1, &s2, &s3); - EXPECT_EQ (tri.to_string (), "((0, 0), (1, 2), (2, 1))"); - EXPECT_EQ (tri.edge (-1) == &s3, true); - EXPECT_EQ (tri.edge (0) == &s1, true); - EXPECT_EQ (tri.edge (1) == &s2, true); - EXPECT_EQ (tri.edge (3) == &s1, true); - - // ordering - TestableEdge s11 (&v1, &v2); - TestableEdge s12 (&v3, &v2); - TestableEdge s13 (&v1, &v3); - - db::Triangle tri2 (&s11, &s12, &s13); - EXPECT_EQ (tri2.to_string (), "((0, 0), (1, 2), (2, 1))"); - - // triangle registration - EXPECT_EQ (s11.right () == &tri2, true); - EXPECT_EQ (s11.left () == 0, true); - EXPECT_EQ (s12.left () == &tri2, true); - EXPECT_EQ (s12.right () == 0, true); - EXPECT_EQ (s13.left () == &tri2, true); - EXPECT_EQ (s13.right () == 0, true); - - EXPECT_EQ (s13.to_string (), "((0, 0), (2, 1))"); - s13.reverse (); - EXPECT_EQ (s13.to_string (), "((2, 1), (0, 0))"); - EXPECT_EQ (s13.right () == &tri2, true); - EXPECT_EQ (s13.left () == 0, true); - - // flags - EXPECT_EQ (tri.is_outside (), false); - tri.set_outside (true); - EXPECT_EQ (tri.is_outside (), true); -} - -TEST(Triangle_find_segment_with) -{ - db::Vertex v1; - db::Vertex v2 (1, 2); - db::Vertex v3 (2, 1); - - TestableEdge s1 (&v1, &v2); - TestableEdge s2 (&v2, &v3); - TestableEdge s3 (&v3, &v1); - - db::Triangle tri (&s1, &s2, &s3); - - EXPECT_EQ (tri.find_edge_with (&v1, &v2)->to_string (), "((0, 0), (1, 2))"); - EXPECT_EQ (tri.find_edge_with (&v2, &v1)->to_string (), "((0, 0), (1, 2))"); -} - -TEST(Triangle_ext_vertex) -{ - db::Vertex v1; - db::Vertex v2 (1, 2); - db::Vertex v3 (2, 1); - - TestableEdge s1 (&v1, &v2); - TestableEdge s2 (&v2, &v3); - TestableEdge s3 (&v3, &v1); - - db::Triangle tri (&s1, &s2, &s3); - - EXPECT_EQ (tri.opposite (&s1)->to_string (), "(2, 1)"); - EXPECT_EQ (tri.opposite (&s3)->to_string (), "(1, 2)"); -} - -TEST(Triangle_opposite_vertex) -{ - db::Vertex v1; - db::Vertex v2 (1, 2); - db::Vertex v3 (2, 1); - - TestableEdge s1 (&v1, &v2); - TestableEdge s2 (&v2, &v3); - TestableEdge s3 (&v3, &v1); - - db::Triangle tri (&s1, &s2, &s3); - - EXPECT_EQ (tri.opposite (&s1)->to_string (), "(2, 1)"); - EXPECT_EQ (tri.opposite (&s3)->to_string (), "(1, 2)"); -} - -TEST(Triangle_opposite_edge) -{ - db::Vertex v1; - db::Vertex v2 (1, 2); - db::Vertex v3 (2, 1); - - TestableEdge s1 (&v1, &v2); - TestableEdge s2 (&v2, &v3); - TestableEdge s3 (&v3, &v1); - - db::Triangle tri (&s1, &s2, &s3); - - EXPECT_EQ (tri.opposite (&v1)->to_string (), "((1, 2), (2, 1))"); - EXPECT_EQ (tri.opposite (&v3)->to_string (), "((0, 0), (1, 2))"); -} - -TEST(Triangle_contains) -{ - db::Vertex v1; - db::Vertex v2 (1, 2); - db::Vertex v3 (2, 1); - - TestableEdge s1 (&v1, &v2); - TestableEdge s2 (&v2, &v3); - TestableEdge s3 (&v3, &v1); - - { - db::Triangle tri (&s1, &s2, &s3); - EXPECT_EQ (tri.contains (db::DPoint (0, 0)), 0); - EXPECT_EQ (tri.contains (db::DPoint (-1, -2)), -1); - EXPECT_EQ (tri.contains (db::DPoint (0.5, 1)), 0); - EXPECT_EQ (tri.contains (db::DPoint (0.5, 2)), -1); - EXPECT_EQ (tri.contains (db::DPoint (2.5, 1)), -1); - EXPECT_EQ (tri.contains (db::DPoint (1, -1)), -1); - EXPECT_EQ (tri.contains (db::DPoint (1, 1)), 1); - } - - s1.reverse (); - s2.reverse (); - s3.reverse (); - - { - db::Triangle tri2 (&s3, &s2, &s1); - EXPECT_EQ (tri2.contains(db::DPoint(0, 0)), 0); - EXPECT_EQ (tri2.contains(db::DPoint(0.5, 1)), 0); - EXPECT_EQ (tri2.contains(db::DPoint(0.5, 2)), -1); - EXPECT_EQ (tri2.contains(db::DPoint(2.5, 1)), -1); - EXPECT_EQ (tri2.contains(db::DPoint(1, -1)), -1); - EXPECT_EQ (tri2.contains(db::DPoint(1, 1)), 1); - } -} - -TEST(Triangle_contains_small) -{ - db::Vertex v1; - db::Vertex v2 (0.001, 0.002); - db::Vertex v3 (0.002, 0.001); - - TestableEdge s1 (&v1, &v2); - TestableEdge s2 (&v2, &v3); - TestableEdge s3 (&v3, &v1); - - { - db::Triangle tri (&s1, &s2, &s3); - EXPECT_EQ (tri.contains (db::DPoint (0, 0)), 0); - EXPECT_EQ (tri.contains (db::DPoint (-0.001, -0.002)), -1); - EXPECT_EQ (tri.contains (db::DPoint (0.0005, 0.001)), 0); - EXPECT_EQ (tri.contains (db::DPoint (0.0005, 0.002)), -1); - EXPECT_EQ (tri.contains (db::DPoint (0.0025, 0.001)), -1); - EXPECT_EQ (tri.contains (db::DPoint (0.001, -0.001)), -1); - EXPECT_EQ (tri.contains (db::DPoint (0.001, 0.001)), 1); - } - - s1.reverse (); - s2.reverse (); - s3.reverse (); - - { - db::Triangle tri2 (&s3, &s2, &s1); - EXPECT_EQ (tri2.contains(db::DPoint(0, 0)), 0); - EXPECT_EQ (tri2.contains(db::DPoint(0.0005, 0.001)), 0); - EXPECT_EQ (tri2.contains(db::DPoint(0.0005, 0.002)), -1); - EXPECT_EQ (tri2.contains(db::DPoint(0.0025, 0.001)), -1); - EXPECT_EQ (tri2.contains(db::DPoint(0.001, -0.001)), -1); - EXPECT_EQ (tri2.contains(db::DPoint(0.001, 0.001)), 1); - } -} - -TEST(Triangle_circumcircle) -{ - db::Vertex v1; - db::Vertex v2 (1, 2); - db::Vertex v3 (2, 1); - - TestableEdge s1 (&v1, &v2); - TestableEdge s2 (&v2, &v3); - TestableEdge s3 (&v3, &v1); - - db::Triangle tri (&s1, &s2, &s3); - - auto cc = tri.circumcircle (); - auto center = cc.first; - auto radius = cc.second; - - EXPECT_EQ (tl::to_string (center), "0.833333333333,0.833333333333"); - EXPECT_EQ (tl::to_string (radius), "1.17851130198"); - - EXPECT_EQ (db::Vertex::in_circle (center, center, radius), 1); - EXPECT_EQ (db::Vertex::in_circle (db::DPoint (-1, -1), center, radius), -1); - EXPECT_EQ (v1.in_circle (center, radius), 0); - EXPECT_EQ (v2.in_circle (center, radius), 0); - EXPECT_EQ (v3.in_circle (center, radius), 0); -} - -// Tests for TriangleEdge class - -TEST(TriangleEdge_basic) -{ - db::Vertex v1; - db::Vertex v2 (1, 0.5); - - TestableEdge edge (&v1, &v2); - EXPECT_EQ (edge.to_string (), "((0, 0), (1, 0.5))"); - - EXPECT_EQ (edge.is_segment (), false); - edge.set_is_segment (true); - EXPECT_EQ (edge.is_segment (), true); - - EXPECT_EQ (edge.level (), size_t (0)); - edge.set_level (42); - EXPECT_EQ (edge.level (), size_t (42)); - - EXPECT_EQ (edge.other (&v1) == &v2, true); - EXPECT_EQ (edge.other (&v2) == &v1, true); -} - -TEST(TriangleEdge_triangles) -{ - db::Vertex v1 (0, 0); - db::Vertex v2 (1, 2); - db::Vertex v3 (2, 1); - db::Vertex v4 (-1, 2); - - std::unique_ptr e1 (new TestableEdge (&v1, &v2)); - std::unique_ptr e2 (new TestableEdge (&v2, &v3)); - std::unique_ptr e3 (new TestableEdge (&v3, &v1)); - - std::unique_ptr tri (new db::Triangle (e1.get (), e2.get (), e3.get ())); - - std::unique_ptr e4 (new TestableEdge (&v1, &v4)); - std::unique_ptr e5 (new TestableEdge (&v2, &v4)); - std::unique_ptr tri2 (new db::Triangle (e1.get (), e4.get (), e5.get ())); - - EXPECT_EQ (e1->is_outside (), false); - EXPECT_EQ (e2->is_outside (), true); - EXPECT_EQ (e4->is_outside (), true); - - EXPECT_EQ (e1->is_for_outside_triangles (), false); - tri->set_outside (true); - EXPECT_EQ (e1->is_for_outside_triangles (), true); - - EXPECT_EQ (e1->has_triangle (tri.get ()), true); - EXPECT_EQ (e1->has_triangle (tri2.get ()), true); - EXPECT_EQ (e4->has_triangle (tri.get ()), false); - EXPECT_EQ (e4->has_triangle (tri2.get ()), true); - - EXPECT_EQ (e1->other (tri.get ()) == tri2.get (), true); - EXPECT_EQ (e1->other (tri2.get ()) == tri.get (), true); - - EXPECT_EQ (e1->common_vertex (e2.get ()) == &v2, true); - EXPECT_EQ (e2->common_vertex (e4.get ()) == 0, true); - - tri->unlink (); - EXPECT_EQ (e1->has_triangle (tri.get ()), false); - EXPECT_EQ (e1->has_triangle (tri2.get ()), true); -} - -TEST(TriangleEdge_side_of) -{ - db::Vertex v1; - db::Vertex v2 (1, 0.5); - - TestableEdge edge (&v1, &v2); - EXPECT_EQ (edge.to_string (), "((0, 0), (1, 0.5))"); - - EXPECT_EQ (edge.side_of (db::Vertex (0, 0)), 0) - EXPECT_EQ (edge.side_of (db::Vertex (0.5, 0.25)), 0) - EXPECT_EQ (edge.side_of (db::Vertex (0, 1)), -1) - EXPECT_EQ (edge.side_of (db::Vertex (0, -1)), 1) - EXPECT_EQ (edge.side_of (db::Vertex (0.5, 0.5)), -1) - EXPECT_EQ (edge.side_of (db::Vertex (0.5, 0)), 1) - - db::Vertex v3 (1, 0); - db::Vertex v4 (0, 1); - TestableEdge edge2 (&v3, &v4); - - EXPECT_EQ (edge2.side_of (db::Vertex(0.2, 0.2)), -1); -} - -namespace { - class VertexHeap - { - public: - db::Vertex *make_vertex (double x, double y) - { - m_heap.push_back (db::Vertex (x, y)); - return &m_heap.back (); - } - private: - std::list m_heap; - }; -} - -TEST(TriangleEdge_crosses) -{ - VertexHeap heap; - - TestableEdge s1 (heap.make_vertex (0, 0), heap.make_vertex (1, 0.5)); - EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, -0.5), heap.make_vertex(1, -0.5))), false); - EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0), heap.make_vertex(1, 0))), false); // only cuts - EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0.5), heap.make_vertex(1, 0.5))), false); - EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0.5), heap.make_vertex(2, 0.5))), false); - EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0.25), heap.make_vertex(2, 0.25))), true); - EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0.5), heap.make_vertex(-0.1, 0.5))), false); - EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 0.6), heap.make_vertex(0, 0.6))), false); - EXPECT_EQ (s1.crosses (TestableEdge (heap.make_vertex (-1, 1), heap.make_vertex(1, 1))), false); - - EXPECT_EQ (s1.crosses_including (TestableEdge (heap.make_vertex (-1, -0.5), heap.make_vertex(1, -0.5))), false); - EXPECT_EQ (s1.crosses_including (TestableEdge (heap.make_vertex (-1, 0), heap.make_vertex(1, 0))), true); // only cuts - EXPECT_EQ (s1.crosses_including (TestableEdge (heap.make_vertex (-1, 0.25), heap.make_vertex(2, 0.25))), true); -} - -TEST(TriangleEdge_point_on) -{ - VertexHeap heap; - - TestableEdge s1 (heap.make_vertex (0, 0), heap.make_vertex (1, 0.5)); - EXPECT_EQ (s1.point_on (db::DPoint (0, 0)), false); // endpoints are not "on" - EXPECT_EQ (s1.point_on (db::DPoint (0, -0.5)), false); - EXPECT_EQ (s1.point_on (db::DPoint (0.5, 0)), false); - EXPECT_EQ (s1.point_on (db::DPoint (0.5, 0.25)), true); - EXPECT_EQ (s1.point_on (db::DPoint (1, 0.5)), false); // endpoints are not "on" - EXPECT_EQ (s1.point_on (db::DPoint (1, 1)), false); - EXPECT_EQ (s1.point_on (db::DPoint (2, 1)), false); -} - -TEST(TriangleEdge_intersection_point) -{ - VertexHeap heap; - - TestableEdge s1 (heap.make_vertex (0, 0), heap.make_vertex (1, 0.5)); - EXPECT_EQ (s1.intersection_point (TestableEdge (heap.make_vertex (-1, 0.25), heap.make_vertex (2, 0.25))).to_string (), "0.5,0.25"); -} - -TEST(TriangleEdge_can_flip) -{ - db::Vertex v1 (2, -1); - db::Vertex v2 (0, 0); - db::Vertex v3 (1, 0); - db::Vertex v4 (0.5, 1); - TestableEdge s1 (&v1, &v2); - TestableEdge s2 (&v1, &v3); - TestableEdge s3 (&v2, &v3); - TestableEdge s4 (&v2, &v4); - TestableEdge s5 (&v3, &v4); - db::Triangle t1 (&s1, &s2, &s3); - db::Triangle t2 (&s3, &s4, &s5); - EXPECT_EQ (s3.left () == &t2, true); - EXPECT_EQ (s3.right () == &t1, true); - EXPECT_EQ (s3.can_flip(), false); - v1.set_x (0.5); - EXPECT_EQ (s3.can_flip(), true); - v1.set_x (-0.25); - EXPECT_EQ (s3.can_flip(), true); - v1.set_x (-0.5); - EXPECT_EQ (s3.can_flip(), false); - v1.set_x (-1.0); - EXPECT_EQ (s3.can_flip(), false); -} - -TEST(TriangleEdge_distance) -{ - db::Vertex v1 (0, 0); - db::Vertex v2 (1, 0); - - TestableEdge seg (&v1, &v2); - EXPECT_EQ (seg.distance (db::DPoint (0, 0)), 0); - EXPECT_EQ (seg.distance (db::DPoint (0, 1)), 1); - EXPECT_EQ (seg.distance (db::DPoint (1, 2)), 2); - EXPECT_EQ (seg.distance (db::DPoint (1, -1)), 1); - EXPECT_EQ (seg.distance (db::DPoint (2, 0)), 1); - EXPECT_EQ (seg.distance (db::DPoint (-2, 0)), 2); - seg.reverse (); - EXPECT_EQ (seg.distance (db::DPoint (0, 0)), 0); - EXPECT_EQ (seg.distance (db::DPoint (0, 1)), 1); - EXPECT_EQ (seg.distance (db::DPoint (1, 2)), 2); - EXPECT_EQ (seg.distance (db::DPoint (1, -1)), 1); - EXPECT_EQ (seg.distance (db::DPoint (2, 0)), 1); - EXPECT_EQ (seg.distance (db::DPoint (-2, 0)), 2); -} diff --git a/src/db/unit_tests/dbTrianglesTests.cc b/src/db/unit_tests/dbTrianglesTests.cc deleted file mode 100644 index cd5779f2d..000000000 --- a/src/db/unit_tests/dbTrianglesTests.cc +++ /dev/null @@ -1,1538 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2025 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 "dbTriangles.h" -#include "dbWriter.h" -#include "dbRegionProcessors.h" -#include "tlUnitTest.h" -#include "tlStream.h" -#include "tlFileUtils.h" - -#include -#include -#include -#include - -class TestableTriangles - : public db::Triangles -{ -public: - using db::Triangles::Triangles; - using db::Triangles::check; - using db::Triangles::dump; - using db::Triangles::flip; - using db::Triangles::insert_point; - using db::Triangles::search_edges_crossing; - using db::Triangles::find_edge_for_points; - using db::Triangles::find_points_around; - using db::Triangles::find_inside_circle; - using db::Triangles::create_constrained_delaunay; - using db::Triangles::is_illegal_edge; - using db::Triangles::find_vertex_for_point; - using db::Triangles::remove; - using db::Triangles::ensure_edge; - using db::Triangles::constrain; - using db::Triangles::remove_outside_triangles; -}; - -TEST(basic) -{ - TestableTriangles tris; - tris.init_box (db::DBox (1, 0, 5, 4)); - - EXPECT_EQ (tris.bbox ().to_string (), "(1,0;5,4)"); - EXPECT_EQ (tris.to_string (), "((1, 0), (1, 4), (5, 0)), ((1, 4), (5, 4), (5, 0))"); - - EXPECT_EQ (tris.check (), true); - - tris.clear (); - - EXPECT_EQ (tris.bbox ().to_string (), "()"); - EXPECT_EQ (tris.to_string (), ""); - - EXPECT_EQ (tris.check (), true); -} - -TEST(flip) -{ - TestableTriangles tris; - tris.init_box (db::DBox (0, 0, 1, 1)); - EXPECT_EQ (tris.to_string (), "((0, 0), (0, 1), (1, 0)), ((0, 1), (1, 1), (1, 0))"); - - EXPECT_EQ (tris.num_triangles (), size_t (2)); - - const db::Triangle &t1 = *tris.begin (); - db::TriangleEdge *diag_segment; - for (int i = 0; i < 3; ++i) { - diag_segment = t1.edge (i); - if (diag_segment->side_of (db::DPoint (0.5, 0.5)) == 0) { - break; - } - } - tris.flip (diag_segment); - EXPECT_EQ (tris.to_string (), "((1, 1), (0, 0), (0, 1)), ((1, 1), (1, 0), (0, 0))"); - EXPECT_EQ (tris.check (), true); -} - -TEST(insert) -{ - TestableTriangles tris; - tris.init_box (db::DBox (0, 0, 1, 1)); - - tris.insert_point (0.2, 0.2); - EXPECT_EQ (tris.to_string (), "((0, 0), (0, 1), (0.2, 0.2)), ((1, 0), (0, 0), (0.2, 0.2)), ((1, 1), (0.2, 0.2), (0, 1)), ((1, 1), (1, 0), (0.2, 0.2))"); - EXPECT_EQ (tris.check (), true); -} - -TEST(split_segment) -{ - TestableTriangles tris; - tris.init_box (db::DBox (0, 0, 1, 1)); - - tris.insert_point (0.5, 0.5); - EXPECT_EQ (tris.to_string (), "((1, 1), (1, 0), (0.5, 0.5)), ((1, 1), (0.5, 0.5), (0, 1)), ((0, 0), (0, 1), (0.5, 0.5)), ((0, 0), (0.5, 0.5), (1, 0))"); - EXPECT_EQ (tris.check(), true); -} - -TEST(insert_vertex_twice) -{ - TestableTriangles tris; - tris.init_box (db::DBox (0, 0, 1, 1)); - - tris.insert_point (0.5, 0.5); - // inserted a vertex twice does not change anything - tris.insert_point (0.5, 0.5); - EXPECT_EQ (tris.to_string (), "((1, 1), (1, 0), (0.5, 0.5)), ((1, 1), (0.5, 0.5), (0, 1)), ((0, 0), (0, 1), (0.5, 0.5)), ((0, 0), (0.5, 0.5), (1, 0))"); - EXPECT_EQ (tris.check(), true); -} - -TEST(insert_vertex_convex) -{ - TestableTriangles tris; - tris.insert_point (0.2, 0.2); - tris.insert_point (0.2, 0.8); - tris.insert_point (0.6, 0.5); - tris.insert_point (0.7, 0.5); - tris.insert_point (0.6, 0.4); - EXPECT_EQ (tris.to_string (), "((0.2, 0.2), (0.2, 0.8), (0.6, 0.5)), ((0.2, 0.8), (0.7, 0.5), (0.6, 0.5)), ((0.6, 0.4), (0.6, 0.5), (0.7, 0.5)), ((0.6, 0.4), (0.2, 0.2), (0.6, 0.5))"); - EXPECT_EQ (tris.check(), true); -} - -TEST(insert_vertex_convex2) -{ - TestableTriangles tris; - tris.insert_point (0.25, 0.1); - tris.insert_point (0.1, 0.4); - tris.insert_point (0.4, 0.15); - tris.insert_point (1, 0.7); - EXPECT_EQ (tris.to_string (), "((0.25, 0.1), (0.1, 0.4), (0.4, 0.15)), ((1, 0.7), (0.4, 0.15), (0.1, 0.4))"); - EXPECT_EQ (tris.check(), true); -} - -TEST(insert_vertex_convex3) -{ - TestableTriangles tris; - tris.insert_point (0.25, 0.5); - tris.insert_point (0.25, 0.55); - tris.insert_point (0.15, 0.8); - tris.insert_point (1, 0.4); - EXPECT_EQ (tris.to_string (), "((0.25, 0.5), (0.15, 0.8), (0.25, 0.55)), ((1, 0.4), (0.25, 0.5), (0.25, 0.55)), ((0.15, 0.8), (1, 0.4), (0.25, 0.55))"); - EXPECT_EQ (tris.check(), true); -} - -TEST(search_edges_crossing) -{ - TestableTriangles tris; - db::Vertex *v1 = tris.insert_point (0.2, 0.2); - db::Vertex *v2 = tris.insert_point (0.2, 0.8); - db::Vertex *v3 = tris.insert_point (0.6, 0.5); - /*db::Vertex *v4 =*/ tris.insert_point (0.7, 0.5); - db::Vertex *v5 = tris.insert_point (0.6, 0.4); - db::Vertex *v6 = tris.insert_point (0.7, 0.2); - EXPECT_EQ (tris.check(), true); - - auto xedges = tris.search_edges_crossing (v2, v6); - - EXPECT_EQ (xedges.size (), size_t (2)); - auto s1 = tris.find_edge_for_points (*v1, *v3); - auto s2 = tris.find_edge_for_points (*v1, *v5); - EXPECT_EQ (std::find (xedges.begin (), xedges.end (), s1) != xedges.end (), true); - EXPECT_EQ (std::find (xedges.begin (), xedges.end (), s2) != xedges.end (), true); -} - -TEST(illegal_edge1) -{ - db::Vertex v1 (0, 0); - db::Vertex v2 (1.6, 1.6); - db::Vertex v3 (1, 2); - db::Vertex v4 (2, 1); - - { - db::TriangleEdge e1 (&v1, &v3); - db::TriangleEdge e2 (&v3, &v4); - db::TriangleEdge e3 (&v4, &v1); - - db::Triangle t1 (&e1, &e2, &e3); - - db::TriangleEdge ee1 (&v2, &v3); - db::TriangleEdge ee2 (&v4, &v2); - - db::Triangle t2 (&ee1, &e2, &ee2); - - EXPECT_EQ (TestableTriangles::is_illegal_edge (&e2), true); - } - - { - // flipped - db::TriangleEdge e1 (&v1, &v2); - db::TriangleEdge e2 (&v2, &v3); - db::TriangleEdge e3 (&v3, &v1); - - db::Triangle t1 (&e1, &e2, &e3); - - db::TriangleEdge ee1 (&v1, &v4); - db::TriangleEdge ee2 (&v4, &v2); - - db::Triangle t2 (&ee1, &ee2, &e1); - - EXPECT_EQ (TestableTriangles::is_illegal_edge (&e2), false); - } -} - -TEST(illegal_edge2) -{ - // numerical border case - db::Vertex v1 (773.94756216690905, 114.45875269431208); - db::Vertex v2 (773.29574734131643, 113.47402096138073); - db::Vertex v3 (773.10652961562653, 114.25497975904504); - db::Vertex v4 (774.08856345337881, 113.60495072750861); - - { - db::TriangleEdge e1 (&v1, &v2); - db::TriangleEdge e2 (&v2, &v4); - db::TriangleEdge e3 (&v4, &v1); - - db::Triangle t1 (&e1, &e2, &e3); - - db::TriangleEdge ee1 (&v2, &v3); - db::TriangleEdge ee2 (&v3, &v4); - - db::Triangle t2 (&ee1, &ee2, &e2); - - EXPECT_EQ (TestableTriangles::is_illegal_edge (&e2), false); - } - - { - // flipped - db::TriangleEdge e1 (&v1, &v2); - db::TriangleEdge e2 (&v2, &v3); - db::TriangleEdge e3 (&v3, &v1); - - db::Triangle t1 (&e1, &e2, &e3); - - db::TriangleEdge ee1 (&v1, &v4); - db::TriangleEdge ee2 (&v4, &v2); - - db::Triangle t2 (&ee1, &ee2, &e1); - - EXPECT_EQ (TestableTriangles::is_illegal_edge (&e1), false); - } -} - -// Returns a random float number between 0.0 and 1.0 -inline double flt_rand () -{ - return rand () * (1.0 / double (RAND_MAX)); -} - -namespace { - struct PointLessOp - { - bool operator() (const db::DPoint &a, const db::DPoint &b) const - { - return a.less (b); - } - }; -} - -TEST(insert_many) -{ - srand (0); - - TestableTriangles tris; - double res = 65536.0; - - db::DBox bbox; - - unsigned int n = 200000; - for (unsigned int i = 0; i < n; ++i) { - double x = round (flt_rand () * res) * 0.0001; - double y = round (flt_rand () * res) * 0.0001; - tris.insert_point (x, y); - } - - EXPECT_LT (double (tris.flips ()) / double (n), 3.1); - EXPECT_LT (double (tris.hops ()) / double (n), 23.0); -} - -TEST(heavy_insert) -{ - tl::info << "Running test_heavy_insert " << tl::noendl; - - for (unsigned int l = 0; l < 100; ++l) { - - srand (l); - tl::info << "." << tl::noendl; - - TestableTriangles tris; - double res = 128.0; - - unsigned int n = rand () % 190 + 10; - - db::DBox bbox; - std::map vmap; - - for (unsigned int i = 0; i < n; ++i) { - double x = round (flt_rand () * res) * (1.0 / res); - double y = round (flt_rand () * res) * (1.0 / res); - db::Vertex *v = tris.insert_point (x, y); - bbox += db::DPoint (x, y); - vmap.insert (std::make_pair (*v, false)); - } - - // not strictly true, but very likely with at least 10 vertexes: - EXPECT_GT (tris.num_triangles (), size_t (0)); - EXPECT_EQ (tris.bbox ().to_string (), bbox.to_string ()); - - bool ok = true; - for (auto t = tris.begin (); t != tris.end (); ++t) { - for (int i = 0; i < 3; ++i) { - auto f = vmap.find (*t->vertex (i)); - if (f == vmap.end ()) { - tl::error << "Could not identify triangle vertex " << t->vertex (i)->to_string () << " as inserted vertex"; - ok = false; - } else { - f->second = true; - } - } - } - for (auto m = vmap.begin (); m != vmap.end (); ++m) { - if (!m->second) { - tl::error << "Could not identify vertex " << m->first.to_string () << " with a triangle"; - ok = false; - } - } - EXPECT_EQ (ok, true); - - EXPECT_EQ (tris.check(), true); - - } - - tl::info << tl::endl << "done."; -} - -TEST(heavy_remove) -{ - tl::info << "Running test_heavy_remove " << tl::noendl; - - for (unsigned int l = 0; l < 100; ++l) { - - srand (l); - tl::info << "." << tl::noendl; - - TestableTriangles tris; - double res = 128.0; - - unsigned int n = rand () % 190 + 10; - - for (unsigned int i = 0; i < n; ++i) { - double x = round (flt_rand () * res) * (1.0 / res); - double y = round (flt_rand () * res) * (1.0 / res); - tris.insert_point (x, y); - } - - EXPECT_EQ (tris.check(), true); - - std::set vset; - std::vector vertexes; - for (auto t = tris.begin (); t != tris.end (); ++t) { - for (int i = 0; i < 3; ++i) { - db::Vertex *v = t->vertex (i); - if (vset.insert (v).second) { - vertexes.push_back (v); - } - } - } - - while (! vertexes.empty ()) { - - unsigned int n = rand () % (unsigned int) vertexes.size (); - db::Vertex *v = vertexes [n]; - tris.remove (v); - vertexes.erase (vertexes.begin () + n); - - // just a few times as it wastes time otherwise - if (vertexes.size () % 10 == 0) { - EXPECT_EQ (tris.check (), true); - } - - } - - EXPECT_EQ (tris.num_triangles (), size_t (0)); - - } - - tl::info << tl::endl << "done."; -} - -TEST(ensure_edge) -{ - srand (0); - - TestableTriangles tris; - double res = 128.0; - - db::DEdge ee[] = { - db::DEdge (0.25, 0.25, 0.25, 0.75), - db::DEdge (0.25, 0.75, 0.75, 0.75), - db::DEdge (0.75, 0.75, 0.75, 0.25), - db::DEdge (0.75, 0.25, 0.25, 0.25) - }; - - for (unsigned int i = 0; i < 200; ++i) { - double x = round (flt_rand () * res) * (1.0 / res); - double y = round (flt_rand () * res) * (1.0 / res); - bool ok = true; - for (unsigned int j = 0; j < sizeof (ee) / sizeof (ee[0]); ++j) { - if (ee[j].side_of (db::DPoint (x, y)) == 0) { - --i; - ok = false; - } - } - if (ok) { - tris.insert_point (x, y); - } - } - - for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { - tris.insert_point (ee[i].p1 ()); - } - - EXPECT_EQ (tris.check (), true); - - for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { - tris.ensure_edge (tris.find_vertex_for_point (ee[i].p1 ()), tris.find_vertex_for_point (ee[i].p2 ())); - } - - EXPECT_EQ (tris.check (false), true); - - double area_in = 0.0; - db::DBox clip_box; - for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { - clip_box += ee[i].p1 (); - } - for (auto t = tris.begin (); t != tris.end (); ++t) { - if (clip_box.overlaps (t->bbox ())) { - EXPECT_EQ (t->bbox ().inside (clip_box), true); - area_in += t->area (); - } - } - - EXPECT_EQ (tl::to_string (area_in), "0.25"); -} - -static bool safe_inside (const db::DBox &b1, const db::DBox &b2) -{ - typedef db::coord_traits ct; - - return (ct::less (b2.left (), b1.left ()) || ct::equal (b2.left (), b1.left ())) && - (ct::less (b1.right (), b2.right ()) || ct::equal (b1.right (), b2.right ())) && - (ct::less (b2.bottom (), b1.bottom ()) || ct::equal (b2.bottom (), b1.bottom ())) && - (ct::less (b1.top (), b2.top ()) || ct::equal (b1.top (), b2.top ())); -} - -TEST(constrain) -{ - srand (0); - - TestableTriangles tris; - double res = 128.0; - - db::DEdge ee[] = { - db::DEdge (0.25, 0.25, 0.25, 0.75), - db::DEdge (0.25, 0.75, 0.75, 0.75), - db::DEdge (0.75, 0.75, 0.75, 0.25), - db::DEdge (0.75, 0.25, 0.25, 0.25) - }; - - for (unsigned int i = 0; i < 200; ++i) { - double x = round (flt_rand () * res) * (1.0 / res); - double y = round (flt_rand () * res) * (1.0 / res); - bool ok = true; - for (unsigned int j = 0; j < sizeof (ee) / sizeof (ee[0]); ++j) { - if (ee[j].side_of (db::DPoint (x, y)) == 0) { - --i; - ok = false; - } - } - if (ok) { - tris.insert_point (x, y); - } - } - - std::vector contour; - for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { - contour.push_back (tris.insert_point (ee[i].p1 ())); - } - std::vector > contours; - contours.push_back (contour); - - EXPECT_EQ (tris.check (), true); - - tris.constrain (contours); - EXPECT_EQ (tris.check (false), true); - - tris.remove_outside_triangles (); - - EXPECT_EQ (tris.check (), true); - - double area_in = 0.0; - db::DBox clip_box; - for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { - clip_box += ee[i].p1 (); - } - for (auto t = tris.begin (); t != tris.end (); ++t) { - EXPECT_EQ (clip_box.overlaps (t->bbox ()), true); - EXPECT_EQ (safe_inside (t->bbox (), clip_box), true); - area_in += t->area (); - } - - EXPECT_EQ (tl::to_string (area_in), "0.25"); -} - -TEST(heavy_constrain) -{ - tl::info << "Running test_heavy_constrain " << tl::noendl; - - for (unsigned int l = 0; l < 100; ++l) { - - srand (l); - tl::info << "." << tl::noendl; - - TestableTriangles tris; - double res = 128.0; - - db::DEdge ee[] = { - db::DEdge (0.25, 0.25, 0.25, 0.75), - db::DEdge (0.25, 0.75, 0.75, 0.75), - db::DEdge (0.75, 0.75, 0.75, 0.25), - db::DEdge (0.75, 0.25, 0.25, 0.25) - }; - - unsigned int n = rand () % 150 + 50; - - for (unsigned int i = 0; i < n; ++i) { - double x = round (flt_rand () * res) * (1.0 / res); - double y = round (flt_rand () * res) * (1.0 / res); - bool ok = true; - for (unsigned int j = 0; j < sizeof (ee) / sizeof (ee[0]); ++j) { - if (ee[j].side_of (db::DPoint (x, y)) == 0) { - --i; - ok = false; - } - } - if (ok) { - tris.insert_point (x, y); - } - } - - std::vector contour; - for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { - contour.push_back (tris.insert_point (ee[i].p1 ())); - } - std::vector > contours; - contours.push_back (contour); - - EXPECT_EQ (tris.check (), true); - - tris.constrain (contours); - EXPECT_EQ (tris.check (false), true); - - tris.remove_outside_triangles (); - - EXPECT_EQ (tris.check (), true); - - double area_in = 0.0; - db::DBox clip_box; - for (unsigned int i = 0; i < sizeof (ee) / sizeof (ee[0]); ++i) { - clip_box += ee[i].p1 (); - } - for (auto t = tris.begin (); t != tris.end (); ++t) { - EXPECT_EQ (clip_box.overlaps (t->bbox ()), true); - EXPECT_EQ (safe_inside (t->bbox (), clip_box), true); - area_in += t->area (); - } - - EXPECT_EQ (tl::to_string (area_in), "0.25"); - - } - - tl::info << tl::endl << "done."; -} - -TEST(heavy_find_point_around) -{ - tl::info << "Running Triangle_test_heavy_find_point_around " << tl::noendl; - - for (unsigned int l = 0; l < 100; ++l) { - - srand (l); - tl::info << "." << tl::noendl; - - TestableTriangles tris; - double res = 128.0; - - unsigned int n = rand () % 190 + 10; - - std::vector vertexes; - - for (unsigned int i = 0; i < n; ++i) { - double x = round (flt_rand () * res) * (1.0 / res); - double y = round (flt_rand () * res) * (1.0 / res); - vertexes.push_back (tris.insert_point (x, y)); - } - - EXPECT_EQ (tris.check(), true); - - for (int i = 0; i < 100; ++i) { - - unsigned int nv = rand () % (unsigned int) vertexes.size (); - auto vertex = vertexes [nv]; - - double r = round (flt_rand () * res) * (1.0 / res); - auto p1 = tris.find_points_around (vertex, r); - auto p2 = tris.find_inside_circle (*vertex, r); - - std::set sp1 (p1.begin (), p1.end ()); - std::set sp2 (p2.begin (), p2.end ()); - sp2.erase (vertex); - - EXPECT_EQ (sp1 == sp2, true); - - } - - } - - tl::info << tl::endl << "done."; -} - -TEST(create_constrained_delaunay) -{ - db::Region r; - r.insert (db::Box (0, 0, 1000, 1000)); - - db::Region r2; - r2.insert (db::Box (200, 200, 800, 800)); - - r -= r2; - - TestableTriangles tri; - tri.create_constrained_delaunay (r); - tri.remove_outside_triangles (); - - EXPECT_EQ (tri.check (), true); - - EXPECT_EQ (tri.to_string (), - "((1000, 0), (0, 0), (200, 200)), " - "((0, 1000), (200, 200), (0, 0)), " - "((1000, 0), (200, 200), (800, 200)), " - "((1000, 0), (800, 200), (1000, 1000)), " - "((800, 200), (800, 800), (1000, 1000)), " - "((0, 1000), (1000, 1000), (800, 800)), " - "((0, 1000), (800, 800), (200, 800)), " - "((0, 1000), (200, 800), (200, 200))"); -} - -TEST(triangulate_basic) -{ - db::Region r; - r.insert (db::Box (0, 0, 10000, 10000)); - - db::Region r2; - r2.insert (db::Box (2000, 2000, 8000, 8000)); - - r -= r2; - - db::Triangles::TriangulateParameters param; - param.min_b = 1.2; - param.max_area = 1.0; - - TestableTriangles tri; - tri.triangulate (r, param, 0.001); - - EXPECT_EQ (tri.check (), true); - - for (auto t = tri.begin (); t != tri.end (); ++t) { - EXPECT_LE (t->area (), param.max_area); - EXPECT_GE (t->b (), param.min_b); - } - - EXPECT_GT (tri.num_triangles (), size_t (100)); - EXPECT_LT (tri.num_triangles (), size_t (150)); - - // for debugging: - // tri.dump ("debug.gds"); - - param.min_b = 1.0; - param.max_area = 0.1; - - tri.triangulate (r, param, 0.001); - - EXPECT_EQ (tri.check (), true); - - for (auto t = tri.begin (); t != tri.end (); ++t) { - EXPECT_LE (t->area (), param.max_area); - EXPECT_GE (t->b (), param.min_b); - } - - EXPECT_GT (tri.num_triangles (), size_t (900)); - EXPECT_LT (tri.num_triangles (), size_t (1000)); -} - -void read_polygons (const std::string &path, db::Region ®ion, double dbu) -{ - tl::InputStream is (path); - tl::TextInputStream ti (is); - - unsigned int nvert = 0, nedges = 0; - - { - tl::Extractor ex (ti.get_line ().c_str ()); - ex.read (nvert); - ex.read (nedges); - } - - std::vector v; - auto dbu_trans = db::CplxTrans (dbu).inverted (); - for (unsigned int i = 0; i < nvert; ++i) { - double x = 0, y = 0; - tl::Extractor ex (ti.get_line ().c_str ()); - ex.read (x); - ex.read (y); - v.push_back (dbu_trans * db::DPoint (x, y)); - } - - unsigned int nstart = 0; - bool new_contour = true; - std::vector contour; - - for (unsigned int i = 0; i < nedges; ++i) { - - unsigned int n1 = 0, n2 = 0; - - tl::Extractor ex (ti.get_line ().c_str ()); - ex.read (n1); - ex.read (n2); - - if (new_contour) { - nstart = n1; - new_contour = false; - } - - contour.push_back (v[n1]); - - if (n2 == nstart) { - // finish contour - db::SimplePolygon sp; - sp.assign_hull (contour.begin (), contour.end ()); - region.insert (sp); - new_contour = true; - contour.clear (); - } else if (n2 <= n1) { - tl::error << "Invalid polygon wrap in line " << ti.line_number (); - tl_assert (false); - } - - } -} - -TEST(triangulate_geo) -{ - double dbu = 0.001; - - db::Region r; - read_polygons (tl::combine_path (tl::testsrc (), "testdata/algo/triangles1.txt"), r, dbu); - - // for debugging purposes dump the inputs - if (false) { - - db::Layout layout = db::Layout (); - layout.dbu (dbu); - db::Cell &top = layout.cell (layout.add_cell ("DUMP")); - unsigned int l1 = layout.insert_layer (db::LayerProperties (1, 0)); - r.insert_into (&layout, top.cell_index (), l1); - - { - tl::OutputStream stream ("input.gds"); - db::SaveLayoutOptions opt; - db::Writer writer (opt); - writer.write (layout, stream); - } - - } - - db::Triangles::TriangulateParameters param; - param.min_b = 1.0; - param.max_area = 0.1; - param.min_length = 0.001; - - TestableTriangles tri; - tri.triangulate (r, param, dbu); - - EXPECT_EQ (tri.check (false), true); - - // for debugging: - // tri.dump ("debug.gds"); - - size_t n_skinny = 0; - for (auto t = tri.begin (); t != tri.end (); ++t) { - EXPECT_LE (t->area (), param.max_area); - if (t->b () < param.min_b) { - ++n_skinny; - } - } - - EXPECT_LT (n_skinny, size_t (20)); - EXPECT_GT (tri.num_triangles (), size_t (29000)); - EXPECT_LT (tri.num_triangles (), size_t (30000)); -} - -TEST(triangulate_analytic) -{ - double dbu = 0.0001; - - double star1 = 9.0, star2 = 5.0; - double r = 1.0; - int n = 100; - - auto dbu_trans = db::CplxTrans (dbu).inverted (); - - std::vector contour1, contour2; - for (int i = 0; i < n; ++i) { - double a = -M_PI * 2.0 * double (i) / double (n); // "-" for clockwise orientation - double rr, x, y; - rr = r * (1.0 + 0.4 * cos (star1 * a)); - x = rr * cos (a); - y = rr * sin (a); - contour1.push_back (dbu_trans * db::DPoint (x, y)); - rr = r * (0.1 + 0.03 * cos (star2 * a)); - x = rr * cos (a); - y = rr * sin (a); - contour2.push_back (dbu_trans * db::DPoint (x, y)); - } - - db::Region rg; - - db::SimplePolygon sp1; - sp1.assign_hull (contour1.begin (), contour1.end ()); - db::SimplePolygon sp2; - sp2.assign_hull (contour2.begin (), contour2.end ()); - - rg = db::Region (sp1) - db::Region (sp2); - - db::Triangles::TriangulateParameters param; - param.min_b = 1.0; - param.max_area = 0.01; - - TestableTriangles tri; - tri.triangulate (rg, param, dbu); - - EXPECT_EQ (tri.check (false), true); - - // for debugging: - // tri.dump ("debug.gds"); - - for (auto t = tri.begin (); t != tri.end (); ++t) { - EXPECT_LE (t->area (), param.max_area); - EXPECT_GE (t->b (), param.min_b); - } - - EXPECT_GT (tri.num_triangles (), size_t (1250)); - EXPECT_LT (tri.num_triangles (), size_t (1300)); -} - -TEST(triangulate_problematic) -{ - db::DPoint contour[] = { - db::DPoint (129145.00000, -30060.80000), - db::DPoint (129145.00000, -28769.50000), - db::DPoint (129159.50000, -28754.90000), // this is a very short edge <-- from here. - db::DPoint (129159.60000, -28754.80000), // <-- to here. - db::DPoint (129159.50000, -28754.70000), - db::DPoint (129366.32200, -28547.90000), - db::DPoint (130958.54600, -26955.84600), - db::DPoint (131046.25000, -27043.55000), - db::DPoint (130152.15000, -27937.65000), - db::DPoint (130152.15000, -30060.80000) - }; - - db::DPolygon poly; - poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); - - db::Triangles::TriangulateParameters param; - param.min_b = 1.0; - param.max_area = 100000.0; - param.min_length = 0.002; - - TestableTriangles tri; - tri.triangulate (poly, param); - - EXPECT_EQ (tri.check (false), true); - - // for debugging: - // tri.dump ("debug.gds"); - - for (auto t = tri.begin (); t != tri.end (); ++t) { - EXPECT_LE (t->area (), param.max_area); - EXPECT_GE (t->b (), param.min_b); - } - - EXPECT_GT (tri.num_triangles (), size_t (540)); - EXPECT_LT (tri.num_triangles (), size_t (560)); -} - -TEST(triangulate_thin) -{ - db::DPoint contour[] = { - db::DPoint (18790, 58090), - db::DPoint (18790, 58940), - db::DPoint (29290, 58940), - db::DPoint (29290, 58090) - }; - - db::DPoint hole[] = { - db::DPoint (18791, 58091), - db::DPoint (29289, 58091), - db::DPoint (29289, 58939), - db::DPoint (18791, 58939) - }; - - db::DPolygon poly; - poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); - poly.insert_hole (hole + 0, hole + sizeof (hole) / sizeof (hole[0])); - - double dbu = 0.001; - - db::Triangles::TriangulateParameters param; - param.min_b = 0.5; - param.max_area = 0.0; - param.min_length = 2 * dbu; - - TestableTriangles tri; - db::DCplxTrans trans = db::DCplxTrans (dbu) * db::DCplxTrans (db::DTrans (db::DPoint () - poly.box ().center ())); - tri.triangulate (trans * poly, param); - - EXPECT_EQ (tri.check (false), true); - - // for debugging: - // tri.dump ("debug.gds"); - - for (auto t = tri.begin (); t != tri.end (); ++t) { - EXPECT_GE (t->b (), param.min_b); - } - - EXPECT_GT (tri.num_triangles (), size_t (13000)); - EXPECT_LT (tri.num_triangles (), size_t (13200)); -} - -TEST(triangulate_issue1996) -{ - db::DPoint contour[] = { - db::DPoint (-8000, -8075), - db::DPoint (-8000, 8075), - db::DPoint (18000, 8075), - db::DPoint (18000, -8075) - }; - - db::DPolygon poly; - poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); - - double dbu = 0.001; - - db::Triangles::TriangulateParameters param; - param.min_b = 0.5; - param.max_area = 5000.0 * dbu * dbu; - - TestableTriangles tri; - db::DCplxTrans trans = db::DCplxTrans (dbu) * db::DCplxTrans (db::DTrans (db::DPoint () - poly.box ().center ())); - tri.triangulate (trans * poly, param); - - EXPECT_EQ (tri.check (false), true); - - // for debugging: - // tri.dump ("debug.gds"); - - for (auto t = tri.begin (); t != tri.end (); ++t) { - EXPECT_LE (t->area (), param.max_area); - EXPECT_GE (t->b (), param.min_b); - } - - EXPECT_GT (tri.num_triangles (), size_t (128000)); - EXPECT_LT (tri.num_triangles (), size_t (132000)); -} - -TEST(triangulate_with_vertexes) -{ - db::Point contour[] = { - db::Point (0, 0), - db::Point (0, 100), - db::Point (1000, 100), - db::Point (1000, 0) - }; - - db::Polygon poly; - poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); - - double dbu = 0.001; - - db::Triangles::TriangulateParameters param; - param.min_b = 0.0; - param.max_area = 0.0; - - std::vector vertexes; - - TestableTriangles tri; - db::CplxTrans trans = db::DCplxTrans (dbu) * db::CplxTrans (db::Trans (db::Point () - poly.box ().center ())); - tri.triangulate (poly, param, trans); - - EXPECT_EQ (tri.to_string (), "((-0.5, -0.05), (-0.5, 0.05), (0.5, 0.05)), ((0.5, -0.05), (-0.5, -0.05), (0.5, 0.05))"); - - vertexes.clear (); - - // outside vertexes are ignored, but lead to a different triangulation - vertexes.push_back (db::Point (50, 150)); - tri.triangulate (poly, vertexes, param, trans); - - EXPECT_EQ (tri.to_string (), "((-0.5, -0.05), (-0.133333333333, 0.05), (0.5, -0.05)), ((0.5, 0.05), (0.5, -0.05), (-0.133333333333, 0.05)), ((-0.133333333333, 0.05), (-0.5, -0.05), (-0.5, 0.05))"); - - for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { - auto *vp = tri.find_vertex_for_point (trans * *v); - EXPECT_EQ (vp, 0); - } - - vertexes.clear (); - vertexes.push_back (db::Point (50, 50)); - tri.triangulate (poly, vertexes, param, trans); - - EXPECT_EQ (tri.to_string (), "((-0.45, 0), (-0.5, -0.05), (-0.5, 0.05)), ((0.5, 0.05), (-0.45, 0), (-0.5, 0.05)), ((-0.45, 0), (0.5, -0.05), (-0.5, -0.05)), ((-0.45, 0), (0.5, 0.05), (0.5, -0.05))"); - - for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { - auto *vp = tri.find_vertex_for_point (trans * *v); - if (! vp) { - tl::warn << "Vertex not present in output: " << v->to_string (); - EXPECT_EQ (1, 0); - } - } - - // aggressive triangulation - param.min_b = 1.0; - param.max_area = 20 * 20 * dbu * dbu; - - tri.triangulate (poly, vertexes, param, trans); - - EXPECT_GT (tri.num_triangles (), size_t (380)); - EXPECT_LT (tri.num_triangles (), size_t (400)); - - for (auto t = tri.begin (); t != tri.end (); ++t) { - EXPECT_LE (t->area (), param.max_area); - EXPECT_GE (t->b (), param.min_b); - } - - for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { - auto *vp = tri.find_vertex_for_point (trans * *v); - if (! vp) { - tl::warn << "Vertex not present in output: " << v->to_string (); - EXPECT_EQ (1, 0); - } - } -} - -// @@@@@@@@@@@ - -struct SortAngleAndEdgesByEdgeLength -{ - typedef std::list > angle_and_edges_list; - - bool operator() (const angle_and_edges_list::iterator &a, const angle_and_edges_list::iterator &b) const - { - double la = a->second->edge ().double_sq_length (); - double lb = b->second->edge ().double_sq_length (); - if (fabs (la - lb) > db::epsilon) { - return la < lb; - } else { - return a->second->edge ().less (b->second->edge ()); - } - } -}; - -// TODO: move to some generic header -template -struct less_compare_func -{ - bool operator() (const T &a, const T &b) const - { - return a.less (b); - } -}; - -// TODO: move to some generic header -template -struct equal_compare_func -{ - bool operator() (const T &a, const T &b) const - { - return a.equal (b); - } -}; - -struct ConcaveCorner -{ - ConcaveCorner () - : corner (0), incoming (0), outgoing (0) - { - // .. nothing yet .. - } - - ConcaveCorner (db::Vertex *_corner, db::TriangleEdge *_incoming, db::TriangleEdge *_outgoing) - : corner (_corner), incoming (_incoming), outgoing (_outgoing) - { - // .. nothing yet .. - } - - db::Vertex *corner; - db::TriangleEdge *incoming, *outgoing; -}; - -db::TriangleEdge *find_outgoing_segment (db::Vertex *vertex, db::TriangleEdge *incoming, int &vp_max_sign) -{ - db::Vertex *vfrom = incoming->other (vertex); - db::DEdge e1 (*vfrom, *vertex); - - double vp_max = 0.0; - vp_max_sign = 0; - db::TriangleEdge *outgoing = 0; - - // Look for the outgoing edge. We pick the one which bends "most", favoring - // convex corners. Multiple edges per vertex are possible is corner cases such as the - // "hourglass" configuration. - - for (auto e = vertex->begin_edges (); e != vertex->end_edges (); ++e) { - - db::TriangleEdge *en = *e; - if (en != incoming && en->is_segment ()) { - - db::Vertex *v = en->other (vertex); - db::DEdge e2 (*vertex, *v); - double vp = double (db::vprod (e1, e2)) / (e1.double_length () * e2.double_length ()); - - // vp > 0: concave, vp < 0: convex - - if (! outgoing || vp > vp_max) { - vp_max_sign = db::vprod_sign (e1, e2); - vp_max = vp; - outgoing = en; - } - - } - - } - - tl_assert (outgoing != 0); - return outgoing; -} - -void collect_concave_vertexes (db::Triangles &tris, std::vector &concave_vertexes) -{ - concave_vertexes.clear (); - - // @@@ use edge "level" - // @@@ use edges from heap - std::unordered_set left; - - for (auto it = tris.begin (); it != tris.end (); ++it) { - for (unsigned int i = 0; i < 3; ++i) { - db::TriangleEdge *e = it->edge (i); - if (e->is_segment ()) { - left.insert (e); - } - } - } - - while (! left.empty ()) { - - // First segment for a new loop - db::TriangleEdge *segment = *left.begin (); - - // walk along the segments in clockwise direction. Find concave - // vertexes and create new vertexes perpendicular to the incoming - // and outgoing edge. - - db::TriangleEdge *start_segment = segment; - db::Vertex *vto = segment->right () ? segment->v2 () : segment->v1 (); - - do { - - left.erase (segment); - - db::TriangleEdge *prev_segment = segment; - - int vp_sign = 0; - segment = find_outgoing_segment (vto, prev_segment, vp_sign); - - if (vp_sign > 0) { - concave_vertexes.push_back (ConcaveCorner (vto, prev_segment, segment)); - } - - vto = segment->other (vto); - - } while (segment != start_segment); - - } -} - -std::pair -search_crossing_with_next_segment (const db::Vertex *v0, const db::DVector &direction) -{ - auto vtri = v0->triangles (); // TODO: slow? - std::vector nvv, nvv_next; - - for (auto it = vtri.begin (); it != vtri.end (); ++it) { - - // Search for a segment in the direction perpendicular to the edge - nvv.clear (); - nvv.push_back (v0); - const db::Triangle *t = *it; - - while (! nvv.empty ()) { - - nvv_next.clear (); - - for (auto iv = nvv.begin (); iv != nvv.end (); ++iv) { - - const db::Vertex *v = *iv; - const db::TriangleEdge *oe = t->opposite (v); - const db::Triangle *tt = oe->other (t); - const db::Vertex *v1 = oe->v1 (); - const db::Vertex *v2 = oe->v2 (); - - if (db::sprod_sign (*v2 - *v, direction) >= 0 && db::sprod_sign (*v1 - *v, direction) >= 0 && - db::vprod_sign (*v2 - *v, direction) * db::vprod_sign (*v1 - *v, direction) < 0) { - - // this triangle covers the normal vector of e1 -> stop here or continue searching in that direction - if (oe->is_segment ()) { - auto cp = oe->edge ().cut_point (db::DEdge (*v0, *v0 + direction)); - if (cp.first) { - return std::make_pair (true, cp.second); - } - } else { - // continue searching in that direction - nvv_next.push_back (v1); - nvv_next.push_back (v2); - t = tt; - } - - break; - - } - - } - - nvv.swap (nvv_next); - - } - - } - - return std::make_pair (false, db::DPoint ()); -} - -void -hertel_mehlhorn_decomposition (db::Triangles &tri, bool diagonals_only, bool no_collinear_edges, std::list &polygons) -{ - std::vector concave_vertexes; - collect_concave_vertexes (tri, concave_vertexes); - - // @@@ return if no concave corners - - // @@@ sort concave vertexes - - std::vector new_points; - - if (! diagonals_only) { - - // Create internal segments cutting off pieces orthogonal to the edges - // connecting the concave vertex. - - for (auto cc = concave_vertexes.begin (); cc != concave_vertexes.end (); ++cc) { - - for (unsigned int ei = 0; ei < 2; ++ei) { - - db::DEdge ee; - const db::Vertex *v0 = cc->corner; - if (ei == 0) { - ee = db::DEdge (*cc->incoming->other (v0), *v0); - } else { - ee = db::DEdge (*v0, *cc->outgoing->other (v0)); - } - - auto cp = search_crossing_with_next_segment (v0, db::DVector (ee.dy (), -ee.dx ())); - if (cp.first) { - new_points.push_back (cp.second); - } - - } - - } - - } - - // eliminate duplicates and put the new points in some order - - if (! new_points.empty ()) { - - std::sort (new_points.begin (), new_points.end (), less_compare_func ()); - new_points.erase (std::unique (new_points.begin (), new_points.end (), equal_compare_func ()), new_points.end ()); - - // Insert the new points and make connections - for (auto p = new_points.begin (); p != new_points.end (); ++p) { - tri.insert_point (*p); - } - - // As the insertion invalidates the edges, we need to collect the concave vertexes again - collect_concave_vertexes (tri, concave_vertexes); - - } - - // Collect essential edges - // Every concave vertex can have up to two essential edges. - // Other then suggested by Hertel-Mehlhorn we don't pick - // them one-by-one, but using them in length order, from the - - std::unordered_set essential_edges; - - typedef std::list > angles_and_edges_list; - angles_and_edges_list angles_and_edges; - std::vector sorted_edges; - - for (auto cc = concave_vertexes.begin (); cc != concave_vertexes.end (); ++cc) { - - angles_and_edges.clear (); - const db::Vertex *v0 = cc->corner; - - const db::TriangleEdge *e = cc->incoming; - while (e) { - - const db::Triangle *t = e->v2 () == v0 ? e->right () : e->left (); - tl_assert (t != 0); - - // @@@ make a method of triangle - const db::TriangleEdge *en = 0; - for (unsigned int i = 0; i < 3; ++i) { - const db::TriangleEdge *ee = t->edge (i); - if (ee != e && (ee->v1 () == v0 || ee->v2 () == v0)) { - en = ee; - break; - } - } - - db::DVector v1 = e->edge ().d () * (e->v1 () == v0 ? 1.0 : -1.0); - db::DVector v2 = en->edge ().d () * (en->v1 () == v0 ? 1.0 : -1.0); - - double angle = atan2 (db::vprod (v1, v2), db::sprod (v1, v2)); - - e = (en == cc->outgoing) ? 0 : en; - angles_and_edges.push_back (std::make_pair (angle, e)); - - } - - sorted_edges.clear (); - - for (auto i = angles_and_edges.begin (); i != angles_and_edges.end (); ++i) { - if (i->second) { - sorted_edges.push_back (i); - } - } - - std::sort (sorted_edges.begin (), sorted_edges.end (), SortAngleAndEdgesByEdgeLength ()); - - for (auto i = sorted_edges.end (); i != sorted_edges.begin (); ) { - --i; - angles_and_edges_list::iterator ii = *i; - angles_and_edges_list::iterator iin = ii; - ++iin; - if (ii->first + iin->first < (no_collinear_edges ? M_PI - db::epsilon : M_PI + db::epsilon)) { - // not an essential edge -> remove - iin->first += ii->first; - angles_and_edges.erase (ii); - } - } - - for (auto i = angles_and_edges.begin (); i != angles_and_edges.end (); ++i) { - essential_edges.insert (i->second); - } - - } - - // Combine triangles, but don't cross essential edges - - std::unordered_set left_triangles; - for (auto it = tri.begin (); it != tri.end (); ++it) { - left_triangles.insert (it.operator-> ()); - } - - while (! left_triangles.empty ()) { - - std::unordered_map edges; - - const db::Triangle *tri = *left_triangles.begin (); - std::vector queue, next_queue; - queue.push_back (tri); - - while (! queue.empty ()) { - - next_queue.clear (); - - for (auto q = queue.begin (); q != queue.end (); ++q) { - - left_triangles.erase (*q); - - for (unsigned int i = 0; i < 3; ++i) { - - const db::TriangleEdge *e = (*q)->edge (i); - const db::Triangle *qq = e->other (*q); - - if (! qq || essential_edges.find (e) != essential_edges.end ()) { - if (e->right () == *q) { - edges.insert (std::make_pair (e->v1 (), e->v2 ())); - } else { - edges.insert (std::make_pair (e->v2 (), e->v1 ())); - } - } else if (left_triangles.find (qq) != left_triangles.end ()) { - next_queue.push_back (qq); - } - } - - } - - queue.swap (next_queue); - - } - - // stitch the loop points into a polygon - - tl_assert (! edges.empty ()); - - const db::Vertex *v = edges.begin ()->first; - const db::Vertex *v0 = v; - const db::Vertex *vv = edges.begin ()->second; - - std::vector polygon_points; - - do { - - polygon_points.push_back (*v); - - auto i = edges.find (vv); - tl_assert (i != edges.end ()); - - v = i->first; - vv = i->second; - - } while (v != v0); - - polygons.push_back (db::DPolygon ()); - polygons.back ().assign_hull (polygon_points.begin (), polygon_points.end ()); - - } -} - - -// Hertel-Mehlhorn :) -TEST(JoinTriangles) -{ -#if 0 - db::Point contour[] = { - db::Point (0, 0), - db::Point (0, 100), - db::Point (1000, 100), - db::Point (1000, 500), - db::Point (1100, 500), - db::Point (1100, 100), - db::Point (2100, 100), - db::Point (2100, 0) - }; -#else - - db::Point contour[] = { - db::Point (0, 0), - db::Point (0, 100), - db::Point (1000, 100), - db::Point (1000, 500), - db::Point (1100, 500), - db::Point (1100, 100), - db::Point (2100, 100), - db::Point (2100, -1000), - db::Point (150, -1000), - db::Point (150, 0) - }; -#endif - - db::Polygon poly; - poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); - - // @@@ don't to anything if already convex - - double dbu = 0.001; - - db::Triangles::TriangulateParameters param; - param.min_b = 0.0; - - TestableTriangles tri; - db::CplxTrans trans = db::CplxTrans (dbu); - tri.triangulate (poly, param, trans); - - std::list polygons; - hertel_mehlhorn_decomposition (tri, false, true, polygons); - - db::Region result; - for (auto p = polygons.begin (); p != polygons.end (); ++p) { - result.insert (trans.inverted () * *p); - } - - // @@@ - tri.dump ("debugt.gds"); - result.write ("debug.gds"); - -} - -// @@@@@@@@@@q diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index f16d29b44..b5aadac53 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -20,8 +20,6 @@ SOURCES = \ dbQuadTreeTests.cc \ dbRecursiveInstanceIteratorTests.cc \ dbRegionCheckUtilsTests.cc \ - dbTriangleTests.cc \ - dbTrianglesTests.cc \ dbUtilsTests.cc \ dbWriterTools.cc \ dbLoadLayoutOptionsTests.cc \ From 60d1fb0685cce65d1f9c8d42eefc46aca7d7d443 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 16 Apr 2025 00:02:25 +0200 Subject: [PATCH 139/392] Porting HM decomposition to new PLC framework --- src/db/db/db.pro | 2 + src/db/db/dbPLC.cc | 103 +--- src/db/db/dbPLC.h | 1 + src/db/db/dbPLCConvexDecomposition.cc | 530 ++++++++++++++++++ src/db/db/dbPLCConvexDecomposition.h | 132 +++++ src/db/db/dbPLCTriangulation.cc | 3 - .../dbPLCConvexDecompositionTests.cc | 112 ++++ src/db/unit_tests/unit_tests.pro | 1 + testdata/algo/hm_decomposition_au1.gds | Bin 0 -> 1636 bytes testdata/algo/hm_decomposition_au2.gds | Bin 0 -> 1922 bytes testdata/algo/hm_decomposition_au3.gds | Bin 0 -> 1460 bytes testdata/algo/hm_decomposition_au4.gds | Bin 0 -> 1786 bytes 12 files changed, 780 insertions(+), 104 deletions(-) create mode 100644 src/db/db/dbPLCConvexDecomposition.cc create mode 100644 src/db/db/dbPLCConvexDecomposition.h create mode 100644 src/db/unit_tests/dbPLCConvexDecompositionTests.cc create mode 100644 testdata/algo/hm_decomposition_au1.gds create mode 100644 testdata/algo/hm_decomposition_au2.gds create mode 100644 testdata/algo/hm_decomposition_au3.gds create mode 100644 testdata/algo/hm_decomposition_au4.gds diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 93795a97d..295976015 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -71,6 +71,7 @@ SOURCES = \ dbObject.cc \ dbObjectWithProperties.cc \ dbPLC.cc \ + dbPLCConvexDecomposition.cc \ dbPLCTriangulation.cc \ dbPath.cc \ dbPCellDeclaration.cc \ @@ -309,6 +310,7 @@ HEADERS = \ dbObjectTag.h \ dbObjectWithProperties.h \ dbPLC.h \ + dbPLCConvexDecomposition.h \ dbPLCTriangulation.h \ dbPath.h \ dbPCellDeclaration.h \ diff --git a/src/db/db/dbPLC.cc b/src/db/db/dbPLC.cc index 559bf4572..f9572550e 100644 --- a/src/db/db/dbPLC.cc +++ b/src/db/db/dbPLC.cc @@ -804,105 +804,6 @@ Graph::bbox () const return box; } -#if 0 // @@@ -bool -Graph::check (bool check_delaunay) const -{ - bool res = true; - - if (check_delaunay) { - for (auto t = mp_polygons.begin (); t != mp_polygons.end (); ++t) { - auto cp = t->circumcircle (); - auto vi = find_inside_circle (cp.first, cp.second); - if (! vi.empty ()) { - res = false; - tl::error << "(check error) polygon does not meet Delaunay criterion: " << t->to_string (); - for (auto v = vi.begin (); v != vi.end (); ++v) { - tl::error << " vertex inside circumcircle: " << (*v)->to_string (true); - } - } - } - } - - for (auto t = mp_polygons.begin (); t != mp_polygons.end (); ++t) { - for (int i = 0; i < 3; ++i) { - if (! t->edge (i)->has_polygon (t.operator-> ())) { - tl::error << "(check error) edges " << t->edge (i)->to_string (true) - << " attached to polygon " << t->to_string (true) << " does not refer to this polygon"; - res = false; - } - } - } - - for (auto e = m_edges_heap.begin (); e != m_edges_heap.end (); ++e) { - - if (!e->left () && !e->right ()) { - continue; - } - - if (e->left () && e->right ()) { - if (e->left ()->is_outside () != e->right ()->is_outside () && ! e->is_segment ()) { - tl::error << "(check error) edge " << e->to_string (true) << " splits an outside and inside polygon, but is not a segment"; - res = false; - } - } - - for (auto t = e->begin_polygons (); t != e->end_polygons (); ++t) { - if (! t->has_edge (e.operator-> ())) { - tl::error << "(check error) edge " << e->to_string (true) << " not found in adjacent polygon " << t->to_string (true); - res = false; - } - if (! t->has_vertex (e->v1 ())) { - tl::error << "(check error) edges " << e->to_string (true) << " vertex 1 not found in adjacent polygon " << t->to_string (true); - res = false; - } - if (! t->has_vertex (e->v2 ())) { - tl::error << "(check error) edges " << e->to_string (true) << " vertex 2 not found in adjacent polygon " << t->to_string (true); - res = false; - } - Vertex *vopp = t->opposite (e.operator-> ()); - double sgn = (e->left () == t.operator-> ()) ? 1.0 : -1.0; - double vp = db::vprod (e->d(), *vopp - *e->v1 ()); // positive if on left side - if (vp * sgn <= 0.0) { - const char * side_str = sgn > 0.0 ? "left" : "right"; - tl::error << "(check error) external point " << vopp->to_string (true) << " not on " << side_str << " side of edge " << e->to_string (true); - res = false; - } - } - - if (! e->v1 ()->has_edge (e.operator-> ())) { - tl::error << "(check error) edge " << e->to_string (true) << " vertex 1 does not list this edge"; - res = false; - } - if (! e->v2 ()->has_edge (e.operator-> ())) { - tl::error << "(check error) edge " << e->to_string (true) << " vertex 2 does not list this edge"; - res = false; - } - - } - - for (auto v = m_vertex_heap.begin (); v != m_vertex_heap.end (); ++v) { - unsigned int num_outside_edges = 0; - for (auto e = v->begin_edges (); e != v->end_edges (); ++e) { - if ((*e)->is_outside ()) { - ++num_outside_edges; - } - } - if (num_outside_edges > 0 && num_outside_edges != 2) { - tl::error << "(check error) vertex " << v->to_string (true) << " has " << num_outside_edges << " outside edges (can only be 2)"; - res = false; - for (auto e = v->begin_edges (); e != v->end_edges (); ++e) { - if ((*e)->is_outside ()) { - tl::error << " Outside edge is " << (*e)->to_string (true); - } - } - } - } - - return res; -} -#endif - db::Layout * Graph::to_layout (bool decompose_by_id) const { @@ -913,7 +814,7 @@ Graph::to_layout (bool decompose_by_id) const db::Cell &top = layout->cell (layout->add_cell ("DUMP")); unsigned int l1 = layout->insert_layer (db::LayerProperties (1, 0)); - // @@@ unsigned int l2 = layout->insert_layer (db::LayerProperties (2, 0)); + unsigned int l2 = layout->insert_layer (db::LayerProperties (2, 0)); unsigned int l10 = layout->insert_layer (db::LayerProperties (10, 0)); unsigned int l20 = layout->insert_layer (db::LayerProperties (20, 0)); unsigned int l21 = layout->insert_layer (db::LayerProperties (21, 0)); @@ -927,7 +828,7 @@ Graph::to_layout (bool decompose_by_id) const } db::DPolygon poly; poly.assign_hull (pts.begin (), pts.end ()); - top.shapes (/*@@@t->is_outside () ? l2 :*/ l1).insert (dbu_trans * poly); + top.shapes (t->is_outside () ? l2 : l1).insert (dbu_trans * poly); if (decompose_by_id) { if ((t->id () & 1) != 0) { top.shapes (l20).insert (dbu_trans * poly); diff --git a/src/db/db/dbPLC.h b/src/db/db/dbPLC.h index 14ac8d01c..d5d6f0a26 100644 --- a/src/db/db/dbPLC.h +++ b/src/db/db/dbPLC.h @@ -504,6 +504,7 @@ private: friend class Polygon; friend class Graph; friend class Triangulation; + friend class ConvexDecomposition; Graph *mp_graph; Vertex *mp_v1, *mp_v2; diff --git a/src/db/db/dbPLCConvexDecomposition.cc b/src/db/db/dbPLCConvexDecomposition.cc new file mode 100644 index 000000000..1905b27db --- /dev/null +++ b/src/db/db/dbPLCConvexDecomposition.cc @@ -0,0 +1,530 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbPLCConvexDecomposition.h" +#include "dbPLCTriangulation.h" +#include "tlLog.h" +#include "tlTimer.h" + +#include +#include +#include +#include + +namespace db +{ + +namespace plc +{ + +ConvexDecomposition::ConvexDecomposition (Graph *graph) +{ + mp_graph = graph; + clear (); +} + +void +ConvexDecomposition::clear () +{ + mp_graph->clear (); +} + +struct SortAngleAndEdgesByEdgeLength +{ + typedef std::list > angle_and_edges_list; + + bool operator() (const angle_and_edges_list::iterator &a, const angle_and_edges_list::iterator &b) const + { + double la = a->second->edge ().double_sq_length (); + double lb = b->second->edge ().double_sq_length (); + if (fabs (la - lb) > db::epsilon) { + return la < lb; + } else { + return a->second->edge ().less (b->second->edge ()); + } + } +}; + +// TODO: move to some generic header +template +struct less_compare_func +{ + bool operator() (const T &a, const T &b) const + { + return a.less (b); + } +}; + +// TODO: move to some generic header +template +struct equal_compare_func +{ + bool operator() (const T &a, const T &b) const + { + return a.equal (b); + } +}; + +struct ConcaveCorner +{ + ConcaveCorner () + : corner (0), incoming (0), outgoing (0) + { + // .. nothing yet .. + } + + ConcaveCorner (Vertex *_corner, Edge *_incoming, Edge *_outgoing) + : corner (_corner), incoming (_incoming), outgoing (_outgoing) + { + // .. nothing yet .. + } + + Vertex *corner; + Edge *incoming, *outgoing; +}; + +Edge *find_outgoing_segment (Vertex *vertex, Edge *incoming, int &vp_max_sign) +{ + Vertex *vfrom = incoming->other (vertex); + db::DEdge e1 (*vfrom, *vertex); + + double vp_max = 0.0; + vp_max_sign = 0; + Edge *outgoing = 0; + + // Look for the outgoing edge. We pick the one which bends "most", favoring + // convex corners. Multiple edges per vertex are possible is corner cases such as the + // "hourglass" configuration. + + for (auto e = vertex->begin_edges (); e != vertex->end_edges (); ++e) { + + Edge *en = *e; + if (en != incoming && en->is_segment ()) { + + Vertex *v = en->other (vertex); + db::DEdge e2 (*vertex, *v); + double vp = double (db::vprod (e1, e2)) / (e1.double_length () * e2.double_length ()); + + // vp > 0: concave, vp < 0: convex + + if (! outgoing || vp > vp_max) { + vp_max_sign = db::vprod_sign (e1, e2); + vp_max = vp; + outgoing = en; + } + + } + + } + + tl_assert (outgoing != 0); + return outgoing; +} + +void collect_concave_vertexes (Graph &tris, std::vector &concave_vertexes) +{ + concave_vertexes.clear (); + + // @@@ use edge "level" + // @@@ use edges from heap + std::unordered_set left; + + for (auto it = tris.begin (); it != tris.end (); ++it) { + for (unsigned int i = 0; i < 3; ++i) { + Edge *e = it->edge (i); + if (e->is_segment ()) { + left.insert (e); + } + } + } + + while (! left.empty ()) { + + // First segment for a new loop + Edge *segment = *left.begin (); + + // walk along the segments in clockwise direction. Find concave + // vertexes and create new vertexes perpendicular to the incoming + // and outgoing edge. + + Edge *start_segment = segment; + Vertex *vto = segment->right () ? segment->v2 () : segment->v1 (); + + do { + + left.erase (segment); + + Edge *prev_segment = segment; + + int vp_sign = 0; + segment = find_outgoing_segment (vto, prev_segment, vp_sign); + + if (vp_sign > 0) { + concave_vertexes.push_back (ConcaveCorner (vto, prev_segment, segment)); + } + + vto = segment->other (vto); + + } while (segment != start_segment); + + } +} + +std::pair +search_crossing_with_next_segment (const Vertex *v0, const db::DVector &direction) +{ + auto vtri = v0->polygons (); // TODO: slow? + std::vector nvv, nvv_next; + + for (auto it = vtri.begin (); it != vtri.end (); ++it) { + + // Search for a segment in the direction perpendicular to the edge + nvv.clear (); + nvv.push_back (v0); + const Polygon *t = *it; + + while (! nvv.empty ()) { + + nvv_next.clear (); + + for (auto iv = nvv.begin (); iv != nvv.end (); ++iv) { + + const Vertex *v = *iv; + const Edge *oe = t->opposite (v); + const Polygon *tt = oe->other (t); + const Vertex *v1 = oe->v1 (); + const Vertex *v2 = oe->v2 (); + + if (db::sprod_sign (*v2 - *v, direction) >= 0 && db::sprod_sign (*v1 - *v, direction) >= 0 && + db::vprod_sign (*v2 - *v, direction) * db::vprod_sign (*v1 - *v, direction) < 0) { + + // this triangle covers the normal vector of e1 -> stop here or continue searching in that direction + if (oe->is_segment ()) { + auto cp = oe->edge ().cut_point (db::DEdge (*v0, *v0 + direction)); + if (cp.first) { + return std::make_pair (true, cp.second); + } + } else { + // continue searching in that direction + nvv_next.push_back (v1); + nvv_next.push_back (v2); + t = tt; + } + + break; + + } + + } + + nvv.swap (nvv_next); + + } + + } + + return std::make_pair (false, db::DPoint ()); +} + +void +ConvexDecomposition::hertel_mehlhorn_decomposition (Triangulation &tris, const ConvexDecompositionParameters ¶m) +{ + bool with_segments = param.with_segments; + bool split_edges = param.split_edges; + + std::vector concave_vertexes; + collect_concave_vertexes (*mp_graph, concave_vertexes); + + // @@@ return if no concave corners + + // @@@ sort concave vertexes + + std::vector new_points; + + if (with_segments) { + + // Create internal segments cutting off pieces orthogonal to the edges + // connecting the concave vertex. + + for (auto cc = concave_vertexes.begin (); cc != concave_vertexes.end (); ++cc) { + + for (unsigned int ei = 0; ei < 2; ++ei) { + + db::DEdge ee; + const Vertex *v0 = cc->corner; + if (ei == 0) { + ee = db::DEdge (*cc->incoming->other (v0), *v0); + } else { + ee = db::DEdge (*v0, *cc->outgoing->other (v0)); + } + + auto cp = search_crossing_with_next_segment (v0, db::DVector (ee.dy (), -ee.dx ())); + if (cp.first) { + new_points.push_back (cp.second); + } + + } + + } + + } + + // eliminate duplicates and put the new points in some order + + if (! new_points.empty ()) { + + std::sort (new_points.begin (), new_points.end (), less_compare_func ()); + new_points.erase (std::unique (new_points.begin (), new_points.end (), equal_compare_func ()), new_points.end ()); + + // Insert the new points and make connections + for (auto p = new_points.begin (); p != new_points.end (); ++p) { + tris.insert_point (*p); + } + + // As the insertion invalidates the edges, we need to collect the concave vertexes again + collect_concave_vertexes (*mp_graph, concave_vertexes); + + } + + // Collect essential edges + // Every concave vertex can have up to two essential edges. + // Other then suggested by Hertel-Mehlhorn we don't pick + // them one-by-one, but using them in length order, from the + + std::unordered_set essential_edges; + + typedef std::list > angles_and_edges_list; + angles_and_edges_list angles_and_edges; + std::vector sorted_edges; + + for (auto cc = concave_vertexes.begin (); cc != concave_vertexes.end (); ++cc) { + + angles_and_edges.clear (); + const Vertex *v0 = cc->corner; + + const Edge *e = cc->incoming; + while (e) { + + const Polygon *t = e->v2 () == v0 ? e->right () : e->left (); + tl_assert (t != 0); + + // @@@ make a method of Polygon -> next_edge(Edge *e, Vertex *at) + const Edge *en = 0; + for (unsigned int i = 0; i < 3; ++i) { + const Edge *ee = t->edge (i); + if (ee != e && (ee->v1 () == v0 || ee->v2 () == v0)) { + en = ee; + break; + } + } + + db::DVector v1 = e->edge ().d () * (e->v1 () == v0 ? 1.0 : -1.0); + db::DVector v2 = en->edge ().d () * (en->v1 () == v0 ? 1.0 : -1.0); + + double angle = atan2 (db::vprod (v1, v2), db::sprod (v1, v2)); + + e = (en == cc->outgoing) ? 0 : en; + angles_and_edges.push_back (std::make_pair (angle, e)); + + } + + sorted_edges.clear (); + + for (auto i = angles_and_edges.begin (); i != angles_and_edges.end (); ++i) { + if (i->second) { + sorted_edges.push_back (i); + } + } + + std::sort (sorted_edges.begin (), sorted_edges.end (), SortAngleAndEdgesByEdgeLength ()); + + for (auto i = sorted_edges.end (); i != sorted_edges.begin (); ) { + --i; + angles_and_edges_list::iterator ii = *i; + angles_and_edges_list::iterator iin = ii; + ++iin; + if (ii->first + iin->first < (split_edges ? M_PI + db::epsilon : M_PI - db::epsilon)) { + // not an essential edge -> remove + iin->first += ii->first; + angles_and_edges.erase (ii); + } + } + + for (auto i = angles_and_edges.begin (); i != angles_and_edges.end (); ++i) { + essential_edges.insert (i->second); + } + + } + + // Combine triangles, but don't cross essential edges + + std::unordered_set left_triangles; + for (auto it = mp_graph->begin (); it != mp_graph->end (); ++it) { + left_triangles.insert (it.operator-> ()); + } + + std::list > polygons; + + while (! left_triangles.empty ()) { + + polygons.push_back (std::vector ()); + std::vector &edges = polygons.back (); + + const Polygon *tri = *left_triangles.begin (); + std::vector queue, next_queue; + queue.push_back (tri); + + while (! queue.empty ()) { + + next_queue.clear (); + + for (auto q = queue.begin (); q != queue.end (); ++q) { + + left_triangles.erase (*q); + + for (unsigned int i = 0; i < 3; ++i) { + + const Edge *e = (*q)->edge (i); + const Polygon *qq = e->other (*q); + + if (! qq || essential_edges.find (e) != essential_edges.end ()) { + edges.push_back (const_cast (e)); // @@@ const_cast + } else if (left_triangles.find (qq) != left_triangles.end ()) { + next_queue.push_back (qq); + } + } + + } + + queue.swap (next_queue); + + } + + } + + // remove the triangles + + while (mp_graph->begin () != mp_graph->end ()) { + delete mp_graph->begin ().operator-> (); + } + + // create the polygons + + for (auto p = polygons.begin (); p != polygons.end (); ++p) { + mp_graph->create_polygon (p->begin (), p->end ()); + } +} + +void +ConvexDecomposition::decompose (const db::Polygon &poly, const ConvexDecompositionParameters ¶meters, double dbu) +{ + decompose (poly, parameters, db::CplxTrans (dbu)); +} + +void +ConvexDecomposition::decompose (const db::Polygon &poly, const std::vector &vertexes, const ConvexDecompositionParameters ¶meters, double dbu) +{ + decompose (poly, vertexes, parameters, db::CplxTrans (dbu)); +} + +void +ConvexDecomposition::decompose (const db::Polygon &poly, const ConvexDecompositionParameters ¶meters, const db::CplxTrans &trans) +{ + // start with a triangulation + TriangulationParameters param; + param.max_area = 0.0; + param.min_b = 0.0; + + Triangulation tri (mp_graph); + tri.triangulate (poly, param, trans); + + hertel_mehlhorn_decomposition (tri, parameters); +} + +void +ConvexDecomposition::decompose (const db::Polygon &poly, const std::vector &vertexes, const ConvexDecompositionParameters ¶meters, const db::CplxTrans &trans) +{ + // start with a triangulation + TriangulationParameters param; + param.max_area = 0.0; + param.min_b = 0.0; + + Triangulation tri (mp_graph); + tri.triangulate (poly, vertexes, param, trans); + + // @@@ consider vertexes + hertel_mehlhorn_decomposition (tri, parameters); +} + +void +ConvexDecomposition::decompose (const db::DPolygon &poly, const ConvexDecompositionParameters ¶meters, const db::DCplxTrans &trans) +{ + // start with a triangulation + TriangulationParameters param; + param.max_area = 0.0; + param.min_b = 0.0; + + Triangulation tri (mp_graph); + tri.triangulate (poly, param, trans); + + hertel_mehlhorn_decomposition (tri, parameters); +} + +void +ConvexDecomposition::decompose (const db::DPolygon &poly, const std::vector &vertexes, const ConvexDecompositionParameters ¶meters, const db::DCplxTrans &trans) +{ + // start with a triangulation + TriangulationParameters param; + param.max_area = 0.0; + param.min_b = 0.0; + + Triangulation tri (mp_graph); + tri.triangulate (poly, vertexes, param, trans); + + // @@@ consider vertexes + hertel_mehlhorn_decomposition (tri, parameters); +} + +void +ConvexDecomposition::decompose (const db::Region ®ion, const ConvexDecompositionParameters ¶meters, double dbu) +{ + decompose (region, parameters, db::CplxTrans (dbu)); +} + +void +ConvexDecomposition::decompose (const db::Region ®ion, const ConvexDecompositionParameters ¶meters, const db::CplxTrans &trans) +{ + // start with a triangulation + TriangulationParameters param; + param.max_area = 0.0; + param.min_b = 0.0; + + Triangulation tri (mp_graph); + tri.triangulate (region, param, trans); + + hertel_mehlhorn_decomposition (tri, parameters); +} + +} // namespace plc + +} // namespace db diff --git a/src/db/db/dbPLCConvexDecomposition.h b/src/db/db/dbPLCConvexDecomposition.h new file mode 100644 index 000000000..a04fdd844 --- /dev/null +++ b/src/db/db/dbPLCConvexDecomposition.h @@ -0,0 +1,132 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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_dbPLCConvexDecomposition +#define HDR_dbPLCConvexDecomposition + +#include "dbCommon.h" +#include "dbPLC.h" + +#include +#include +#include +#include + +namespace db +{ + +namespace plc +{ + +class Triangulation; + +struct DB_PUBLIC ConvexDecompositionParameters +{ + ConvexDecompositionParameters () + : with_segments (false), + split_edges (false), + base_verbosity (30) + { } + + /** + * @brief Introduce new segments + * + * If true, new segments will be introduced. + * New segments are constructed perpendicular to the edges forming + * a concave corner. + */ + bool with_segments; + + /** + * @brief Split edges + * + * If true, edges in the resulting polygons may be split. + * This will produce edge sections that correlate with + * other polygon edges, but may be collinear with neighbor + * edges. + */ + double split_edges; + + /** + * @brief The verbosity level above which triangulation reports details + */ + int base_verbosity; +}; + +/** + * @brief A convex decomposition algorithm + * + * This class implements a variant of the Hertel-Mehlhorn decomposition. + */ +class DB_PUBLIC ConvexDecomposition +{ +public: + /** + * @brief The constructor + * + * The graph will be one filled by the decomposition. + */ + ConvexDecomposition (Graph *graph); + + /** + * @brief Clears the triangulation + */ + void clear (); + + /** + * @brief Creates a decomposition for the given region + * + * The database unit should be chosen in a way that target area values are "in the order of 1". + * For inputs featuring acute angles (angles < ~25 degree), the parameters should defined a min + * edge length ("min_length"). + * "min_length" should be at least 1e-4. If a min edge length is given, the max area constaints + * may not be satisfied. + * + * Edges in the input should not be shorter than 1e-4. + */ + void decompose (const db::Region ®ion, const ConvexDecompositionParameters ¶meters, double dbu = 1.0); + + // more versions + void decompose (const db::Region ®ion, const ConvexDecompositionParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ()); + void decompose (const db::Polygon &poly, const ConvexDecompositionParameters ¶meters, double dbu = 1.0); + void decompose (const db::Polygon &poly, const std::vector &vertexes, const ConvexDecompositionParameters ¶meters, double dbu = 1.0); + void decompose (const db::Polygon &poly, const ConvexDecompositionParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ()); + void decompose (const db::Polygon &poly, const std::vector &vertexes, const ConvexDecompositionParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ()); + + /** + * @brief Decomposes a floating-point polygon + */ + void decompose (const db::DPolygon &poly, const ConvexDecompositionParameters ¶meters, const db::DCplxTrans &trans = db::DCplxTrans ()); + void decompose (const db::DPolygon &poly, const std::vector &vertexes, const ConvexDecompositionParameters ¶meters, const db::DCplxTrans &trans = db::DCplxTrans ()); + +private: + Graph *mp_graph; + + void hertel_mehlhorn_decomposition (Triangulation &tris, const ConvexDecompositionParameters ¶m); +}; + +} // namespace plc + +} // namespace db + +#endif + diff --git a/src/db/db/dbPLCTriangulation.cc b/src/db/db/dbPLCTriangulation.cc index 879cd9e7d..9785616a6 100644 --- a/src/db/db/dbPLCTriangulation.cc +++ b/src/db/db/dbPLCTriangulation.cc @@ -22,9 +22,6 @@ #include "dbPLCTriangulation.h" -#include "dbLayout.h" -#include "dbWriter.h" -#include "tlStream.h" #include "tlLog.h" #include "tlTimer.h" diff --git a/src/db/unit_tests/dbPLCConvexDecompositionTests.cc b/src/db/unit_tests/dbPLCConvexDecompositionTests.cc new file mode 100644 index 000000000..d2c334b4b --- /dev/null +++ b/src/db/unit_tests/dbPLCConvexDecompositionTests.cc @@ -0,0 +1,112 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbPLCConvexDecomposition.h" +#include "dbWriter.h" +#include "dbRegionProcessors.h" +#include "dbTestSupport.h" +#include "tlUnitTest.h" +#include "tlStream.h" +#include "tlFileUtils.h" + +#include +#include +#include +#include + +class TestableConvexDecomposition + : public db::plc::ConvexDecomposition +{ +public: + using db::plc::ConvexDecomposition::ConvexDecomposition; +}; + +TEST(basic) +{ + db::plc::Graph plc; + TestableConvexDecomposition decomp (&plc); + + db::Point contour[] = { + db::Point (0, 0), + db::Point (0, 100), + db::Point (1000, 100), + db::Point (1000, 500), + db::Point (1100, 500), + db::Point (1100, 100), + db::Point (2100, 100), + db::Point (2100, 0) + }; + + db::Point contour2[] = { + db::Point (4000, 0), + db::Point (4000, 100), + db::Point (5000, 100), + db::Point (5000, 500), + db::Point (5100, 500), + db::Point (5100, 100), + db::Point (6100, 100), + db::Point (6100, -1000), + db::Point (4150, -1000), + db::Point (4150, 0) + }; + + db::Region region; + + db::Polygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + region.insert (poly); + + poly.clear (); + poly.assign_hull (contour2 + 0, contour2 + sizeof (contour2) / sizeof (contour2[0])); + region.insert (poly); + + double dbu = 0.001; + + db::plc::ConvexDecompositionParameters param; + decomp.decompose (region, param, dbu); + + std::unique_ptr ly (plc.to_layout ()); + db::compare_layouts (_this, *ly, tl::testdata () + "/algo/hm_decomposition_au1.gds"); + + param.with_segments = true; + param.split_edges = false; + decomp.decompose (region, param, dbu); + + ly.reset (plc.to_layout ()); + db::compare_layouts (_this, *ly, tl::testdata () + "/algo/hm_decomposition_au2.gds"); + + param.with_segments = false; + param.split_edges = true; + decomp.decompose (region, param, dbu); + + ly.reset (plc.to_layout ()); + db::compare_layouts (_this, *ly, tl::testdata () + "/algo/hm_decomposition_au3.gds"); + + param.with_segments = true; + param.split_edges = true; + decomp.decompose (region, param, dbu); + + ly.reset (plc.to_layout ()); + db::compare_layouts (_this, *ly, tl::testdata () + "/algo/hm_decomposition_au4.gds"); +} + diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index b5aadac53..ace029ba3 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -12,6 +12,7 @@ SOURCES = \ dbFillToolTests.cc \ dbLogTests.cc \ dbObjectWithPropertiesTests.cc \ + dbPLCConvexDecompositionTests.cc \ dbPLCGraphTests.cc \ dbPLCTests.cc \ dbPLCTriangulationTests.cc \ diff --git a/testdata/algo/hm_decomposition_au1.gds b/testdata/algo/hm_decomposition_au1.gds new file mode 100644 index 0000000000000000000000000000000000000000..dd04b81d925b3ab992d3ac34c8e92a3a3deff618 GIT binary patch literal 1636 zcmb7^u}T9$5QhKTyW5hgf$G_P6rYU_SrargOjhxb;k| z|IYp>FZGi8$$l|@v7YMg@^+k9mp}Vg#ou-MNu8t)qC;5sHLv?R>uz_P`b9}Jjh@@( z$C#jrrlgi?9H&I7P?bs9$OX`Ha;xpK!^A&sopCAvRkix=J zh_kyhms9S{T(-#UW%f6-JNwPTAPgVS55mp|Vq_?AhU4lSX=uh*mq0H{&M%%$uHRlh zPe-puv**V<^pk%$V@CHkw}?}~?1Ttlc0&M%-3Z8LG^63{1jwiKLAE<|e~UlfLp>jl z{W@ofO9!5JmZt1)ui3@6?3tkXTRgOybiWm2j;-4 zAJ{N&;(<#$K?A5fOw)8Zm-}f>`Ha^cif@`3gRPPmqUDu&}Tc z;>^7>*(j?!xkYA^*>C4$_8d6G@h$pM+<8Na0u@ehxO$B|c9ZinpqFH)qlcr5m#2^8 z@$>tj#@y+EG(j0JGF(SC_7{Gp+0L6^1==eGXy7%;jYB}^jYd_sVGav4f zPvY`H;C-!|fAYy|xSwnENkb-eWgXIQ;s>n194;1LyR}}`U(LgFtGV`lZF-L1gK}J1 zNB6z1tV8;q-w1o$ls&pV-ez8q8ah2z_2W#j4)yClx6jhet!}EF(me}pRLB%>LuezV zUXpiZq|`|=(@145>|Xq5q|~i4(@3djBs)e*w;{lb7qNl|1@Y{|)MxP0o3G##_ym0jg&sV3 z6zc5kOsZn+#D(xByZg(`&dv-R;`j#rDDJ!>MS%*(IQaUGJa*%=Q=pe*C&T;0^XJF6 zqtVme^x^gj{p=6UxzY8-CDI&l8!;ld%^1LLHv!Tyz0qL31M+?PP}O-_-lFF>K6&vp z;5B*=|54R>(nh{JSS&uaf$o5Ik=<&hs`IRkyth#g;@Kb7dZF2VIiXFo->dh#oG(@9 zNgM4K(_n;^{-ft$r6pNsel5g*UE=R{{4L}K-2#oCzm0E0tQsjjvzgKxlZY8T^m*dr zBy$rqd?(jO7eAeWydhf1}xRn7i$uDVV`-5NdAYL_Z)L2+*gy~0RoElXw@DSs~RJE0F5Deam< a8!64+b~&&2B{q$=w9iWxGu5GrN%0A3iHH^e literal 0 HcmV?d00001 From 4f1b03496b6f273d6a416f1d20c0c47a056d9957 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 16 Apr 2025 01:15:18 +0200 Subject: [PATCH 140/392] Handling of points in Convex decomposition --- src/db/db/dbPLC.cc | 13 ++++++++ src/db/db/dbPLC.h | 33 ++++++++++++++++++- src/db/db/dbPLCConvexDecomposition.cc | 27 +++++++++++---- .../dbPLCConvexDecompositionTests.cc | 33 +++++++++++++++++++ 4 files changed, 98 insertions(+), 8 deletions(-) diff --git a/src/db/db/dbPLC.cc b/src/db/db/dbPLC.cc index f9572550e..d72b57362 100644 --- a/src/db/db/dbPLC.cc +++ b/src/db/db/dbPLC.cc @@ -542,6 +542,19 @@ Polygon::bbox () const return box; } +db::DPolygon +Polygon::polygon () const +{ + std::vector pts; + for (int i = 0; i < int (size ()); ++i) { + pts.push_back (*vertex (i)); + } + + db::DPolygon poly; + poly.assign_hull (pts.begin (), pts.end ()); + return poly; +} + std::pair Polygon::circumcircle (bool *ok) const { diff --git a/src/db/db/dbPLC.h b/src/db/db/dbPLC.h index d5d6f0a26..ee7f1a4f0 100644 --- a/src/db/db/dbPLC.h +++ b/src/db/db/dbPLC.h @@ -562,7 +562,25 @@ public: */ size_t size () const { - return mp_v.size (); + return mp_e.size (); + } + + /** + * @brief Gets the internal vertexes + * + * Internal vertexes are special points inside the polygons. + */ + size_t internal_vertexes () const + { + return mp_v.size () - mp_e.size (); + } + + /** + * @brief Adds a vertex as an internal vertex + */ + void add_internal_vertex (Vertex *v) + { + mp_v.push_back (v); } /** @@ -580,6 +598,14 @@ public: } } + /** + * @brief Gets the nth internal vertex + */ + inline Vertex *internal_vertex (size_t n) const + { + return mp_v[mp_e.size () + n]; + } + /** * @brief Gets the nth edge (n wraps around and can be negative) */ @@ -604,6 +630,11 @@ public: */ db::DBox bbox () const; + /** + * @brief Returns a DPolygon object for this polygon + */ + db::DPolygon polygon () const; + /** * @brief Gets the center point and radius of the circumcircle * If ok is non-null, it will receive a boolean value indicating whether the circumcircle is valid. diff --git a/src/db/db/dbPLCConvexDecomposition.cc b/src/db/db/dbPLCConvexDecomposition.cc index 1905b27db..ad7c65735 100644 --- a/src/db/db/dbPLCConvexDecomposition.cc +++ b/src/db/db/dbPLCConvexDecomposition.cc @@ -383,12 +383,16 @@ ConvexDecomposition::hertel_mehlhorn_decomposition (Triangulation &tris, const C left_triangles.insert (it.operator-> ()); } - std::list > polygons; + std::list > polygons; + std::list > internal_vertexes; while (! left_triangles.empty ()) { - polygons.push_back (std::vector ()); - std::vector &edges = polygons.back (); + polygons.push_back (std::unordered_set ()); + std::unordered_set &edges = polygons.back (); + + internal_vertexes.push_back (std::unordered_set ()); + std::unordered_set &ivs = internal_vertexes.back (); const Polygon *tri = *left_triangles.begin (); std::vector queue, next_queue; @@ -407,8 +411,15 @@ ConvexDecomposition::hertel_mehlhorn_decomposition (Triangulation &tris, const C const Edge *e = (*q)->edge (i); const Polygon *qq = e->other (*q); + if (e->v1 ()->is_precious ()) { + ivs.insert (e->v1 ()); + } + if (e->v2 ()->is_precious ()) { + ivs.insert (e->v2 ()); + } + if (! qq || essential_edges.find (e) != essential_edges.end ()) { - edges.push_back (const_cast (e)); // @@@ const_cast + edges.insert (const_cast (e)); // @@@ const_cast } else if (left_triangles.find (qq) != left_triangles.end ()) { next_queue.push_back (qq); } @@ -430,8 +441,12 @@ ConvexDecomposition::hertel_mehlhorn_decomposition (Triangulation &tris, const C // create the polygons + auto iv = internal_vertexes.begin (); for (auto p = polygons.begin (); p != polygons.end (); ++p) { - mp_graph->create_polygon (p->begin (), p->end ()); + Polygon *poly = mp_graph->create_polygon (p->begin (), p->end ()); + for (auto i = iv->begin (); i != iv->end (); ++i) { + poly->add_internal_vertex (*i); // @@@ reserve? + } } } @@ -472,7 +487,6 @@ ConvexDecomposition::decompose (const db::Polygon &poly, const std::vector vertexes; + vertexes.push_back (db::Point (10, 50)); + vertexes.push_back (db::Point (200, 70)); + + db::Polygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + + double dbu = 0.001; + + db::plc::ConvexDecompositionParameters param; + decomp.decompose (poly, vertexes, param, dbu); + + for (auto p = plc.begin (); p != plc.end (); ++p) { + std::cout << p->polygon ().to_string () << std::endl; // @@@ + for (size_t i = 0; i < p->internal_vertexes (); ++i) { + std::cout << " " << p->internal_vertex (i)->to_string () << std::endl; // @@@ + } + } + +} + From 94396da11718e7c53f6c7f726c7996bc75f0b54c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 16 Apr 2025 23:43:27 +0200 Subject: [PATCH 141/392] Fixed problem of on-edge internal vertex/precious vertex --- src/db/db/dbPLC.cc | 2 +- src/db/db/dbPLCTriangulation.cc | 45 ++++++++++++++----- .../dbPLCConvexDecompositionTests.cc | 21 ++++++--- 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/src/db/db/dbPLC.cc b/src/db/db/dbPLC.cc index d72b57362..2bcf56202 100644 --- a/src/db/db/dbPLC.cc +++ b/src/db/db/dbPLC.cc @@ -551,7 +551,7 @@ Polygon::polygon () const } db::DPolygon poly; - poly.assign_hull (pts.begin (), pts.end ()); + poly.assign_hull (pts.begin (), pts.end (), false); return poly; } diff --git a/src/db/db/dbPLCTriangulation.cc b/src/db/db/dbPLCTriangulation.cc index 9785616a6..02afd64da 100644 --- a/src/db/db/dbPLCTriangulation.cc +++ b/src/db/db/dbPLCTriangulation.cc @@ -1017,7 +1017,7 @@ Triangulation::search_edges_crossing (Vertex *from, Vertex *to) if (os->has_vertex (vv)) { return result; } - if (os->crosses (edge)) { + if (os->crosses_including (edge)) { result.push_back (os); current_triangle = t.operator-> (); next_edge = os; @@ -1087,12 +1087,19 @@ Triangulation::find_edge_for_points (const db::DPoint &p1, const db::DPoint &p2) return 0; } +static bool is_touching (const db::DEdge &a, const db::DEdge &b) +{ + return (a.side_of (b.p1 ()) == 0 || a.side_of (b.p2 ()) == 0); +} + std::vector Triangulation::ensure_edge_inner (Vertex *from, Vertex *to) { auto crossed_edges = search_edges_crossing (from, to); std::vector result; + db::DEdge dedge (*from , *to); + if (crossed_edges.empty ()) { // no crossing edge - there should be a edge already @@ -1100,7 +1107,7 @@ Triangulation::ensure_edge_inner (Vertex *from, Vertex *to) tl_assert (res != 0); result.push_back (res); - } else if (crossed_edges.size () == 1) { + } else if (crossed_edges.size () == 1 && ! is_touching (dedge, crossed_edges.front ()->edge ())) { // can be solved by flipping auto pp = flip (crossed_edges.front ()); @@ -1112,18 +1119,29 @@ Triangulation::ensure_edge_inner (Vertex *from, Vertex *to) // split edge close to center db::DPoint split_point; + Edge *split_edge = 0; double d = -1.0; double l_half = 0.25 * (*to - *from).sq_length (); for (auto e = crossed_edges.begin (); e != crossed_edges.end (); ++e) { - db::DPoint p = (*e)->intersection_point (db::DEdge (*from, *to)); + db::DPoint p = (*e)->intersection_point (dedge); double dp = fabs ((p - *from).sq_length () - l_half); if (d < 0.0 || dp < d) { dp = d; split_point = p; + split_edge = *e; } } - Vertex *split_vertex = insert_point (split_point); + Vertex *split_vertex; + if (dedge.side_of (split_point) == 0) { + if (dedge.side_of (*split_edge->v1 ()) == 0) { + split_vertex = split_edge->v1 (); + } else { + split_vertex = split_edge->v2 (); + } + } else { + split_vertex = insert_point (split_point); + } result = ensure_edge_inner (from, split_vertex); @@ -1167,15 +1185,20 @@ Triangulation::join_edges (std::vector &edges) tl_assert (cp != 0); std::vector join_edges; - for (auto e = cp->begin_edges (); e != cp->end_edges (); ++e) { - if (*e != s1 && *e != s2) { - if ((*e)->can_join_via (cp)) { - join_edges.push_back (*e); - } else { - join_edges.clear (); - break; + + if (! cp->is_precious ()) { + + for (auto e = cp->begin_edges (); e != cp->end_edges (); ++e) { + if (*e != s1 && *e != s2) { + if ((*e)->can_join_via (cp)) { + join_edges.push_back (*e); + } else { + join_edges.clear (); + break; + } } } + } if (! join_edges.empty ()) { diff --git a/src/db/unit_tests/dbPLCConvexDecompositionTests.cc b/src/db/unit_tests/dbPLCConvexDecompositionTests.cc index df2c10a89..fb6dcd245 100644 --- a/src/db/unit_tests/dbPLCConvexDecompositionTests.cc +++ b/src/db/unit_tests/dbPLCConvexDecompositionTests.cc @@ -123,7 +123,7 @@ TEST(internal_vertex) }; std::vector vertexes; - vertexes.push_back (db::Point (10, 50)); + vertexes.push_back (db::Point (0, 50)); // on edge vertexes.push_back (db::Point (200, 70)); db::Polygon poly; @@ -134,12 +134,21 @@ TEST(internal_vertex) db::plc::ConvexDecompositionParameters param; decomp.decompose (poly, vertexes, param, dbu); - for (auto p = plc.begin (); p != plc.end (); ++p) { - std::cout << p->polygon ().to_string () << std::endl; // @@@ - for (size_t i = 0; i < p->internal_vertexes (); ++i) { - std::cout << " " << p->internal_vertex (i)->to_string () << std::endl; // @@@ - } + EXPECT_EQ (plc.begin () == plc.end (), false); + if (plc.begin () == plc.end ()) { + return; } + auto p = plc.begin (); + EXPECT_EQ (p->polygon ().to_string (), "(0,0;0,0.05;0,0.1;1,0.1;1,0)"); + + std::vector ip; + for (size_t i = 0; i < p->internal_vertexes (); ++i) { + ip.push_back (p->internal_vertex (i)->to_string ()); + } + std::sort (ip.begin (), ip.end ()); + EXPECT_EQ (tl::join (ip, "/"), "(0, 0.05)/(0.2, 0.07)"); + + EXPECT_EQ (++p == plc.end (), true); } From a2ef7a28f88ffb57a7fdc53cb6696e6c8371d779 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 17 Apr 2025 23:20:37 +0200 Subject: [PATCH 142/392] WIP --- src/db/db/dbPLC.cc | 10 ++ src/db/db/dbPLC.h | 43 +++++++-- src/db/db/dbPLCConvexDecomposition.cc | 38 ++------ src/db/db/dbPLCConvexDecomposition.h | 20 ++++ src/db/db/dbPLCTriangulation.cc | 10 +- src/db/unit_tests/dbPLCGraphTests.cc | 96 ++++++++++++++++++-- src/db/unit_tests/dbPLCTests.cc | 2 + src/db/unit_tests/dbPLCTriangulationTests.cc | 7 +- 8 files changed, 174 insertions(+), 52 deletions(-) diff --git a/src/db/db/dbPLC.cc b/src/db/db/dbPLC.cc index 2bcf56202..e75673cf1 100644 --- a/src/db/db/dbPLC.cc +++ b/src/db/db/dbPLC.cc @@ -72,6 +72,11 @@ Vertex::Vertex (const Vertex &v) // NOTE: edges are not copied! } +Vertex::~Vertex () +{ + // .. nothing yet .. +} + Vertex &Vertex::operator= (const Vertex &v) { if (this != &v) { @@ -176,6 +181,11 @@ Edge::Edge (Graph *graph, Vertex *v1, Vertex *v2) // .. nothing yet .. } +Edge::~Edge () +{ + // .. nothing yet .. +} + void Edge::set_left (Polygon *t) { diff --git a/src/db/db/dbPLC.h b/src/db/db/dbPLC.h index ee7f1a4f0..c88c8a506 100644 --- a/src/db/db/dbPLC.h +++ b/src/db/db/dbPLC.h @@ -161,10 +161,12 @@ protected: Vertex (Graph *graph, const DPoint &p); Vertex (Graph *graph, const Vertex &v); Vertex (Graph *graph, db::DCoord x, db::DCoord y); + ~Vertex (); private: friend class Edge; friend class Graph; + friend class tl::stable_vector; void remove_edge (const edges_iterator_non_const &ec) { @@ -499,12 +501,14 @@ protected: Edge (Graph *graph); Edge (Graph *graph, Vertex *v1, Vertex *v2); + ~Edge (); private: friend class Polygon; friend class Graph; friend class Triangulation; friend class ConvexDecomposition; + friend class tl::stable_vector; Graph *mp_graph; Vertex *mp_v1, *mp_v2; @@ -538,23 +542,34 @@ class DB_PUBLIC Polygon : public tl::list_node, public tl::Object { public: - template - Polygon (Graph *graph, Iter from, Iter to) - : mp_graph (graph), mp_e (from, to) - { - init (); - } + /** + * @brief Destructor + * + * It is legal to delete a polygon object to remove it. + */ ~Polygon (); + /** + * @brief Detaches a polygon object from the edges + */ void unlink (); - void set_id (size_t id) { m_id = id; } + /** + * @brief Gets the polygon's unique ID + */ size_t id () const { return m_id; } + /** + * @brief Gets a value indicating whether the polygon is an outside polygon + * + * Outside polygons are polygons that fill concave parts in a triangulation for example. + */ bool is_outside () const { return m_is_outside; } - void set_outside (bool o) { m_is_outside = o; } + /** + * @brief Returns a string representation + */ std::string to_string (bool with_id = false) const; /** @@ -727,8 +742,19 @@ protected: Polygon (Graph *graph); Polygon (Graph *graph, Edge *e1, Edge *e2, Edge *e3); + template + Polygon (Graph *graph, Iter from, Iter to) + : mp_graph (graph), mp_e (from, to) + { + init (); + } + + void set_outside (bool o) { m_is_outside = o; } + void set_id (size_t id) { m_id = id; } + private: friend class Graph; + friend class Triangulation; Graph *mp_graph; bool m_is_outside; @@ -764,6 +790,7 @@ struct PolygonLessFunc * hold triangles (polygons with 3 vertexes). */ class DB_PUBLIC Graph + : public tl::Object { public: typedef tl::list polygons_type; diff --git a/src/db/db/dbPLCConvexDecomposition.cc b/src/db/db/dbPLCConvexDecomposition.cc index ad7c65735..a39b5295d 100644 --- a/src/db/db/dbPLCConvexDecomposition.cc +++ b/src/db/db/dbPLCConvexDecomposition.cc @@ -85,24 +85,6 @@ struct equal_compare_func } }; -struct ConcaveCorner -{ - ConcaveCorner () - : corner (0), incoming (0), outgoing (0) - { - // .. nothing yet .. - } - - ConcaveCorner (Vertex *_corner, Edge *_incoming, Edge *_outgoing) - : corner (_corner), incoming (_incoming), outgoing (_outgoing) - { - // .. nothing yet .. - } - - Vertex *corner; - Edge *incoming, *outgoing; -}; - Edge *find_outgoing_segment (Vertex *vertex, Edge *incoming, int &vp_max_sign) { Vertex *vfrom = incoming->other (vertex); @@ -141,20 +123,16 @@ Edge *find_outgoing_segment (Vertex *vertex, Edge *incoming, int &vp_max_sign) return outgoing; } -void collect_concave_vertexes (Graph &tris, std::vector &concave_vertexes) +void +ConvexDecomposition::collect_concave_vertexes (std::vector &concave_vertexes) { concave_vertexes.clear (); - // @@@ use edge "level" - // @@@ use edges from heap std::unordered_set left; - for (auto it = tris.begin (); it != tris.end (); ++it) { - for (unsigned int i = 0; i < 3; ++i) { - Edge *e = it->edge (i); - if (e->is_segment ()) { - left.insert (e); - } + for (auto e = mp_graph->edges ().begin (); e != mp_graph->edges ().end (); ++e) { + if (e->is_segment () && (e->left () != 0 || e->right () != 0)) { + left.insert (e.operator-> ()); } } @@ -191,7 +169,7 @@ void collect_concave_vertexes (Graph &tris, std::vector &concave_ } std::pair -search_crossing_with_next_segment (const Vertex *v0, const db::DVector &direction) +ConvexDecomposition::search_crossing_with_next_segment (const Vertex *v0, const db::DVector &direction) { auto vtri = v0->polygons (); // TODO: slow? std::vector nvv, nvv_next; @@ -253,7 +231,7 @@ ConvexDecomposition::hertel_mehlhorn_decomposition (Triangulation &tris, const C bool split_edges = param.split_edges; std::vector concave_vertexes; - collect_concave_vertexes (*mp_graph, concave_vertexes); + collect_concave_vertexes (concave_vertexes); // @@@ return if no concave corners @@ -302,7 +280,7 @@ ConvexDecomposition::hertel_mehlhorn_decomposition (Triangulation &tris, const C } // As the insertion invalidates the edges, we need to collect the concave vertexes again - collect_concave_vertexes (*mp_graph, concave_vertexes); + collect_concave_vertexes (concave_vertexes); } diff --git a/src/db/db/dbPLCConvexDecomposition.h b/src/db/db/dbPLCConvexDecomposition.h index a04fdd844..03a6949bb 100644 --- a/src/db/db/dbPLCConvexDecomposition.h +++ b/src/db/db/dbPLCConvexDecomposition.h @@ -121,7 +121,27 @@ public: private: Graph *mp_graph; + struct ConcaveCorner + { + ConcaveCorner () + : corner (0), incoming (0), outgoing (0) + { + // .. nothing yet .. + } + + ConcaveCorner (Vertex *_corner, Edge *_incoming, Edge *_outgoing) + : corner (_corner), incoming (_incoming), outgoing (_outgoing) + { + // .. nothing yet .. + } + + Vertex *corner; + Edge *incoming, *outgoing; + }; + void hertel_mehlhorn_decomposition (Triangulation &tris, const ConvexDecompositionParameters ¶m); + void collect_concave_vertexes (std::vector &concave_vertexes); + std::pair search_crossing_with_next_segment (const Vertex *v0, const db::DVector &direction); }; } // namespace plc diff --git a/src/db/db/dbPLCTriangulation.cc b/src/db/db/dbPLCTriangulation.cc index 02afd64da..30757e249 100644 --- a/src/db/db/dbPLCTriangulation.cc +++ b/src/db/db/dbPLCTriangulation.cc @@ -1133,12 +1133,10 @@ Triangulation::ensure_edge_inner (Vertex *from, Vertex *to) } Vertex *split_vertex; - if (dedge.side_of (split_point) == 0) { - if (dedge.side_of (*split_edge->v1 ()) == 0) { - split_vertex = split_edge->v1 (); - } else { - split_vertex = split_edge->v2 (); - } + if (dedge.side_of (*split_edge->v1 ()) == 0) { + split_vertex = split_edge->v1 (); + } else if (dedge.side_of (*split_edge->v2 ()) == 0) { + split_vertex = split_edge->v2 (); } else { split_vertex = insert_point (split_point); } diff --git a/src/db/unit_tests/dbPLCGraphTests.cc b/src/db/unit_tests/dbPLCGraphTests.cc index e02449401..3536b5ba4 100644 --- a/src/db/unit_tests/dbPLCGraphTests.cc +++ b/src/db/unit_tests/dbPLCGraphTests.cc @@ -27,15 +27,97 @@ #include #include -TEST(basic) +namespace { - db::DBox box (0, 0, 100.0, 200.0); - db::plc::Graph plc; - // @@@ pg.insert_polygon (db::DSimplePolygon (box)); +class TestableGraph + : public db::plc::Graph +{ +public: + using db::plc::Graph::Graph; + using db::plc::Graph::create_vertex; + using db::plc::Graph::create_edge; + using db::plc::Graph::create_triangle; + using db::plc::Graph::create_polygon; +}; - // @@@ - tl::info << plc.to_string (); - plc.dump ("debug.gds"); // @@@ } +TEST(basic) +{ + TestableGraph plc; + + db::plc::Vertex *v1 = plc.create_vertex (db::DPoint (1, 2)); + EXPECT_EQ (v1->to_string (), "(1, 2)"); + + v1 = plc.create_vertex (db::DPoint (2, 1)); + EXPECT_EQ (v1->to_string (), "(2, 1)"); + + EXPECT_EQ (v1->is_precious (), false); + v1->set_is_precious (true); + EXPECT_EQ (v1->is_precious (), true); +} + +TEST(edge) +{ + TestableGraph plc; + + db::plc::Vertex *v1 = plc.create_vertex (db::DPoint (1, 2)); + db::plc::Vertex *v2 = plc.create_vertex (db::DPoint (3, 4)); + + db::plc::Edge *e = plc.create_edge (v1, v2); + EXPECT_EQ (e->to_string (), "((1, 2), (3, 4))"); + + EXPECT_EQ (v1->num_edges (), size_t (1)); + EXPECT_EQ (v2->num_edges (), size_t (1)); + + EXPECT_EQ ((*v1->begin_edges ())->edge ().to_string (), "(1,2;3,4)"); + EXPECT_EQ ((*v2->begin_edges ())->edge ().to_string (), "(1,2;3,4)"); +} + +TEST(polygon) +{ + TestableGraph plc; + EXPECT_EQ (plc.num_polygons (), size_t (0)); + EXPECT_EQ (plc.bbox ().to_string (), "()"); + + db::plc::Vertex *v1 = plc.create_vertex (db::DPoint (1, 2)); + db::plc::Vertex *v2 = plc.create_vertex (db::DPoint (3, 4)); + db::plc::Vertex *v3 = plc.create_vertex (db::DPoint (3, 2)); + + db::plc::Edge *e1 = plc.create_edge (v1, v2); + db::plc::Edge *e2 = plc.create_edge (v1, v3); + db::plc::Edge *e3 = plc.create_edge (v2, v3); + + db::plc::Polygon *tri = plc.create_triangle (e1, e2, e3); + EXPECT_EQ (tri->to_string (), "((1, 2), (3, 4), (3, 2))"); + EXPECT_EQ (tri->polygon ().to_string (), "(1,2;3,4;3,2)"); + EXPECT_EQ (plc.bbox ().to_string (), "(1,2;3,4)"); + EXPECT_EQ (plc.num_polygons (), size_t (1)); + + EXPECT_EQ (v1->num_edges (), size_t (2)); + EXPECT_EQ (v2->num_edges (), size_t (2)); + EXPECT_EQ (v3->num_edges (), size_t (2)); + + EXPECT_EQ (tri->edge (0) == e1, true); + EXPECT_EQ (tri->edge (3) == e1, true); + EXPECT_EQ (tri->edge (1) == e3, true); + EXPECT_EQ (tri->edge (2) == e2, true); + EXPECT_EQ (tri->edge (-1) == e2, true); + + EXPECT_EQ (e1->left () == 0, true); + EXPECT_EQ (e1->right () == tri, true); + EXPECT_EQ (e2->left () == tri, true); + EXPECT_EQ (e2->right () == 0, true); + EXPECT_EQ (e3->left () == 0, true); + EXPECT_EQ (e3->right () == tri, true); + + delete tri; + + EXPECT_EQ (e1->left () == 0, true); + EXPECT_EQ (e1->right () == 0, true); + EXPECT_EQ (e2->left () == 0, true); + EXPECT_EQ (e2->right () == 0, true); + EXPECT_EQ (e3->left () == 0, true); + EXPECT_EQ (e3->right () == 0, true); +} diff --git a/src/db/unit_tests/dbPLCTests.cc b/src/db/unit_tests/dbPLCTests.cc index f93c5d373..3c7745484 100644 --- a/src/db/unit_tests/dbPLCTests.cc +++ b/src/db/unit_tests/dbPLCTests.cc @@ -71,6 +71,8 @@ public: TestablePolygon (db::plc::Edge *e1, db::plc::Edge *e2, db::plc::Edge *e3) : db::plc::Polygon (0, e1, e2, e3) { } + + using db::plc::Polygon::set_outside; }; // Tests for Vertex class diff --git a/src/db/unit_tests/dbPLCTriangulationTests.cc b/src/db/unit_tests/dbPLCTriangulationTests.cc index 0d58ffc04..ae8856bb4 100644 --- a/src/db/unit_tests/dbPLCTriangulationTests.cc +++ b/src/db/unit_tests/dbPLCTriangulationTests.cc @@ -33,6 +33,9 @@ #include #include +namespace +{ + class TestableTriangulation : public db::plc::Triangulation { @@ -64,6 +67,8 @@ public: using db::plc::Graph::create_triangle; }; +} + TEST(basic) { db::plc::Graph plc; @@ -343,7 +348,7 @@ TEST(heavy_insert) double y = round (flt_rand () * res) * (1.0 / res); db::plc::Vertex *v = tris.insert_point (x, y); bbox += db::DPoint (x, y); - vmap.insert (std::make_pair (*v, false)); + vmap.insert (std::pair (*v, false)); } // not strictly true, but very likely with at least 10 vertexes: From 3ed39e8a4af66518eec0222b576c302bf4cb2cd6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 17 Apr 2025 23:40:58 +0200 Subject: [PATCH 143/392] Internal vertex ID added, cleanup --- src/db/db/dbPLC.cc | 18 ++++++++-- src/db/db/dbPLC.h | 34 ++++++++++++++++--- src/db/db/dbPLCConvexDecomposition.cc | 20 +++-------- src/db/db/dbPLCTriangulation.cc | 6 ++-- .../dbPLCConvexDecompositionTests.cc | 5 +-- src/db/unit_tests/dbPLCGraphTests.cc | 3 +- 6 files changed, 59 insertions(+), 27 deletions(-) diff --git a/src/db/db/dbPLC.cc b/src/db/db/dbPLC.cc index e75673cf1..c44a96760 100644 --- a/src/db/db/dbPLC.cc +++ b/src/db/db/dbPLC.cc @@ -55,19 +55,19 @@ Vertex::Vertex (Graph *graph, const db::DPoint &p) } Vertex::Vertex (Graph *graph, const Vertex &v) - : DPoint (), mp_graph (graph), m_is_precious (false) + : DPoint (), mp_graph (graph), m_is_precious (false), m_id (0) { operator= (v); } Vertex::Vertex (Graph *graph, db::DCoord x, db::DCoord y) - : DPoint (x, y), mp_graph (graph), m_is_precious (false) + : DPoint (x, y), mp_graph (graph), m_is_precious (false), m_id (0) { // .. nothing yet .. } Vertex::Vertex (const Vertex &v) - : DPoint (v), mp_graph (v.mp_graph), m_is_precious (v.m_is_precious) + : DPoint (v), mp_graph (v.mp_graph), m_is_precious (v.m_is_precious), m_id (v.m_id) { // NOTE: edges are not copied! } @@ -83,6 +83,7 @@ Vertex &Vertex::operator= (const Vertex &v) // NOTE: edges are not copied! db::DPoint::operator= (v); m_is_precious = v.m_is_precious; + m_id = v.m_id; } return *this; } @@ -686,6 +687,17 @@ Polygon::contains (const db::DPoint &point) const return res; } +Edge * +Polygon::next_edge (const Edge *edge, const Vertex *vertex) const +{ + for (auto e = mp_e.begin (); e != mp_e.end (); ++e) { + if (*e != edge && ((*e)->v1 () == vertex || (*e)->v2 () == vertex)) { + return *e; + } + } + return 0; +} + double Polygon::min_edge_length () const { diff --git a/src/db/db/dbPLC.h b/src/db/db/dbPLC.h index c88c8a506..ea5ed4e38 100644 --- a/src/db/db/dbPLC.h +++ b/src/db/db/dbPLC.h @@ -127,17 +127,29 @@ public: bool has_edge (const Edge *edge) const; /** - * @brief Sets a valid indicating whether the vertex is precious + * @brief Sets a value indicating whether the vertex is precious * * "precious" vertexes are not removed during triangulation for example. */ - void set_is_precious (bool f) { m_is_precious = f; } + void set_is_precious (bool f, unsigned int id) + { + m_is_precious = f; + m_id = id; + } /** - * @brief Gets a valid indicating whether the vertex is precious + * @brief Gets a value indicating whether the vertex is precious */ bool is_precious () const { return m_is_precious; } + /** + * @brief Gets the ID passed to "set_is_precious" + * + * This ID can be used to identify the vertex in the context it came from (e.g. + * index in point vector). + */ + unsigned int id () const { return m_id; } + /** * @brief Returns a string representation of the vertex */ @@ -175,7 +187,8 @@ private: Graph *mp_graph; edges_type mp_edges; - bool m_is_precious; + bool m_is_precious : 1; + unsigned int m_id : 31; }; /** @@ -598,6 +611,14 @@ public: mp_v.push_back (v); } + /** + * @brief Reserves for n internal vertexes + */ + void reserve_internal_vertexes (size_t n) + { + mp_v.reserve (mp_v.size () + n); + } + /** * @brief Gets the nth vertex (n wraps around and can be negative) * The vertexes are oriented clockwise. @@ -716,6 +737,11 @@ public: return false; } + /** + * @brief Coming from an edge e and the vertex v, gets the next edge + */ + Edge *next_edge (const Edge *e, const Vertex *v) const; + /** * @brief Returns the minimum edge length */ diff --git a/src/db/db/dbPLCConvexDecomposition.cc b/src/db/db/dbPLCConvexDecomposition.cc index a39b5295d..45eda37ed 100644 --- a/src/db/db/dbPLCConvexDecomposition.cc +++ b/src/db/db/dbPLCConvexDecomposition.cc @@ -233,10 +233,6 @@ ConvexDecomposition::hertel_mehlhorn_decomposition (Triangulation &tris, const C std::vector concave_vertexes; collect_concave_vertexes (concave_vertexes); - // @@@ return if no concave corners - - // @@@ sort concave vertexes - std::vector new_points; if (with_segments) { @@ -306,15 +302,8 @@ ConvexDecomposition::hertel_mehlhorn_decomposition (Triangulation &tris, const C const Polygon *t = e->v2 () == v0 ? e->right () : e->left (); tl_assert (t != 0); - // @@@ make a method of Polygon -> next_edge(Edge *e, Vertex *at) - const Edge *en = 0; - for (unsigned int i = 0; i < 3; ++i) { - const Edge *ee = t->edge (i); - if (ee != e && (ee->v1 () == v0 || ee->v2 () == v0)) { - en = ee; - break; - } - } + const Edge *en = t->next_edge (e, v0); + tl_assert (en != 0); db::DVector v1 = e->edge ().d () * (e->v1 () == v0 ? 1.0 : -1.0); db::DVector v2 = en->edge ().d () * (en->v1 () == v0 ? 1.0 : -1.0); @@ -397,7 +386,7 @@ ConvexDecomposition::hertel_mehlhorn_decomposition (Triangulation &tris, const C } if (! qq || essential_edges.find (e) != essential_edges.end ()) { - edges.insert (const_cast (e)); // @@@ const_cast + edges.insert (const_cast (e)); // TODO: ugly const_cast } else if (left_triangles.find (qq) != left_triangles.end ()) { next_queue.push_back (qq); } @@ -422,8 +411,9 @@ ConvexDecomposition::hertel_mehlhorn_decomposition (Triangulation &tris, const C auto iv = internal_vertexes.begin (); for (auto p = polygons.begin (); p != polygons.end (); ++p) { Polygon *poly = mp_graph->create_polygon (p->begin (), p->end ()); + poly->reserve_internal_vertexes (iv->size ()); for (auto i = iv->begin (); i != iv->end (); ++i) { - poly->add_internal_vertex (*i); // @@@ reserve? + poly->add_internal_vertex (*i); } } } diff --git a/src/db/db/dbPLCTriangulation.cc b/src/db/db/dbPLCTriangulation.cc index 30757e249..90f68d296 100644 --- a/src/db/db/dbPLCTriangulation.cc +++ b/src/db/db/dbPLCTriangulation.cc @@ -1364,8 +1364,9 @@ Triangulation::create_constrained_delaunay (const db::Polygon &p, const std::vec { clear (); + unsigned int id = 0; for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { - insert_point (trans * *v)->set_is_precious (true); + insert_point (trans * *v)->set_is_precious (true, id++); } std::vector > edge_contours; @@ -1379,8 +1380,9 @@ Triangulation::create_constrained_delaunay (const db::DPolygon &p, const std::ve { clear (); + unsigned int id = 0; for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { - insert_point (trans * *v)->set_is_precious (true); + insert_point (trans * *v)->set_is_precious (true, id++); } std::vector > edge_contours; diff --git a/src/db/unit_tests/dbPLCConvexDecompositionTests.cc b/src/db/unit_tests/dbPLCConvexDecompositionTests.cc index fb6dcd245..2cb1e1a70 100644 --- a/src/db/unit_tests/dbPLCConvexDecompositionTests.cc +++ b/src/db/unit_tests/dbPLCConvexDecompositionTests.cc @@ -125,6 +125,7 @@ TEST(internal_vertex) std::vector vertexes; vertexes.push_back (db::Point (0, 50)); // on edge vertexes.push_back (db::Point (200, 70)); + vertexes.push_back (db::Point (0, 0)); // on vertex db::Polygon poly; poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); @@ -144,10 +145,10 @@ TEST(internal_vertex) std::vector ip; for (size_t i = 0; i < p->internal_vertexes (); ++i) { - ip.push_back (p->internal_vertex (i)->to_string ()); + ip.push_back (p->internal_vertex (i)->to_string () + "#" + tl::to_string (p->internal_vertex (i)->id ())); } std::sort (ip.begin (), ip.end ()); - EXPECT_EQ (tl::join (ip, "/"), "(0, 0.05)/(0.2, 0.07)"); + EXPECT_EQ (tl::join (ip, "/"), "(0, 0)#2/(0, 0.05)#0/(0.2, 0.07)#1"); EXPECT_EQ (++p == plc.end (), true); } diff --git a/src/db/unit_tests/dbPLCGraphTests.cc b/src/db/unit_tests/dbPLCGraphTests.cc index 3536b5ba4..29b7d9597 100644 --- a/src/db/unit_tests/dbPLCGraphTests.cc +++ b/src/db/unit_tests/dbPLCGraphTests.cc @@ -54,8 +54,9 @@ TEST(basic) EXPECT_EQ (v1->to_string (), "(2, 1)"); EXPECT_EQ (v1->is_precious (), false); - v1->set_is_precious (true); + v1->set_is_precious (true, 17); EXPECT_EQ (v1->is_precious (), true); + EXPECT_EQ (v1->id (), 17u); } TEST(edge) From c6a4b6aba0de4d6ac612515cc9fb70307157c9ed Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 18 Apr 2025 00:15:43 +0200 Subject: [PATCH 144/392] Added a new utility function --- src/db/db/dbPLCTriangulation.cc | 35 ++++++++++++++++++-- src/db/db/dbPLCTriangulation.h | 31 ++++++++++------- src/db/unit_tests/dbPLCTriangulationTests.cc | 30 +++++++++++++++++ 3 files changed, 81 insertions(+), 15 deletions(-) diff --git a/src/db/db/dbPLCTriangulation.cc b/src/db/db/dbPLCTriangulation.cc index 90f68d296..d148656a6 100644 --- a/src/db/db/dbPLCTriangulation.cc +++ b/src/db/db/dbPLCTriangulation.cc @@ -294,7 +294,7 @@ Triangulation::find_triangle_for_point (const db::DPoint &point) } Edge * -Triangulation::find_closest_edge (const db::DPoint &p, Vertex *vstart, bool inside_only) +Triangulation::find_closest_edge (const db::DPoint &p, Vertex *vstart, bool inside_only) const { if (!vstart) { @@ -1057,7 +1057,7 @@ Triangulation::search_edges_crossing (Vertex *from, Vertex *to) } Vertex * -Triangulation::find_vertex_for_point (const db::DPoint &point) +Triangulation::find_vertex_for_point (const db::DPoint &point) const { Edge *edge = find_closest_edge (point); if (!edge) { @@ -1073,7 +1073,7 @@ Triangulation::find_vertex_for_point (const db::DPoint &point) } Edge * -Triangulation::find_edge_for_points (const db::DPoint &p1, const db::DPoint &p2) +Triangulation::find_edge_for_points (const db::DPoint &p1, const db::DPoint &p2) const { Vertex *v = find_vertex_for_point (p1); if (!v) { @@ -1087,6 +1087,35 @@ Triangulation::find_edge_for_points (const db::DPoint &p1, const db::DPoint &p2) return 0; } +std::vector +Triangulation::find_vertexes_along_line (const db::DPoint &p1, const db::DPoint &p2) const +{ + db::DEdge e12 (p1, p2); + + Vertex *v = find_vertex_for_point (p1); + if (!v) { + v = find_vertex_for_point (p2); + e12.swap_points (); + } + + std::vector result; + + while (v) { + Vertex *vn = 0; + for (auto e = v->begin_edges (); e != v->end_edges (); ++e) { + Vertex *vv = (*e)->other (v); + if (db::vprod_sign (e12.d (), *vv - *v) == 0 && db::sprod_sign (e12.d (), *vv - *v) > 0 && db::sprod_sign (e12.d (), *vv - e12.p2 ()) < 0) { + result.push_back (vv); + vn = vv; + break; + } + } + v = vn; + } + + return result; +} + static bool is_touching (const db::DEdge &a, const db::DEdge &b) { return (a.side_of (b.p1 ()) == 0 || a.side_of (b.p2 ()) == 0); diff --git a/src/db/db/dbPLCTriangulation.h b/src/db/db/dbPLCTriangulation.h index b3e941f89..af17aa876 100644 --- a/src/db/db/dbPLCTriangulation.h +++ b/src/db/db/dbPLCTriangulation.h @@ -158,6 +158,23 @@ public: */ Vertex *insert_point (const db::DPoint &point, std::list > *new_triangles = 0); + /** + * @brief Finds the edge for two given points + */ + Edge *find_edge_for_points (const db::DPoint &p1, const db::DPoint &p2) const; + + /** + * @brief Finds the vertex for a point + */ + Vertex *find_vertex_for_point (const db::DPoint &pt) const; + + /** + * @brief Finds the vertexes along the line given from p1 and p2 + * + * At least one of the points p1 and p2 must be existing vertexes. + */ + std::vector find_vertexes_along_line (const db::DPoint &p1, const db::DPoint &p2) const; + /** * @brief Statistics: number of flips (fixing) */ @@ -216,16 +233,6 @@ protected: */ std::vector search_edges_crossing (Vertex *from, Vertex *to); - /** - * @brief Finds the edge for two given points - */ - Edge *find_edge_for_points (const db::DPoint &p1, const db::DPoint &p2); - - /** - * @brief Finds the vertex for a point - */ - Vertex *find_vertex_for_point (const db::DPoint &pt); - /** * @brief Ensures all points between from an to are connected by edges and makes these segments */ @@ -275,7 +282,7 @@ private: bool m_is_constrained; size_t m_level; size_t m_id; - size_t m_flips, m_hops; + mutable size_t m_flips, m_hops; template void make_contours (const Poly &poly, const Trans &trans, std::vector > &contours); @@ -284,7 +291,7 @@ private: std::vector fill_concave_corners (const std::vector &edges); void fix_triangles (const std::vector &tris, const std::vector &fixed_edges, std::list > *new_triangles); std::vector find_triangle_for_point (const db::DPoint &point); - Edge *find_closest_edge (const db::DPoint &p, Vertex *vstart = 0, bool inside_only = false); + Edge *find_closest_edge (const db::DPoint &p, Vertex *vstart = 0, bool inside_only = false) const; Vertex *insert (Vertex *vertex, std::list > *new_triangles = 0); void split_triangle (Polygon *t, Vertex *vertex, std::list > *new_triangles_out); void split_triangles_on_edge (Vertex *vertex, Edge *split_edge, std::list > *new_triangles_out); diff --git a/src/db/unit_tests/dbPLCTriangulationTests.cc b/src/db/unit_tests/dbPLCTriangulationTests.cc index ae8856bb4..9253eb0ce 100644 --- a/src/db/unit_tests/dbPLCTriangulationTests.cc +++ b/src/db/unit_tests/dbPLCTriangulationTests.cc @@ -146,6 +146,36 @@ TEST(insert_vertex_twice) EXPECT_EQ (tris.check(), true); } +TEST(collect_vertexes) +{ + db::plc::Graph plc; + TestableTriangulation tris (&plc); + tris.init_box (db::DBox (0, 0, 1, 1)); + tris.insert_point (0.2, 0.2); + tris.insert_point (0.5, 0.5); + + std::vector vertexes = tris.find_vertexes_along_line (db::DPoint (0, 0), db::DPoint (1.5, 1.5)); + EXPECT_EQ (vertexes.size (), size_t (3)); + if (vertexes.size () >= size_t (3)) { + EXPECT_EQ (vertexes [0]->to_string (), "(0.2, 0.2)"); + EXPECT_EQ (vertexes [1]->to_string (), "(0.5, 0.5)"); + EXPECT_EQ (vertexes [2]->to_string (), "(1, 1)"); + } + + vertexes = tris.find_vertexes_along_line (db::DPoint (0, 0), db::DPoint (1.0, 1.0)); + EXPECT_EQ (vertexes.size (), size_t (2)); + if (vertexes.size () >= size_t (2)) { + EXPECT_EQ (vertexes [0]->to_string (), "(0.2, 0.2)"); + EXPECT_EQ (vertexes [1]->to_string (), "(0.5, 0.5)"); + } + + vertexes = tris.find_vertexes_along_line (db::DPoint (1, 1), db::DPoint (0.25, 0.25)); + EXPECT_EQ (vertexes.size (), size_t (1)); + if (vertexes.size () >= size_t (1)) { + EXPECT_EQ (vertexes [0]->to_string (), "(0.5, 0.5)"); + } +} + TEST(insert_vertex_convex) { db::plc::Graph plc; From 16604e5a92bd37b586f6f8f8b1611d6008e1cad6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 18 Apr 2025 13:12:45 +0200 Subject: [PATCH 145/392] New module: 'pex' --- setup.py | 45 +++++++++++++++- src/klayout.pri | 1 + src/klayout.pro | 2 + src/pex/pex.pro | 6 +++ src/pex/pex/gsiDeclRExtractor.cc | 44 ++++++++++++++++ src/pex/pex/pex.pro | 23 +++++++++ src/pex/pex/pexCommon.h | 51 +++++++++++++++++++ src/pex/pex/pexForceLink.cc | 32 ++++++++++++ src/pex/pex/pexForceLink.h | 40 +++++++++++++++ src/pex/pex/pexRExtractor.cc | 36 +++++++++++++ src/pex/pex/pexRExtractor.h | 49 ++++++++++++++++++ src/pex/unit_tests/pexRExtractorTests.cc | 31 +++++++++++ src/pex/unit_tests/unit_tests.pro | 16 ++++++ .../distutils_src/klayout/pex/__init__.py | 4 ++ src/pymod/distutils_src/klayout/pexcore.pyi | 0 src/pymod/pex/pex.pro | 14 +++++ src/pymod/pex/pexMain.cc | 31 +++++++++++ src/pymod/pex/pexMain.h | 24 +++++++++ src/pymod/pymod.pro | 1 + src/pymod/unit_tests/pymod_tests.cc | 1 + src/with_all_libs.pri | 6 +-- testdata/pymod/import_pex.py | 37 ++++++++++++++ 22 files changed, 489 insertions(+), 5 deletions(-) create mode 100644 src/pex/pex.pro create mode 100644 src/pex/pex/gsiDeclRExtractor.cc create mode 100644 src/pex/pex/pex.pro create mode 100644 src/pex/pex/pexCommon.h create mode 100644 src/pex/pex/pexForceLink.cc create mode 100644 src/pex/pex/pexForceLink.h create mode 100644 src/pex/pex/pexRExtractor.cc create mode 100644 src/pex/pex/pexRExtractor.h create mode 100644 src/pex/unit_tests/pexRExtractorTests.cc create mode 100644 src/pex/unit_tests/unit_tests.pro create mode 100644 src/pymod/distutils_src/klayout/pex/__init__.py create mode 100644 src/pymod/distutils_src/klayout/pexcore.pyi create mode 100644 src/pymod/pex/pex.pro create mode 100644 src/pymod/pex/pexMain.cc create mode 100644 src/pymod/pex/pexMain.h create mode 100755 testdata/pymod/import_pex.py diff --git a/setup.py b/setup.py index c816a0c5e..1ac1ff473 100644 --- a/setup.py +++ b/setup.py @@ -592,6 +592,25 @@ _db = Library( ) config.add_extension(_db) +# ------------------------------------------------------------------ +# _pex dependency library + +_pex_path = os.path.join("src", "pex", "pex") +_pex_sources = set(glob.glob(os.path.join(_pex_path, "*.cc"))) + +_pex = Library( + config.root + "._pex", + define_macros=config.macros() + [("MAKE_PEX_LIBRARY", 1)], + include_dirs=[_tl_path, _gsi_path, _db_path, _pex_path], + extra_objects=[config.path_of("_tl", _tl_path), config.path_of("_gsi", _gsi_path), config.path_of("_db", _db_path)], + language="c++", + libraries=config.libraries('_pex'), + extra_link_args=config.link_args("_pex"), + extra_compile_args=config.compile_args("_pex"), + sources=list(_pex_sources), +) +config.add_extension(_pex) + # ------------------------------------------------------------------ # _lib dependency library @@ -869,6 +888,28 @@ db = Extension( sources=list(db_sources), ) +# ------------------------------------------------------------------ +# pex extension library + +pex_path = os.path.join("src", "pymod", "pex") +pex_sources = set(glob.glob(os.path.join(pex_path, "*.cc"))) + +pex = Extension( + config.root + ".pexcore", + define_macros=config.macros(), + include_dirs=[_db_path, _tl_path, _gsi_path, _pya_path, _pex_path], + extra_objects=[ + config.path_of("_db", _db_path), + config.path_of("_pex", _pex_path), + config.path_of("_tl", _tl_path), + config.path_of("_gsi", _gsi_path), + config.path_of("_pya", _pya_path), + ], + extra_link_args=config.link_args("pexcore"), + extra_compile_args=config.compile_args("pexcore"), + sources=list(pex_sources), +) + # ------------------------------------------------------------------ # lib extension library @@ -1011,8 +1052,8 @@ if __name__ == "__main__": package_data={config.root: ["src/pymod/distutils_src/klayout/*.pyi"]}, data_files=[(config.root, ["src/pymod/distutils_src/klayout/py.typed"])], include_package_data=True, - ext_modules=[_tl, _gsi, _pya, _rba, _db, _lib, _rdb, _lym, _laybasic, _layview, _ant, _edt, _img] + ext_modules=[_tl, _gsi, _pya, _rba, _db, _pex, _lib, _rdb, _lym, _laybasic, _layview, _ant, _edt, _img] + db_plugins - + [tl, db, lib, rdb, lay, pya], + + [tl, db, pex, lib, rdb, lay, pya], cmdclass={'build_ext': klayout_build_ext} ) diff --git a/src/klayout.pri b/src/klayout.pri index e7f90e037..067942aec 100644 --- a/src/klayout.pri +++ b/src/klayout.pri @@ -1,6 +1,7 @@ TL_INC = $$PWD/tl/tl DB_INC = $$PWD/db/db +PEX_INC = $$PWD/pex/pex DRC_INC = $$PWD/drc/drc LVS_INC = $$PWD/lvs/lvs EDT_INC = $$PWD/edt/edt diff --git a/src/klayout.pro b/src/klayout.pro index 2bb0bf555..3e211798d 100644 --- a/src/klayout.pro +++ b/src/klayout.pro @@ -7,6 +7,7 @@ SUBDIRS = \ tl \ gsi \ db \ + pex \ rdb \ lib \ plugins \ @@ -62,6 +63,7 @@ equals(HAVE_PYTHON, "1") { gsi.depends += tl db.depends += gsi +pex.depends += db rdb.depends += db lib.depends += db diff --git a/src/pex/pex.pro b/src/pex/pex.pro new file mode 100644 index 000000000..9bcdb2590 --- /dev/null +++ b/src/pex/pex.pro @@ -0,0 +1,6 @@ + +TEMPLATE = subdirs +SUBDIRS = pex unit_tests + +unit_tests.depends += pex + diff --git a/src/pex/pex/gsiDeclRExtractor.cc b/src/pex/pex/gsiDeclRExtractor.cc new file mode 100644 index 000000000..983ec13f6 --- /dev/null +++ b/src/pex/pex/gsiDeclRExtractor.cc @@ -0,0 +1,44 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "pexRExtractor.h" + +namespace gsi +{ + +// @@@ +static pex::RExtractor *new_sqc_rextractor () +{ + return new pex::RExtractor (); +} + +Class decl_RExtractor ("pex", "RExtractor", + gsi::constructor ("square_counting", &new_sqc_rextractor, + "@brief Creates a square counting R extractor\n" + ), + "@brief A base class for the R extractor\n" +); + +} + diff --git a/src/pex/pex/pex.pro b/src/pex/pex/pex.pro new file mode 100644 index 000000000..80b1b5a8b --- /dev/null +++ b/src/pex/pex/pex.pro @@ -0,0 +1,23 @@ + +DESTDIR = $$OUT_PWD/../.. +TARGET = klayout_pex + +include($$PWD/../../lib.pri) + +DEFINES += MAKE_PEX_LIBRARY + +SOURCES = \ + pexForceLink.cc \ + pexRExtractor.cc \ + gsiDeclRExtractor.cc \ + +HEADERS = \ + pexForceLink.h \ + pexRExtractor.h \ + +RESOURCES = \ + +INCLUDEPATH += $$TL_INC $$GSI_INC $$DB_INC +DEPENDPATH += $$TL_INC $$GSI_INC $$DB_INC +LIBS += -L$$DESTDIR -lklayout_tl -lklayout_gsi -lklayout_db + diff --git a/src/pex/pex/pexCommon.h b/src/pex/pex/pexCommon.h new file mode 100644 index 000000000..f978461ea --- /dev/null +++ b/src/pex/pex/pexCommon.h @@ -0,0 +1,51 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 !defined(HDR_pexCommon_h) +# define HDR_pexCommon_h + +# if defined _WIN32 || defined __CYGWIN__ + +# ifdef MAKE_PEX_LIBRARY +# define PEX_PUBLIC __declspec(dllexport) +# else +# define PEX_PUBLIC __declspec(dllimport) +# endif +# define PEX_LOCAL +# define PEX_PUBLIC_TEMPLATE + +# else + +# if __GNUC__ >= 4 || defined(__clang__) +# define PEX_PUBLIC __attribute__ ((visibility ("default"))) +# define PEX_PUBLIC_TEMPLATE __attribute__ ((visibility ("default"))) +# define PEX_LOCAL __attribute__ ((visibility ("hidden"))) +# else +# define PEX_PUBLIC +# define PEX_PUBLIC_TEMPLATE +# define PEX_LOCAL +# endif + +# endif + +#endif diff --git a/src/pex/pex/pexForceLink.cc b/src/pex/pex/pexForceLink.cc new file mode 100644 index 000000000..55c333593 --- /dev/null +++ b/src/pex/pex/pexForceLink.cc @@ -0,0 +1,32 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "pexForceLink.h" + +namespace pex +{ + int _force_link_f () + { + return 0; + } +} + diff --git a/src/pex/pex/pexForceLink.h b/src/pex/pex/pexForceLink.h new file mode 100644 index 000000000..69452fb3b --- /dev/null +++ b/src/pex/pex/pexForceLink.h @@ -0,0 +1,40 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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_pexForceLink +#define HDR_pexForceLink + +#include "pexCommon.h" + +/** + * @file Include this function to force linking of the pex module + */ + +namespace pex +{ + PEX_PUBLIC int _force_link_f (); + static int _force_link_target = _force_link_f (); +} + +#endif + diff --git a/src/pex/pex/pexRExtractor.cc b/src/pex/pex/pexRExtractor.cc new file mode 100644 index 000000000..ca67f5c59 --- /dev/null +++ b/src/pex/pex/pexRExtractor.cc @@ -0,0 +1,36 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "pexRExtractor.h" + +namespace pex +{ + +RExtractor::RExtractor () +{ + // .. nothing yet .. +} + +} + + diff --git a/src/pex/pex/pexRExtractor.h b/src/pex/pex/pexRExtractor.h new file mode 100644 index 000000000..ddc775927 --- /dev/null +++ b/src/pex/pex/pexRExtractor.h @@ -0,0 +1,49 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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_pexRExtractor +#define HDR_pexRExtractor + +#include "pexCommon.h" +#include + +namespace pex +{ + +/** + * @brief A base class for an resistance extractor + * + * The R extractor takes a polyon, a technology definition + * and port definitions and extracts a resistor network. + * + * Ports are points or polygons that define the connection + * points to the network. + */ +struct PEX_PUBLIC RExtractor +{ + RExtractor (); +}; + +} + +#endif + diff --git a/src/pex/unit_tests/pexRExtractorTests.cc b/src/pex/unit_tests/pexRExtractorTests.cc new file mode 100644 index 000000000..c3ad86a08 --- /dev/null +++ b/src/pex/unit_tests/pexRExtractorTests.cc @@ -0,0 +1,31 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "pexRExtractor.h" +#include "tlUnitTest.h" + +TEST(1) +{ + // @@@ +} + diff --git a/src/pex/unit_tests/unit_tests.pro b/src/pex/unit_tests/unit_tests.pro new file mode 100644 index 000000000..c6ccc1b74 --- /dev/null +++ b/src/pex/unit_tests/unit_tests.pro @@ -0,0 +1,16 @@ + +DESTDIR_UT = $$OUT_PWD/../.. +DESTDIR = $$OUT_PWD/.. + +TARGET = pex_tests + +include($$PWD/../../lib_ut.pri) + +SOURCES = \ + pexRExtractorTests.cc \ + +INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC $$PEX_INC +DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC $$PEX_INC + +LIBS += -L$$DESTDIR_UT -lklayout_db -lklayout_tl -lklayout_gsi -lklayout_pex + diff --git a/src/pymod/distutils_src/klayout/pex/__init__.py b/src/pymod/distutils_src/klayout/pex/__init__.py new file mode 100644 index 000000000..883a7f0b1 --- /dev/null +++ b/src/pymod/distutils_src/klayout/pex/__init__.py @@ -0,0 +1,4 @@ + +import sys +from ..pexcore import __all__ +from ..pexcore import * diff --git a/src/pymod/distutils_src/klayout/pexcore.pyi b/src/pymod/distutils_src/klayout/pexcore.pyi new file mode 100644 index 000000000..e69de29bb diff --git a/src/pymod/pex/pex.pro b/src/pymod/pex/pex.pro new file mode 100644 index 000000000..d705cced3 --- /dev/null +++ b/src/pymod/pex/pex.pro @@ -0,0 +1,14 @@ + +TARGET = pexcore +REALMODULE = pex +PYI = pexcore.pyi + +include($$PWD/../pymod.pri) + +SOURCES = \ + pexMain.cc \ + +HEADERS += \ + +LIBS += -lklayout_pex + diff --git a/src/pymod/pex/pexMain.cc b/src/pymod/pex/pexMain.cc new file mode 100644 index 000000000..2f64943d2 --- /dev/null +++ b/src/pymod/pex/pexMain.cc @@ -0,0 +1,31 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "../pymodHelper.h" +#include "pexMain.h" + +static PyObject *pex_module_init (const char *pymod_name, const char *mod_name, const char *mod_description) +{ + return module_init (pymod_name, mod_name, mod_description); +} + +DEFINE_PYMOD_WITH_INIT(pexcore, "pex", "KLayout core module 'pex'", pex_module_init) diff --git a/src/pymod/pex/pexMain.h b/src/pymod/pex/pexMain.h new file mode 100644 index 000000000..0777186bf --- /dev/null +++ b/src/pymod/pex/pexMain.h @@ -0,0 +1,24 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 + +*/ + +// to force linking of the pex module +#include "../../pex/pex/pexForceLink.h" diff --git a/src/pymod/pymod.pro b/src/pymod/pymod.pro index 1fac8d258..b286f9a74 100644 --- a/src/pymod/pymod.pro +++ b/src/pymod/pymod.pro @@ -4,6 +4,7 @@ include($$PWD/../klayout.pri) TEMPLATE = subdirs SUBDIRS = \ db \ + pex \ tl \ rdb \ lib \ diff --git a/src/pymod/unit_tests/pymod_tests.cc b/src/pymod/unit_tests/pymod_tests.cc index 15cca459a..8d78544b7 100644 --- a/src/pymod/unit_tests/pymod_tests.cc +++ b/src/pymod/unit_tests/pymod_tests.cc @@ -84,6 +84,7 @@ PYMODTEST (bridge, "bridge.py") PYMODTEST (import_tl, "import_tl.py") PYMODTEST (import_db, "import_db.py") +PYMODTEST (import_pex, "import_pex.py") PYMODTEST (klayout_db_tests, "klayout_db_tests.py") PYMODTEST (import_rdb, "import_rdb.py") PYMODTEST (import_lay, "import_lay.py") diff --git a/src/with_all_libs.pri b/src/with_all_libs.pri index c1c4812b9..e3576dd0f 100644 --- a/src/with_all_libs.pri +++ b/src/with_all_libs.pri @@ -1,8 +1,8 @@ -INCLUDEPATH += $$RBA_INC $$PYA_INC $$TL_INC $$GSI_INC $$DB_INC $$RDB_INC $$LYM_INC $$LAYBASIC_INC $$LAYVIEW_INC $$ANT_INC $$IMG_INC $$EDT_INC $$LIB_INC $$VERSION_INC -DEPENDPATH += $$RBA_INC $$PYA_INC $$TL_INC $$GSI_INC $$DB_INC $$RDB_INC $$LYM_INC $$LAYBASIC_INC $$LAYVIEW_INC $$ANT_INC $$IMG_INC $$EDT_INC $$LIB_INC $$VERSION_INC +INCLUDEPATH += $$RBA_INC $$PYA_INC $$TL_INC $$GSI_INC $$DB_INC $$PEX_INC $$RDB_INC $$LYM_INC $$LAYBASIC_INC $$LAYVIEW_INC $$ANT_INC $$IMG_INC $$EDT_INC $$LIB_INC $$VERSION_INC +DEPENDPATH += $$RBA_INC $$PYA_INC $$TL_INC $$GSI_INC $$DB_INC $$PEX_INC $$RDB_INC $$LYM_INC $$LAYBASIC_INC $$LAYVIEW_INC $$ANT_INC $$IMG_INC $$EDT_INC $$LIB_INC $$VERSION_INC -LIBS += "$$PYTHONLIBFILE" "$$RUBYLIBFILE" -L$$DESTDIR -lklayout_tl -lklayout_gsi -lklayout_db -lklayout_rdb -lklayout_lym -lklayout_laybasic -lklayout_layview -lklayout_ant -lklayout_img -lklayout_edt -lklayout_lib +LIBS += "$$PYTHONLIBFILE" "$$RUBYLIBFILE" -L$$DESTDIR -lklayout_tl -lklayout_gsi -lklayout_db -lklayout_pex -lklayout_rdb -lklayout_lym -lklayout_laybasic -lklayout_layview -lklayout_ant -lklayout_img -lklayout_edt -lklayout_lib !equals(HAVE_QT, "0") { diff --git a/testdata/pymod/import_pex.py b/testdata/pymod/import_pex.py new file mode 100755 index 000000000..56f3c3bf4 --- /dev/null +++ b/testdata/pymod/import_pex.py @@ -0,0 +1,37 @@ +# KLayout Layout Viewer +# Copyright (C) 2006-2025 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 + +import testprep +import klayout.pex as pex +import unittest +import sys +import os + +# Tests the basic abilities of the module + +class BasicTest(unittest.TestCase): + + def test_1(self): + self.assertEqual("RExtractor" in pex.__all__, True) + +# run unit tests +if __name__ == '__main__': + suite = unittest.TestSuite() + suite = unittest.TestLoader().loadTestsFromTestCase(BasicTest) + + if not unittest.TextTestRunner(verbosity = 1).run(suite).wasSuccessful(): + sys.exit(1) From e764716bdae81ab9e52bc8ca7f51f6082e8dbe80 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 18 Apr 2025 15:12:30 +0200 Subject: [PATCH 146/392] Preserving the hull of simple polygons during insert in a Region, hence skipping the compression --- src/db/db/dbMutableRegion.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db/db/dbMutableRegion.cc b/src/db/db/dbMutableRegion.cc index fef42681a..aa1a6b3a2 100644 --- a/src/db/db/dbMutableRegion.cc +++ b/src/db/db/dbMutableRegion.cc @@ -83,7 +83,7 @@ MutableRegion::insert (const db::SimplePolygon &polygon) { if (polygon.vertices () > 0) { db::Polygon poly; - poly.assign_hull (polygon.begin_hull (), polygon.end_hull ()); + poly.assign_hull (polygon.hull ()); do_insert (poly, 0); } } @@ -93,7 +93,7 @@ MutableRegion::insert (const db::SimplePolygonWithProperties &polygon) { if (polygon.vertices () > 0) { db::Polygon poly; - poly.assign_hull (polygon.begin_hull (), polygon.end_hull ()); + poly.assign_hull (polygon.hull ()); do_insert (poly, polygon.properties_id ()); } } From 71620445ee079c6714044ce4dba3d38f44fd5696 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 18 Apr 2025 15:13:29 +0200 Subject: [PATCH 147/392] Polygon#triangulate now returns a 'raw' region, same for SimplePolygon#triangulate. Working on hm_decomposition (tests, bug fixes etc.) --- src/db/db/dbPLCConvexDecomposition.cc | 35 +--- src/db/db/dbPLCConvexDecomposition.h | 13 +- src/db/db/gsiDeclDbPolygon.cc | 249 ++++++++++++++++---------- testdata/ruby/dbPolygonTest.rb | 26 +++ testdata/ruby/dbSimplePolygonTest.rb | 25 +++ 5 files changed, 220 insertions(+), 128 deletions(-) diff --git a/src/db/db/dbPLCConvexDecomposition.cc b/src/db/db/dbPLCConvexDecomposition.cc index 45eda37ed..a6dcff7c4 100644 --- a/src/db/db/dbPLCConvexDecomposition.cc +++ b/src/db/db/dbPLCConvexDecomposition.cc @@ -433,13 +433,8 @@ ConvexDecomposition::decompose (const db::Polygon &poly, const std::vector &vertexes, const ConvexDecompositionParameters ¶meters, const db::CplxTrans &trans) { - // start with a triangulation - TriangulationParameters param; - param.max_area = 0.0; - param.min_b = 0.0; - Triangulation tri (mp_graph); - tri.triangulate (poly, vertexes, param, trans); + tri.triangulate (poly, vertexes, parameters.tri_param, trans); hertel_mehlhorn_decomposition (tri, parameters); } @@ -461,13 +451,8 @@ ConvexDecomposition::decompose (const db::Polygon &poly, const std::vector &vertexes, const ConvexDecompositionParameters ¶meters, const db::DCplxTrans &trans) { - // start with a triangulation - TriangulationParameters param; - param.max_area = 0.0; - param.min_b = 0.0; - Triangulation tri (mp_graph); - tri.triangulate (poly, vertexes, param, trans); + tri.triangulate (poly, vertexes, parameters.tri_param, trans); hertel_mehlhorn_decomposition (tri, parameters); } @@ -495,13 +475,8 @@ ConvexDecomposition::decompose (const db::Region ®ion, const ConvexDecomposit void ConvexDecomposition::decompose (const db::Region ®ion, const ConvexDecompositionParameters ¶meters, const db::CplxTrans &trans) { - // start with a triangulation - TriangulationParameters param; - param.max_area = 0.0; - param.min_b = 0.0; - Triangulation tri (mp_graph); - tri.triangulate (region, param, trans); + tri.triangulate (region, parameters.tri_param, trans); hertel_mehlhorn_decomposition (tri, parameters); } diff --git a/src/db/db/dbPLCConvexDecomposition.h b/src/db/db/dbPLCConvexDecomposition.h index 03a6949bb..1fa9ce805 100644 --- a/src/db/db/dbPLCConvexDecomposition.h +++ b/src/db/db/dbPLCConvexDecomposition.h @@ -25,6 +25,7 @@ #include "dbCommon.h" #include "dbPLC.h" +#include "dbPLCTriangulation.h" #include #include @@ -37,15 +38,21 @@ namespace db namespace plc { -class Triangulation; - struct DB_PUBLIC ConvexDecompositionParameters { ConvexDecompositionParameters () : with_segments (false), split_edges (false), base_verbosity (30) - { } + { + tri_param.max_area = 0.0; + tri_param.min_b = 0.0; + } + + /** + * @brief The parameters used for the triangulation + */ + TriangulationParameters tri_param; /** * @brief Introduce new segments diff --git a/src/db/db/gsiDeclDbPolygon.cc b/src/db/db/gsiDeclDbPolygon.cc index 1a4fa5335..a5cb6f994 100644 --- a/src/db/db/gsiDeclDbPolygon.cc +++ b/src/db/db/gsiDeclDbPolygon.cc @@ -29,44 +29,76 @@ #include "dbPolygonGenerators.h" #include "dbHash.h" #include "dbPLCTriangulation.h" +#include "dbPLCConvexDecomposition.h" namespace gsi { +const std::string hm_docstring = + "The Hertel-Mehlhorn decomposition starts with a Delaunay triangulation of the polygons and recombines the " + "triangles into convex polygons.\n" + "\n" + "The decomposition is controlled by two parameters: 'with_segments' and 'split_edges'.\n" + "\n" + "If 'with_segments' is true (the default), new segments are introduced perpendicular to the edges forming " + "a concave corner. If false, only diagonals (edges connecting original vertexes) are used.\n" + "\n" + "If 'split_edges' is true, the algorithm is allowed to create collinear edges in the output. In this case, " + "the resulting polygons may contain edges that are split into collinear partial edges. Such edges usually recombine " + "into longer edges when processing the polygon further. When such a recombination happens, the edges no " + "longer correspond to original edges or diagonals. When 'split_edges' is false (the default), the resulting " + "polygons will not contain collinear edges, but the decomposition will be constrained to fewer cut lines." + "\n" + "'max_area' and 'min_b' are the corresponding parameters used for the triangulation (see \\delaunay).\n" +; + +const std::string delaunay_docstring = + "Refinement is implemented by Chew's second algorithm. A maximum area can be given. Triangles " + "larger than this area will be split. In addition 'skinny' triangles will be resolved where " + "possible. 'skinny' is defined in terms of shortest edge to circumcircle radius ratio (b). " + "A minimum number for b can be given. A value of 1.0 corresponds to a minimum angle of 30 degree " + "and is usually a good choice. The algorithm is stable up to roughly 1.2 which corresponds to " + "a minimum angle of abouth 37 degree.\n" + "\n" + "The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t.\n" + "\n" + "Picking a value of 0.0 for max_area and min_b will " + "make the implementation skip the refinement step. In that case, the results are identical to " + "the standard constrained Delaunay triangulation.\n" +; + +const std::string dbu_docstring = + "The 'dbu' parameter a numerical scaling parameter. It should be choosen in a way that the polygon dimensions " + "are \"in the order of 1\" (very roughly) after multiplication with the dbu parameter. A value of 0.001 is suitable " + "for polygons with typical dimensions in the order to 1000 DBU. Usually the default value is good enough.\n" +; + template -static db::Region region_from_triangles (const db::plc::Graph &tri, const T &trans) +static db::Region region_from_graph (const db::plc::Graph &plc, const T &trans) { db::Region result; + result.set_merged_semantics (false); - db::Point pts [3]; - - for (auto t = tri.begin (); t != tri.end (); ++t) { - for (int i = 0; i < 3; ++i) { - pts [i] = trans * *t->vertex (i); - } - db::SimplePolygon poly; - poly.assign_hull (pts + 0, pts + 3); - result.insert (poly); + for (auto t = plc.begin (); t != plc.end (); ++t) { + db::DPolygon dp = t->polygon (); + db::SimplePolygon sp; + sp.assign_hull (dp.hull ().begin (), dp.hull ().end (), trans, false); + result.insert (sp); } return result; } template -static std::vector

polygons_from_triangles (const db::plc::Graph &tri, const T &trans) +static std::vector

polygons_from_graph (const db::plc::Graph &plc, const T &trans) { std::vector

result; - result.reserve (tri.num_polygons ()); + result.reserve (plc.num_polygons ()); - typename P::point_type pts [3]; - - for (auto t = tri.begin (); t != tri.end (); ++t) { - for (int i = 0; i < 3; ++i) { - pts [i] = trans * *t->vertex (i); - } - P poly; - poly.assign_hull (pts + 0, pts + 3); - result.push_back (poly); + for (auto t = plc.begin (); t != plc.end (); ++t) { + db::DPolygon dp = t->polygon (); + result.push_back (P ()); + result.back ().assign_hull (dp.hull ().begin (), dp.hull ().end (), trans, false); } return result; @@ -87,7 +119,43 @@ static db::polygon to_polygon (const db::polygon &p) } template -static db::Region triangulate_ipolygon (const P *p, double max_area = 0.0, double min_b = 0.0, double dbu = 0.001) +static db::Region hm_decompose_ipolygon (const P *p, bool with_segments, bool split_edges, double max_area, double min_b, double dbu) +{ + db::plc::Graph plc; + db::plc::ConvexDecomposition decomp (&plc); + db::plc::ConvexDecompositionParameters param; + param.with_segments = with_segments; + param.split_edges = split_edges; + param.tri_param.max_area = max_area; + param.tri_param.min_b = min_b; + + db::CplxTrans trans = db::CplxTrans (dbu) * db::ICplxTrans (db::Trans (db::Point () - p->box ().center ())); + + decomp.decompose (to_polygon (*p), param, trans); + + return region_from_graph (plc, trans.inverted ()); +} + +template +static std::vector

hm_decompose_dpolygon (const P *p, bool with_segments, bool split_edges, double max_area, double min_b) +{ + db::plc::Graph plc; + db::plc::ConvexDecomposition decomp (&plc); + db::plc::ConvexDecompositionParameters param; + param.with_segments = with_segments; + param.split_edges = split_edges; + param.tri_param.max_area = max_area; + param.tri_param.min_b = min_b; + + db::DCplxTrans trans = db::DCplxTrans (db::DTrans (db::DPoint () - p->box ().center ())); + + decomp.decompose (to_polygon (*p), param, trans); + + return polygons_from_graph (plc, trans.inverted ()); +} + +template +static db::Region triangulate_ipolygon (const P *p, double max_area, double min_b, double dbu) { db::plc::Graph tris; db::plc::Triangulation triangulation (&tris); @@ -99,11 +167,11 @@ static db::Region triangulate_ipolygon (const P *p, double max_area = 0.0, doubl triangulation.triangulate (to_polygon (*p), param, trans); - return region_from_triangles (tris, trans.inverted ()); + return region_from_graph (tris, trans.inverted ()); } template -static db::Region triangulate_ipolygon_v (const P *p, const std::vector &vertexes, double max_area = 0.0, double min_b = 0.0, double dbu = 0.001) +static db::Region triangulate_ipolygon_v (const P *p, const std::vector &vertexes, double max_area, double min_b, double dbu) { db::plc::Graph tris; db::plc::Triangulation triangulation (&tris); @@ -115,7 +183,7 @@ static db::Region triangulate_ipolygon_v (const P *p, const std::vector @@ -131,7 +199,7 @@ static std::vector

triangulate_dpolygon (const P *p, double max_area = 0.0, d triangulation.triangulate (to_polygon (*p), param, trans); - return polygons_from_triangles (tris, trans.inverted ()); + return polygons_from_graph (tris, trans.inverted ()); } template @@ -147,7 +215,7 @@ static std::vector

triangulate_dpolygon_v (const P *p, const std::vector (tris, trans.inverted ()); + return polygons_from_graph (tris, trans.inverted ()); } template @@ -884,36 +952,35 @@ Class decl_SimplePolygon ("db", "SimplePolygon", "\n" "This method has been introduced in version 0.18.\n" ) + + method_ext ("hm_decomposition", &hm_decompose_ipolygon, + gsi::arg ("with_segments", true), gsi::arg ("split_edges", false), + gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), + gsi::arg ("dbu", 0.001), + "@brief Performs a Hertel-Mehlhorn convex decomposition.\n" + "\n" + "@return A \\Region holding the polygons of the decomposition.\n" + "The resulting region is in 'no merged semantics' mode, " + "to avoid re-merging of the polygons during following operations.\n" + "\n" + hm_docstring + "\n" + dbu_docstring + "\n" + "This method has been introduced in version 0.30.1." + ) + method_ext ("delaunay", &triangulate_ipolygon, gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), gsi::arg ("dbu", 0.001), "@brief Performs a Delaunay triangulation of the polygon.\n" "\n" "@return A \\Region holding the triangles of the refined, constrained Delaunay triangulation.\n" - "\n" - "Refinement is implemented by Chew's second algorithm. A maximum area can be given. Triangles " - "larger than this area will be split. In addition 'skinny' triangles will be resolved where " - "possible. 'skinny' is defined in terms of shortest edge to circumcircle radius ratio (b). " - "A minimum number for b can be given. A value of 1.0 corresponds to a minimum angle of 30 degree " - "and is usually a good choice. The algorithm is stable up to roughly 1.2 which corresponds to " - "a minimum angle of abouth 37 degree.\n" - "\n" - "The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t.\n" - "\n" - "The area value is given in terms of DBU units. Picking a value of 0.0 for area and min b will " - "make the implementation skip the refinement step. In that case, the results are identical to " - "the standard constrained Delaunay triangulation.\n" - "\n" - "The 'dbu' parameter a numerical scaling parameter. It should be choosen in a way that the polygon dimensions " - "are \"in the order of 1\" (very roughly) after multiplication with the dbu parameter. A value of 0.001 is suitable " - "for polygons with typical dimensions in the order to 1000 DBU. Usually the default value is good enough.\n" - "\n" - "This method has been introduced in version 0.30." + "\n" + delaunay_docstring + "\n" + "The area value is given in terms of DBU units.\n" + "\n" + dbu_docstring + "\n" + "This method has been introduced in version 0.30. Since version 0.30.1, the resulting region is in 'no merged semantics' mode, " + "to avoid re-merging of the triangles during following operations." ) + method_ext ("delaunay", &triangulate_ipolygon_v, gsi::arg ("vertexes"), gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), gsi::arg ("dbu", 0.001), "@brief Performs a Delaunay triangulation of the polygon.\n" "\n" "This variant of the triangulation function accepts an array of additional vertexes for the triangulation.\n" "\n" - "This method has been introduced in version 0.30." + "This method has been introduced in version 0.30. Since version 0.30.1, the resulting region is in 'no merged semantics' mode, " + "to avoid re-merging of the triangles during following operations." ) + simple_polygon_defs::methods (), "@brief A simple polygon class\n" @@ -1010,24 +1077,20 @@ Class decl_DSimplePolygon ("db", "DSimplePolygon", "\n" "This method has been introduced in version 0.25.\n" ) + + method_ext ("hm_decomposition", &hm_decompose_dpolygon, + gsi::arg ("with_segments", true), gsi::arg ("split_edges", false), + gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), + "@brief Performs a Hertel-Mehlhorn convex decomposition.\n" + "\n" + "@return An array holding the polygons of the decomposition.\n" + "\n" + hm_docstring + "\n" + "This method has been introduced in version 0.30.1." + ) + method_ext ("delaunay", &triangulate_dpolygon, gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), "@brief Performs a Delaunay triangulation of the polygon.\n" "\n" "@return An array of triangular polygons of the refined, constrained Delaunay triangulation.\n" - "\n" - "Refinement is implemented by Chew's second algorithm. A maximum area can be given. Triangles " - "larger than this area will be split. In addition 'skinny' triangles will be resolved where " - "possible. 'skinny' is defined in terms of shortest edge to circumcircle radius ratio (b). " - "A minimum number for b can be given. A value of 1.0 corresponds to a minimum angle of 30 degree " - "and is usually a good choice. The algorithm is stable up to roughly 1.2 which corresponds to " - "a minimum angle of abouth 37 degree.\n" - "\n" - "The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t.\n" - "\n" - "Picking a value of 0.0 for max area and min b will " - "make the implementation skip the refinement step. In that case, the results are identical to " - "the standard constrained Delaunay triangulation.\n" - "\n" + "\n" + delaunay_docstring + "\n" "This method has been introduced in version 0.30." ) + method_ext ("delaunay", &triangulate_dpolygon_v, gsi::arg ("vertexes"), gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), @@ -2211,36 +2274,36 @@ Class decl_Polygon ("db", "Polygon", "\n" "This method was introduced in version 0.18.\n" ) + + method_ext ("hm_decomposition", &hm_decompose_ipolygon, + gsi::arg ("with_segments", true), gsi::arg ("split_edges", false), + gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), + gsi::arg ("dbu", 0.001), + "@brief Performs a Hertel-Mehlhorn convex decomposition.\n" + "\n" + "@return A \\Region holding the polygons of the decomposition.\n" + "\n" + "The resulting region is in 'no merged semantics' mode, " + "to avoid re-merging of the polygons during following operations.\n" + "\n" + hm_docstring + "\n" + dbu_docstring + "\n" + "This method has been introduced in version 0.30.1." + ) + method_ext ("delaunay", &triangulate_ipolygon, gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), gsi::arg ("dbu", 0.001), "@brief Performs a Delaunay triangulation of the polygon.\n" "\n" "@return A \\Region holding the triangles of the refined, constrained Delaunay triangulation.\n" - "\n" - "Refinement is implemented by Chew's second algorithm. A maximum area can be given. Triangles " - "larger than this area will be split. In addition 'skinny' triangles will be resolved where " - "possible. 'skinny' is defined in terms of shortest edge to circumcircle radius ratio (b). " - "A minimum number for b can be given. A value of 1.0 corresponds to a minimum angle of 30 degree " - "and is usually a good choice. The algorithm is stable up to roughly 1.2 which corresponds to " - "a minimum angle of abouth 37 degree.\n" - "\n" - "The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t.\n" - "\n" - "The area value is given in terms of DBU units. Picking a value of 0.0 for area and min b will " - "make the implementation skip the refinement step. In that case, the results are identical to " - "the standard constrained Delaunay triangulation.\n" - "\n" - "The 'dbu' parameter a numerical scaling parameter. It should be choosen in a way that the polygon dimensions " - "are \"in the order of 1\" (very roughly) after multiplication with the dbu parameter. A value of 0.001 is suitable " - "for polygons with typical dimensions in the order to 1000 DBU. Usually the default value is good enough.\n" - "\n" - "This method has been introduced in version 0.30." + "\n" + delaunay_docstring + "\n" + "The area value is given in terms of DBU units.\n" + "\n" + dbu_docstring + "\n" + "This method has been introduced in version 0.30. Since version 0.30.1, the resulting region is in 'no merged semantics' mode, " + "to avoid re-merging of the triangles during following operations." ) + method_ext ("delaunay", &triangulate_ipolygon_v, gsi::arg ("vertexes"), gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), gsi::arg ("dbu", 0.001), "@brief Performs a Delaunay triangulation of the polygon.\n" "\n" "This variant of the triangulation function accepts an array of additional vertexes for the triangulation.\n" "\n" - "This method has been introduced in version 0.30." + "This method has been introduced in version 0.30. Since version 0.30.1, the resulting region is in 'no merged semantics' mode, " + "to avoid re-merging of the triangles during following operations." ) + polygon_defs::methods (), "@brief A polygon class\n" @@ -2364,24 +2427,20 @@ Class decl_DPolygon ("db", "DPolygon", "\n" "This method has been introduced in version 0.25.\n" ) + + method_ext ("hm_decomposition", &hm_decompose_dpolygon, + gsi::arg ("with_segments", true), gsi::arg ("split_edges", false), + gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), + "@brief Performs a Hertel-Mehlhorn convex decomposition.\n" + "\n" + "@return An array holding the polygons of the decomposition.\n" + "\n" + hm_docstring + "\n" + "This method has been introduced in version 0.30.1." + ) + method_ext ("delaunay", &triangulate_dpolygon, gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), "@brief Performs a Delaunay triangulation of the polygon.\n" "\n" "@return An array of triangular polygons of the refined, constrained Delaunay triangulation.\n" - "\n" - "Refinement is implemented by Chew's second algorithm. A maximum area can be given. Triangles " - "larger than this area will be split. In addition 'skinny' triangles will be resolved where " - "possible. 'skinny' is defined in terms of shortest edge to circumcircle radius ratio (b). " - "A minimum number for b can be given. A value of 1.0 corresponds to a minimum angle of 30 degree " - "and is usually a good choice. The algorithm is stable up to roughly 1.2 which corresponds to " - "a minimum angle of abouth 37 degree.\n" - "\n" - "The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t.\n" - "\n" - "Picking a value of 0.0 for max area and min b will " - "make the implementation skip the refinement step. In that case, the results are identical to " - "the standard constrained Delaunay triangulation.\n" - "\n" + "\n" + delaunay_docstring + "\n" "This method has been introduced in version 0.30." ) + method_ext ("delaunay", &triangulate_dpolygon_v, gsi::arg ("vertexes"), gsi::arg ("max_area", 0.0), gsi::arg ("min_b", 0.0), diff --git a/testdata/ruby/dbPolygonTest.rb b/testdata/ruby/dbPolygonTest.rb index 3ac9c74e4..c9de10f8e 100644 --- a/testdata/ruby/dbPolygonTest.rb +++ b/testdata/ruby/dbPolygonTest.rb @@ -1006,6 +1006,32 @@ class DBPolygon_TestClass < TestBase end + def sorted_polygons(arr) + arr.each.collect { |p| p.downcast.to_s }.sort.join(";") + end + + def sorted_dpolygons(arr) + arr.each.collect { |p| p.to_s }.sort.join(";") + end + + def test_hm_decomposition + + p = RBA::Polygon::new([ [0, 0], [0, 100], [1000, 100], [1000, 1000], [1100, 1000], [1100, 100], [2200, 100], [2200, 0] ]) + assert_equal(sorted_polygons(p.hm_decomposition(false, false)), "(0,0;0,100;1000,100);(0,0;1000,100;1100,100);(0,0;1100,100;2200,100;2200,0);(1000,100;1000,1000;1100,1000;1100,100)") + assert_equal(sorted_polygons(p.hm_decomposition(true, false)), "(0,0;0,100;1000,100;1000,0);(1000,0;1000,100;1100,100;1100,0);(1000,100;1000,1000;1100,1000;1100,100);(1100,0;1100,100;2200,100;2200,0)") + assert_equal(sorted_polygons(p.hm_decomposition(false, true)), "(0,0;0,100;1000,100;1100,100;2200,100;2200,0);(1000,100;1000,1000;1100,1000;1100,100)") + assert_equal(sorted_polygons(p.hm_decomposition(true, true)), "(0,0;0,100;1000,100;1000,0);(1000,0;1000,100;1000,1000;1100,1000;1100,100;1100,0);(1100,0;1100,100;2200,100;2200,0)") + assert_equal(sorted_polygons(p.hm_decomposition(false, false, 0.0, 0.5)), "(0,0;0,100;500,100;1000,100;1100,0;825,0;550,0;275,0);(1000,100;1000,550;1000,775;1000,1000;1100,1000;1100,550;1100,325;1100,100);(1100,0;1000,100;1100,100);(1100,0;1100,100;1650,100;1925,100;2200,100;2200,0;1650,0;1375,0)") + + p = RBA::DPolygon::new([ [0, 0], [0, 100], [1000, 100], [1000, 1000], [1100, 1000], [1100, 100], [2200, 100], [2200, 0] ]) + assert_equal(sorted_dpolygons(p.hm_decomposition(false, false)), "(0,0;0,100;1000,100);(0,0;1000,100;1100,100);(0,0;1100,100;2200,100;2200,0);(1000,100;1000,1000;1100,1000;1100,100)") + assert_equal(sorted_dpolygons(p.hm_decomposition(true, false)), "(0,0;0,100;1000,100;1000,0);(1000,0;1000,100;1100,100;1100,0);(1000,100;1000,1000;1100,1000;1100,100);(1100,0;1100,100;2200,100;2200,0)") + assert_equal(sorted_dpolygons(p.hm_decomposition(false, true)), "(0,0;0,100;1000,100;1100,100;2200,100;2200,0);(1000,100;1000,1000;1100,1000;1100,100)") + assert_equal(sorted_dpolygons(p.hm_decomposition(true, true)), "(0,0;0,100;1000,100;1000,0);(1000,0;1000,100;1000,1000;1100,1000;1100,100;1100,0);(1100,0;1100,100;2200,100;2200,0)") + assert_equal(sorted_dpolygons(p.hm_decomposition(false, false, 0.0, 0.5)), "(0,0;0,100;500,100;1000,100;1100,0;825,0;550,0;275,0);(1000,100;1000,550;1000,775;1000,1000;1100,1000;1100,550;1100,325;1100,100);(1100,0;1000,100;1100,100);(1100,0;1100,100;1650,100;1925,100;2200,100;2200,0;1650,0;1375,0)") + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/dbSimplePolygonTest.rb b/testdata/ruby/dbSimplePolygonTest.rb index 268d675ca..ee30323fe 100644 --- a/testdata/ruby/dbSimplePolygonTest.rb +++ b/testdata/ruby/dbSimplePolygonTest.rb @@ -438,6 +438,31 @@ class DBSimplePolygon_TestClass < TestBase end + def sorted_polygons(arr) + arr.each.collect { |p| p.downcast.to_s }.sort.join(";") + end + + def sorted_dpolygons(arr) + arr.each.collect { |p| p.to_s }.sort.join(";") + end + + def test_hm_decomposition + + p = RBA::SimplePolygon::new([ [0, 0], [0, 100], [1000, 100], [1000, 1000], [1100, 1000], [1100, 100], [2200, 100], [2200, 0] ]) + assert_equal(sorted_polygons(p.hm_decomposition(false, false)), "(0,0;0,100;1000,100);(0,0;1000,100;1100,100);(0,0;1100,100;2200,100;2200,0);(1000,100;1000,1000;1100,1000;1100,100)") + assert_equal(sorted_polygons(p.hm_decomposition(true, false)), "(0,0;0,100;1000,100;1000,0);(1000,0;1000,100;1100,100;1100,0);(1000,100;1000,1000;1100,1000;1100,100);(1100,0;1100,100;2200,100;2200,0)") + assert_equal(sorted_polygons(p.hm_decomposition(false, true)), "(0,0;0,100;1000,100;1100,100;2200,100;2200,0);(1000,100;1000,1000;1100,1000;1100,100)") + assert_equal(sorted_polygons(p.hm_decomposition(true, true)), "(0,0;0,100;1000,100;1000,0);(1000,0;1000,100;1000,1000;1100,1000;1100,100;1100,0);(1100,0;1100,100;2200,100;2200,0)") + assert_equal(sorted_polygons(p.hm_decomposition(false, false, 0.0, 0.5)), "(0,0;0,100;500,100;1000,100;1100,0;825,0;550,0;275,0);(1000,100;1000,550;1000,775;1000,1000;1100,1000;1100,550;1100,325;1100,100);(1100,0;1000,100;1100,100);(1100,0;1100,100;1650,100;1925,100;2200,100;2200,0;1650,0;1375,0)") + + p = RBA::DSimplePolygon::new([ [0, 0], [0, 100], [1000, 100], [1000, 1000], [1100, 1000], [1100, 100], [2200, 100], [2200, 0] ]) + assert_equal(sorted_dpolygons(p.hm_decomposition(false, false)), "(0,0;0,100;1000,100);(0,0;1000,100;1100,100);(0,0;1100,100;2200,100;2200,0);(1000,100;1000,1000;1100,1000;1100,100)") + assert_equal(sorted_dpolygons(p.hm_decomposition(true, false)), "(0,0;0,100;1000,100;1000,0);(1000,0;1000,100;1100,100;1100,0);(1000,100;1000,1000;1100,1000;1100,100);(1100,0;1100,100;2200,100;2200,0)") + assert_equal(sorted_dpolygons(p.hm_decomposition(false, true)), "(0,0;0,100;1000,100;1100,100;2200,100;2200,0);(1000,100;1000,1000;1100,1000;1100,100)") + assert_equal(sorted_dpolygons(p.hm_decomposition(true, true)), "(0,0;0,100;1000,100;1000,0);(1000,0;1000,100;1000,1000;1100,1000;1100,100;1100,0);(1100,0;1100,100;2200,100;2200,0)") + assert_equal(sorted_dpolygons(p.hm_decomposition(false, false, 0.0, 0.5)), "(0,0;0,100;500,100;1000,100;1100,0;825,0;550,0;275,0);(1000,100;1000,550;1000,775;1000,1000;1100,1000;1100,550;1100,325;1100,100);(1100,0;1000,100;1100,100);(1100,0;1100,100;1650,100;1925,100;2200,100;2200,0;1650,0;1375,0)") + + end end load("test_epilogue.rb") From 8a122c8a7d5a4e7776f22689d8470f6687186e7b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 18 Apr 2025 23:29:29 +0200 Subject: [PATCH 148/392] WIP: bug fixes, one more test --- scripts/make_stubs.sh | 3 + src/db/db/dbPLCConvexDecomposition.cc | 12 +- src/db/db/dbPLCConvexDecomposition.h | 3 + src/db/db/dbPLCTriangulation.cc | 8 +- src/db/db/dbPLCTriangulation.h | 8 +- .../dbPLCConvexDecompositionTests.cc | 34 ++++ src/pex/pex/gsiDeclRExtractor.cc | 6 +- src/pex/pex/pexRExtractor.cc | 135 +++++++++++++ src/pex/pex/pexRExtractor.h | 136 ++++++++++++- src/pymod/distutils_src/klayout/dbcore.pyi | 190 +++++++++++++----- src/pymod/distutils_src/klayout/pexcore.pyi | 5 + src/pymod/distutils_src/klayout/tlcore.pyi | 4 +- testdata/algo/hm_decomposition_au5.gds | Bin 0 -> 1314 bytes 13 files changed, 479 insertions(+), 65 deletions(-) create mode 100644 testdata/algo/hm_decomposition_au5.gds diff --git a/scripts/make_stubs.sh b/scripts/make_stubs.sh index e9f5bad03..68e39f1c2 100755 --- a/scripts/make_stubs.sh +++ b/scripts/make_stubs.sh @@ -57,6 +57,9 @@ $python $inst/stubgen.py tl >$pyi_srcdir/tlcore.pyi echo "Generating stubs for db .." $python $inst/stubgen.py db tl,lay,rdb >$pyi_srcdir/dbcore.pyi +echo "Generating stubs for pex .." +$python $inst/stubgen.py pex tl,db >$pyi_srcdir/pexcore.pyi + echo "Generating stubs for rdb .." $python $inst/stubgen.py rdb tl,db >$pyi_srcdir/rdbcore.pyi diff --git a/src/db/db/dbPLCConvexDecomposition.cc b/src/db/db/dbPLCConvexDecomposition.cc index a6dcff7c4..2723eb796 100644 --- a/src/db/db/dbPLCConvexDecomposition.cc +++ b/src/db/db/dbPLCConvexDecomposition.cc @@ -146,7 +146,7 @@ ConvexDecomposition::collect_concave_vertexes (std::vector &conca // and outgoing edge. Edge *start_segment = segment; - Vertex *vto = segment->right () ? segment->v2 () : segment->v1 (); + Vertex *vto = (segment->right () && ! segment->right ()->is_outside ()) ? segment->v2 () : segment->v1 (); do { @@ -338,7 +338,9 @@ ConvexDecomposition::hertel_mehlhorn_decomposition (Triangulation &tris, const C } for (auto i = angles_and_edges.begin (); i != angles_and_edges.end (); ++i) { - essential_edges.insert (i->second); + if (i->second) { + essential_edges.insert (i->second); + } } } @@ -347,7 +349,9 @@ ConvexDecomposition::hertel_mehlhorn_decomposition (Triangulation &tris, const C std::unordered_set left_triangles; for (auto it = mp_graph->begin (); it != mp_graph->end (); ++it) { - left_triangles.insert (it.operator-> ()); + if (! it->is_outside ()) { + left_triangles.insert (it.operator-> ()); + } } std::list > polygons; @@ -385,7 +389,7 @@ ConvexDecomposition::hertel_mehlhorn_decomposition (Triangulation &tris, const C ivs.insert (e->v2 ()); } - if (! qq || essential_edges.find (e) != essential_edges.end ()) { + if (! qq || qq->is_outside () || essential_edges.find (e) != essential_edges.end ()) { edges.insert (const_cast (e)); // TODO: ugly const_cast } else if (left_triangles.find (qq) != left_triangles.end ()) { next_queue.push_back (qq); diff --git a/src/db/db/dbPLCConvexDecomposition.h b/src/db/db/dbPLCConvexDecomposition.h index 1fa9ce805..6b0effc43 100644 --- a/src/db/db/dbPLCConvexDecomposition.h +++ b/src/db/db/dbPLCConvexDecomposition.h @@ -47,6 +47,9 @@ struct DB_PUBLIC ConvexDecompositionParameters { tri_param.max_area = 0.0; tri_param.min_b = 0.0; + + // Needed for the algorithm - don't change this + tri_param.remove_outside_triangles = false; } /** diff --git a/src/db/db/dbPLCTriangulation.cc b/src/db/db/dbPLCTriangulation.cc index d148656a6..3c9371fe4 100644 --- a/src/db/db/dbPLCTriangulation.cc +++ b/src/db/db/dbPLCTriangulation.cc @@ -1522,7 +1522,9 @@ Triangulation::refine (const TriangulationParameters ¶meters) if (parameters.min_b < db::epsilon && parameters.max_area < db::epsilon && parameters.max_area_border < db::epsilon) { // no refinement requested - we're done. - remove_outside_triangles (); + if (parameters.remove_outside_triangles) { + remove_outside_triangles (); + } return; } @@ -1690,7 +1692,9 @@ Triangulation::refine (const TriangulationParameters ¶meters) } - remove_outside_triangles (); + if (parameters.remove_outside_triangles) { + remove_outside_triangles (); + } } } // namespace plc diff --git a/src/db/db/dbPLCTriangulation.h b/src/db/db/dbPLCTriangulation.h index af17aa876..ea0830b00 100644 --- a/src/db/db/dbPLCTriangulation.h +++ b/src/db/db/dbPLCTriangulation.h @@ -46,7 +46,8 @@ struct DB_PUBLIC TriangulationParameters max_area_border (0.0), max_iterations (std::numeric_limits::max ()), base_verbosity (30), - mark_triangles (false) + mark_triangles (false), + remove_outside_triangles (true) { } /** @@ -93,6 +94,11 @@ struct DB_PUBLIC TriangulationParameters * Bit 2: non-Delaunay (in the strict sense) */ bool mark_triangles; + + /** + * @brief If false, the outside triangles are not removed after triangulation + */ + bool remove_outside_triangles; }; /** diff --git a/src/db/unit_tests/dbPLCConvexDecompositionTests.cc b/src/db/unit_tests/dbPLCConvexDecompositionTests.cc index 2cb1e1a70..5ab556ab8 100644 --- a/src/db/unit_tests/dbPLCConvexDecompositionTests.cc +++ b/src/db/unit_tests/dbPLCConvexDecompositionTests.cc @@ -153,3 +153,37 @@ TEST(internal_vertex) EXPECT_EQ (++p == plc.end (), true); } +TEST(problematic_polygon) +{ + db::Point contour[] = { + db::Point (14590, 990), + db::Point (6100, 990), + db::Point (7360, 4450), + db::Point (2280, 4450), + db::Point (2280, 6120), + db::Point (7360, 6120), + db::Point (8760, 7490), + db::Point (13590, 17100), + db::Point (10280, 6120), + db::Point (26790, 13060), + db::Point (41270, 970) + }; + + db::Polygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + + double dbu = 0.001; + + db::plc::ConvexDecompositionParameters param; + param.with_segments = true; + param.split_edges = false; + + db::plc::Graph plc; + TestableConvexDecomposition decomp (&plc); + + decomp.decompose (poly, param, dbu); + + std::unique_ptr ly (plc.to_layout ()); + db::compare_layouts (_this, *ly, tl::testdata () + "/algo/hm_decomposition_au5.gds"); +} + diff --git a/src/pex/pex/gsiDeclRExtractor.cc b/src/pex/pex/gsiDeclRExtractor.cc index 983ec13f6..a0bc54078 100644 --- a/src/pex/pex/gsiDeclRExtractor.cc +++ b/src/pex/pex/gsiDeclRExtractor.cc @@ -28,13 +28,13 @@ namespace gsi { // @@@ -static pex::RExtractor *new_sqc_rextractor () +static pex::RExtractor *new_sqc_rextractor (double dbu) { - return new pex::RExtractor (); + return new pex::SquareCountingRExtractor (dbu); } Class decl_RExtractor ("pex", "RExtractor", - gsi::constructor ("square_counting", &new_sqc_rextractor, + gsi::constructor ("square_counting", &new_sqc_rextractor, gsi::arg ("dbu"), "@brief Creates a square counting R extractor\n" ), "@brief A base class for the R extractor\n" diff --git a/src/pex/pex/pexRExtractor.cc b/src/pex/pex/pexRExtractor.cc index ca67f5c59..5d7a30eab 100644 --- a/src/pex/pex/pexRExtractor.cc +++ b/src/pex/pex/pexRExtractor.cc @@ -26,11 +26,146 @@ namespace pex { +RNetwork::RNetwork () +{ + // .. nothing yet .. +} + +RNetwork::~RNetwork () +{ + clear (); +} + +void +RNetwork::clear () +{ + m_elements.clear (); // must come before m_nodes + m_nodes.clear (); + m_elements_by_nodes.clear (); + m_nodes_by_type.clear (); +} + +std::map, RElement *> m_elements_by_nodes; +std::map, RNode *> m_nodes; + +RNode * +RNetwork::create_node (RNode::node_type type, unsigned int port_index) +{ + if (type != RNode::INTERNAL) { + + auto i = m_nodes_by_type.find (std::make_pair (type, port_index)); + if (i != m_nodes_by_type.end ()) { + + return i->second; + + } else { + + RNode *new_node = new RNode (type, db::DBox (), port_index); + m_nodes.push_back (new_node); + m_nodes_by_type.insert (std::make_pair (std::make_pair (type, port_index), new_node)); + + return new_node; + } + + } else { + + RNode *new_node = new RNode (type, db::DBox (), port_index); + m_nodes.push_back (new_node); + return new_node; + + } +} + +RElement * +RNetwork::create_element (double conductivity, RNode *a, RNode *b) +{ + auto i = m_elements_by_nodes.find (std::make_pair (a, b)); + if (i != m_elements_by_nodes.end ()) { + + i->second->conductivity += conductivity; + return i->second; + + } else { + + RElement *element = new RElement (conductivity, a, b); + a->elements.push_back (element); + element->m_ia = --a->elements.end (); + b->elements.push_back (element); + element->m_ia = --b->elements.end (); + + m_elements_by_nodes.insert (std::make_pair (std::make_pair (a, b), element)); + return element; + + } +} + +void +RNetwork::remove_node (RNode *node) +{ + tl_assert (node->type == RNode::INTERNAL); + while (! node->elements.empty ()) { + remove_element (const_cast (node->elements.front ())); + } + delete node; +} + +void +RNetwork::remove_element (RElement *element) +{ + RNode *a = const_cast (element->a); + RNode *b = const_cast (element->b); + + delete element; + + if (a && a->type == RNode::INTERNAL && a->elements.empty ()) { + delete a; + } + if (b && b->type == RNode::INTERNAL && b->elements.empty ()) { + delete b; + } +} + + RExtractor::RExtractor () { // .. nothing yet .. } +RExtractor::~RExtractor () +{ + // .. nothing yet .. +} + + +SquareCountingRExtractor::SquareCountingRExtractor (double dbu) +{ + m_dbu = dbu; + + m_decomp_param.split_edges = true; + m_decomp_param.with_segments = false; +} + +void +SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector &vertex_ports, const std::vector &polygon_ports, RNetwork &rnetwork) +{ + db::CplxTrans trans = db::CplxTrans (m_dbu) * db::ICplxTrans (db::Trans (db::Point () - polygon.box ().center ())); + auto inv_trans = trans.inverted (); + + db::plc::Graph plc; + + db::plc::ConvexDecomposition decomp (&plc); + decomp.decompose (polygon, vertex_ports, m_decomp_param, trans); + + std::vector > decomp_polygons; + for (auto p = plc.begin (); p != plc.end (); ++p) { + // @@@decomp_polygons.push_back (db::Polygon ()); + // @@@decomp_polygons.back ().first = inv_trans * p->polygon (); + } + + // @@@ use box_scanner to find interactions between polygon_ports and decomp_polygons + +} + } diff --git a/src/pex/pex/pexRExtractor.h b/src/pex/pex/pexRExtractor.h index ddc775927..dccc95616 100644 --- a/src/pex/pex/pexRExtractor.h +++ b/src/pex/pex/pexRExtractor.h @@ -24,11 +24,112 @@ #define HDR_pexRExtractor #include "pexCommon.h" + +#include "dbPolygon.h" +#include "dbPLC.h" +#include "dbPLCConvexDecomposition.h" // @@@ +#include "dbPLCTriangulation.h" // @@@ +#include "tlList.h" + #include +#include namespace pex { +class RElement; +class RNode; + +struct PEX_PUBLIC RNode + : public tl::list_node +{ +public: + enum node_type { + INTERNAL, + VERTEX_PORT, + POLYGON_PORT + }; + + node_type type; + db::DBox location; + unsigned int port_index; + mutable std::list elements; + +protected: + friend class RNetwork; + friend class tl::list_impl; + + RNode (node_type _type, const db::DBox &_location, unsigned int _port_index) + : type (_type), location (_location), port_index (_port_index) + { } + + ~RNode () { } + +private: + RNode (const RNode &other); + RNode &operator= (const RNode &other); +}; + +struct PEX_PUBLIC RElement + : public tl::list_node +{ + double conductivity; + const RNode *a, *b; + + double resistance () const + { + return 1.0 / conductivity; + } + +protected: + friend class RNetwork; + friend class tl::list_impl; + + RElement (double _conductivity, const RNode *_a, const RNode *_b) + : conductivity (_conductivity), a (_a), b (_b) + { } + + ~RElement () + { + if (a) { + a->elements.erase (m_ia); + } + if (b) { + b->elements.erase (m_ib); + } + a = b = 0; + } + + std::list::iterator m_ia, m_ib; + +private: + RElement (const RElement &other); + RElement &operator= (const RElement &other); +}; + +class PEX_PUBLIC RNetwork +{ +public: + RNetwork (); + ~RNetwork (); + + RNode *create_node (RNode::node_type type, unsigned int port_index); + RElement *create_element (double conductivity, RNode *a, RNode *b); + void remove_element (RElement *element); + void remove_node (RNode *node); + void clear (); + +private: + tl::list m_nodes; + tl::list m_elements; + std::map, RElement *> m_elements_by_nodes; + std::map, RNode *> m_nodes_by_type; + + RNetwork (const RNetwork &); + RNetwork &operator= (const RNetwork &); +}; + + /** * @brief A base class for an resistance extractor * @@ -38,9 +139,42 @@ namespace pex * Ports are points or polygons that define the connection * points to the network. */ -struct PEX_PUBLIC RExtractor +class PEX_PUBLIC RExtractor { +public: RExtractor (); + virtual ~RExtractor (); + + virtual void extract (const db::Polygon &polygon, const std::vector &vertex_ports, const std::vector &polygon_ports, RNetwork &rnetwork) = 0; +}; + +// @@@ +class PEX_PUBLIC SquareCountingRExtractor + : public RExtractor +{ +public: + SquareCountingRExtractor (double dbu); + + db::plc::ConvexDecompositionParameters &decomposition_parameters () + { + return m_decomp_param; + } + + void set_dbu (double dbu) + { + m_dbu = dbu; + } + + double dbu () const + { + return m_dbu; + } + + virtual void extract (const db::Polygon &polygon, const std::vector &vertex_ports, const std::vector &polygon_ports, RNetwork &rnetwork); + +private: + db::plc::ConvexDecompositionParameters m_decomp_param; + double m_dbu; }; } diff --git a/src/pymod/distutils_src/klayout/dbcore.pyi b/src/pymod/distutils_src/klayout/dbcore.pyi index a9d4ed50c..989021861 100644 --- a/src/pymod/distutils_src/klayout/dbcore.pyi +++ b/src/pymod/distutils_src/klayout/dbcore.pyi @@ -13813,7 +13813,7 @@ class DPolygon: The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t. - Picking a value of 0.0 for max area and min b will make the implementation skip the refinement step. In that case, the results are identical to the standard constrained Delaunay triangulation. + Picking a value of 0.0 for max_area and min_b will make the implementation skip the refinement step. In that case, the results are identical to the standard constrained Delaunay triangulation. This method has been introduced in version 0.30. """ @@ -13912,6 +13912,24 @@ class DPolygon: This method has been introduced in version 0.25. """ ... + def hm_decomposition(self, with_segments: Optional[bool] = ..., split_edges: Optional[bool] = ..., max_area: Optional[float] = ..., min_b: Optional[float] = ...) -> List[DPolygon]: + r""" + @brief Performs a Hertel-Mehlhorn convex decomposition. + + @return An array holding the polygons of the decomposition. + + The Hertel-Mehlhorn decomposition starts with a Delaunay triangulation of the polygons and recombines the triangles into convex polygons. + + The decomposition is controlled by two parameters: 'with_segments' and 'split_edges'. + + If 'with_segments' is true (the default), new segments are introduced perpendicular to the edges forming a concave corner. If false, only diagonals (edges connecting original vertexes) are used. + + If 'split_edges' is true, the algorithm is allowed to create collinear edges in the output. In this case, the resulting polygons may contain edges that are split into collinear partial edges. Such edges usually recombine into longer edges when processing the polygon further. When such a recombination happens, the edges no longer correspond to original edges or diagonals. When 'split_edges' is false (the default), the resulting polygons will not contain collinear edges, but the decomposition will be constrained to fewer cut lines. + 'max_area' and 'min_b' are the corresponding parameters used for the triangulation (see \delaunay). + + This method has been introduced in version 0.30.1. + """ + ... def holes(self) -> int: r""" @brief Returns the number of holes @@ -14926,7 +14944,7 @@ class DSimplePolygon: The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t. - Picking a value of 0.0 for max area and min b will make the implementation skip the refinement step. In that case, the results are identical to the standard constrained Delaunay triangulation. + Picking a value of 0.0 for max_area and min_b will make the implementation skip the refinement step. In that case, the results are identical to the standard constrained Delaunay triangulation. This method has been introduced in version 0.30. """ @@ -15003,6 +15021,24 @@ class DSimplePolygon: This method has been introduced in version 0.25. """ ... + def hm_decomposition(self, with_segments: Optional[bool] = ..., split_edges: Optional[bool] = ..., max_area: Optional[float] = ..., min_b: Optional[float] = ...) -> List[DSimplePolygon]: + r""" + @brief Performs a Hertel-Mehlhorn convex decomposition. + + @return An array holding the polygons of the decomposition. + + The Hertel-Mehlhorn decomposition starts with a Delaunay triangulation of the polygons and recombines the triangles into convex polygons. + + The decomposition is controlled by two parameters: 'with_segments' and 'split_edges'. + + If 'with_segments' is true (the default), new segments are introduced perpendicular to the edges forming a concave corner. If false, only diagonals (edges connecting original vertexes) are used. + + If 'split_edges' is true, the algorithm is allowed to create collinear edges in the output. In this case, the resulting polygons may contain edges that are split into collinear partial edges. Such edges usually recombine into longer edges when processing the polygon further. When such a recombination happens, the edges no longer correspond to original edges or diagonals. When 'split_edges' is false (the default), the resulting polygons will not contain collinear edges, but the decomposition will be constrained to fewer cut lines. + 'max_area' and 'min_b' are the corresponding parameters used for the triangulation (see \delaunay). + + This method has been introduced in version 0.30.1. + """ + ... def inside(self, p: DPoint) -> bool: r""" @brief Gets a value indicating whether the given point is inside the polygon @@ -34920,11 +34956,11 @@ class Instance: Starting with version 0.25 the displacement is of vector type. Setter: - @brief Sets the displacement vector for the 'b' axis in micrometer units + @brief Sets the displacement vector for the 'b' axis - Like \b= with an integer displacement, this method will set the displacement vector but it accepts a vector in micrometer units that is of \DVector type. The vector will be translated to database units internally. + If the instance was not an array instance before it is made one. - This method has been introduced in version 0.25. + This method has been introduced in version 0.23. Starting with version 0.25 the displacement is of vector type. """ cell: Cell r""" @@ -34956,10 +34992,10 @@ class Instance: Getter: @brief Gets the basic \CellInstArray object associated with this instance reference. Setter: - @brief Changes the \CellInstArray object to the given one. - This method replaces the instance by the given CellInstArray object. + @brief Returns the basic cell instance array object by giving a micrometer unit object. + This method replaces the instance by the given CellInstArray object and it internally transformed into database units. - This method has been introduced in version 0.22 + This method has been introduced in version 0.25 """ cplx_trans: ICplxTrans r""" @@ -34967,10 +35003,9 @@ class Instance: @brief Gets the complex transformation of the instance or the first instance in the array This method is always valid compared to \trans, since simple transformations can be expressed as complex transformations as well. Setter: - @brief Sets the complex transformation of the instance or the first instance in the array (in micrometer units) - This method sets the transformation the same way as \cplx_trans=, but the displacement of this transformation is given in micrometer units. It is internally translated into database units. + @brief Sets the complex transformation of the instance or the first instance in the array - This method has been introduced in version 0.25. + This method has been introduced in version 0.23. """ da: DVector r""" @@ -46700,17 +46735,17 @@ class NetTerminalRef: @overload def device(self) -> Device: r""" - @brief Gets the device reference (non-const version). + @brief Gets the device reference. Gets the device object that this connection is made to. - - This constness variant has been introduced in version 0.26.8 """ ... @overload def device(self) -> Device: r""" - @brief Gets the device reference. + @brief Gets the device reference (non-const version). Gets the device object that this connection is made to. + + This constness variant has been introduced in version 0.26.8 """ ... def device_class(self) -> DeviceClass: @@ -47722,17 +47757,26 @@ class Netlist: @overload def circuit_by_name(self, name: str) -> Circuit: r""" - @brief Gets the circuit object for a given name. + @brief Gets the circuit object for a given name (const version). If the name is not a valid circuit name, nil is returned. + + This constness variant has been introduced in version 0.26.8. """ ... @overload def circuit_by_name(self, name: str) -> Circuit: r""" - @brief Gets the circuit object for a given name (const version). + @brief Gets the circuit object for a given name. If the name is not a valid circuit name, nil is returned. + """ + ... + @overload + def circuits_by_name(self, name_pattern: str) -> List[Circuit]: + r""" + @brief Gets the circuit objects for a given name filter. + The name filter is a glob pattern. This method will return all \Circuit objects matching the glob pattern. - This constness variant has been introduced in version 0.26.8. + This method has been introduced in version 0.26.4. """ ... @overload @@ -47745,15 +47789,6 @@ class Netlist: This constness variant has been introduced in version 0.26.8. """ ... - @overload - def circuits_by_name(self, name_pattern: str) -> List[Circuit]: - r""" - @brief Gets the circuit objects for a given name filter. - The name filter is a glob pattern. This method will return all \Circuit objects matching the glob pattern. - - This method has been introduced in version 0.26.4. - """ - ... def combine_devices(self) -> None: r""" @brief Combines devices where possible @@ -47996,7 +48031,7 @@ class Netlist: @overload def top_circuit(self) -> Circuit: r""" - @brief Gets the top circuit. + @brief Gets the top circuit (const version). This method will return nil, if there is no top circuit. It will raise an error, if there is more than a single top circuit. This convenience method has been added in version 0.29.5. @@ -48005,7 +48040,7 @@ class Netlist: @overload def top_circuit(self) -> Circuit: r""" - @brief Gets the top circuit (const version). + @brief Gets the top circuit. This method will return nil, if there is no top circuit. It will raise an error, if there is more than a single top circuit. This convenience method has been added in version 0.29.5. @@ -53581,11 +53616,13 @@ class Polygon: The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t. - The area value is given in terms of DBU units. Picking a value of 0.0 for area and min b will make the implementation skip the refinement step. In that case, the results are identical to the standard constrained Delaunay triangulation. + Picking a value of 0.0 for max_area and min_b will make the implementation skip the refinement step. In that case, the results are identical to the standard constrained Delaunay triangulation. + + The area value is given in terms of DBU units. The 'dbu' parameter a numerical scaling parameter. It should be choosen in a way that the polygon dimensions are "in the order of 1" (very roughly) after multiplication with the dbu parameter. A value of 0.001 is suitable for polygons with typical dimensions in the order to 1000 DBU. Usually the default value is good enough. - This method has been introduced in version 0.30. + This method has been introduced in version 0.30. Since version 0.30.1, the resulting region is in 'no merged semantics' mode, to avoid re-merging of the triangles during following operations. """ ... @overload @@ -53595,7 +53632,7 @@ class Polygon: This variant of the triangulation function accepts an array of additional vertexes for the triangulation. - This method has been introduced in version 0.30. + This method has been introduced in version 0.30. Since version 0.30.1, the resulting region is in 'no merged semantics' mode, to avoid re-merging of the triangles during following operations. """ ... def destroy(self) -> None: @@ -53682,6 +53719,28 @@ class Polygon: This method has been introduced in version 0.25. """ ... + def hm_decomposition(self, with_segments: Optional[bool] = ..., split_edges: Optional[bool] = ..., max_area: Optional[float] = ..., min_b: Optional[float] = ..., dbu: Optional[float] = ...) -> Region: + r""" + @brief Performs a Hertel-Mehlhorn convex decomposition. + + @return A \Region holding the polygons of the decomposition. + + The resulting region is in 'no merged semantics' mode, to avoid re-merging of the polygons during following operations. + + The Hertel-Mehlhorn decomposition starts with a Delaunay triangulation of the polygons and recombines the triangles into convex polygons. + + The decomposition is controlled by two parameters: 'with_segments' and 'split_edges'. + + If 'with_segments' is true (the default), new segments are introduced perpendicular to the edges forming a concave corner. If false, only diagonals (edges connecting original vertexes) are used. + + If 'split_edges' is true, the algorithm is allowed to create collinear edges in the output. In this case, the resulting polygons may contain edges that are split into collinear partial edges. Such edges usually recombine into longer edges when processing the polygon further. When such a recombination happens, the edges no longer correspond to original edges or diagonals. When 'split_edges' is false (the default), the resulting polygons will not contain collinear edges, but the decomposition will be constrained to fewer cut lines. + 'max_area' and 'min_b' are the corresponding parameters used for the triangulation (see \delaunay). + + The 'dbu' parameter a numerical scaling parameter. It should be choosen in a way that the polygon dimensions are "in the order of 1" (very roughly) after multiplication with the dbu parameter. A value of 0.001 is suitable for polygons with typical dimensions in the order to 1000 DBU. Usually the default value is good enough. + + This method has been introduced in version 0.30.1. + """ + ... def holes(self) -> int: r""" @brief Returns the number of holes @@ -62995,11 +63054,12 @@ class Shape: This method has been introduced in version 0.23. Setter: - @brief Sets the lower left point of the box + @brief Sets the lower left corner of the box with the point being given in micrometer units Applies to boxes only. Changes the lower left point of the box and throws an exception if the shape is not a box. + Translation from micrometer units to database units is done internally. - This method has been introduced in version 0.23. + This method has been introduced in version 0.25. """ box_p2: Point r""" @@ -63377,10 +63437,11 @@ class Shape: Starting with version 0.23, this method returns nil, if the shape does not represent a geometrical primitive that can be converted to a simple polygon. Setter: - @brief Replaces the shape by the given simple polygon (in micrometer units) - This method replaces the shape by the given text, like \simple_polygon= with a \SimplePolygon argument does. This version translates the polygon from micrometer units to database units internally. + @brief Replaces the shape by the given simple polygon object + This method replaces the shape by the given simple polygon object. This method can only be called for editable layouts. It does not change the user properties of the shape. + Calling this method will invalidate any iterators. It should not be called inside a loop iterating over shapes. - This method has been introduced in version 0.25. + This method has been introduced in version 0.22. """ text: Any r""" @@ -66718,11 +66779,13 @@ class SimplePolygon: The minimum angle of the resulting triangles relates to the 'b' parameter as: @t min_angle = arcsin(B/2) @/t. - The area value is given in terms of DBU units. Picking a value of 0.0 for area and min b will make the implementation skip the refinement step. In that case, the results are identical to the standard constrained Delaunay triangulation. + Picking a value of 0.0 for max_area and min_b will make the implementation skip the refinement step. In that case, the results are identical to the standard constrained Delaunay triangulation. + + The area value is given in terms of DBU units. The 'dbu' parameter a numerical scaling parameter. It should be choosen in a way that the polygon dimensions are "in the order of 1" (very roughly) after multiplication with the dbu parameter. A value of 0.001 is suitable for polygons with typical dimensions in the order to 1000 DBU. Usually the default value is good enough. - This method has been introduced in version 0.30. + This method has been introduced in version 0.30. Since version 0.30.1, the resulting region is in 'no merged semantics' mode, to avoid re-merging of the triangles during following operations. """ ... @overload @@ -66732,7 +66795,7 @@ class SimplePolygon: This variant of the triangulation function accepts an array of additional vertexes for the triangulation. - This method has been introduced in version 0.30. + This method has been introduced in version 0.30. Since version 0.30.1, the resulting region is in 'no merged semantics' mode, to avoid re-merging of the triangles during following operations. """ ... def destroy(self) -> None: @@ -66797,6 +66860,27 @@ class SimplePolygon: This method has been introduced in version 0.25. """ ... + def hm_decomposition(self, with_segments: Optional[bool] = ..., split_edges: Optional[bool] = ..., max_area: Optional[float] = ..., min_b: Optional[float] = ..., dbu: Optional[float] = ...) -> Region: + r""" + @brief Performs a Hertel-Mehlhorn convex decomposition. + + @return A \Region holding the polygons of the decomposition. + The resulting region is in 'no merged semantics' mode, to avoid re-merging of the polygons during following operations. + + The Hertel-Mehlhorn decomposition starts with a Delaunay triangulation of the polygons and recombines the triangles into convex polygons. + + The decomposition is controlled by two parameters: 'with_segments' and 'split_edges'. + + If 'with_segments' is true (the default), new segments are introduced perpendicular to the edges forming a concave corner. If false, only diagonals (edges connecting original vertexes) are used. + + If 'split_edges' is true, the algorithm is allowed to create collinear edges in the output. In this case, the resulting polygons may contain edges that are split into collinear partial edges. Such edges usually recombine into longer edges when processing the polygon further. When such a recombination happens, the edges no longer correspond to original edges or diagonals. When 'split_edges' is false (the default), the resulting polygons will not contain collinear edges, but the decomposition will be constrained to fewer cut lines. + 'max_area' and 'min_b' are the corresponding parameters used for the triangulation (see \delaunay). + + The 'dbu' parameter a numerical scaling parameter. It should be choosen in a way that the polygon dimensions are "in the order of 1" (very roughly) after multiplication with the dbu parameter. A value of 0.001 is suitable for polygons with typical dimensions in the order to 1000 DBU. Usually the default value is good enough. + + This method has been introduced in version 0.30.1. + """ + ... def inside(self, p: Point) -> bool: r""" @brief Gets a value indicating whether the given point is inside the polygon @@ -67552,17 +67636,23 @@ class SubCircuit(NetlistObject): @overload def circuit(self) -> Circuit: r""" - @brief Gets the circuit the subcircuit lives in. + @brief Gets the circuit the subcircuit lives in (non-const version). This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \circuit_ref. + + This constness variant has been introduced in version 0.26.8 """ ... @overload def circuit(self) -> Circuit: r""" - @brief Gets the circuit the subcircuit lives in (non-const version). + @brief Gets the circuit the subcircuit lives in. This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \circuit_ref. - - This constness variant has been introduced in version 0.26.8 + """ + ... + @overload + def circuit_ref(self) -> Circuit: + r""" + @brief Gets the circuit referenced by the subcircuit. """ ... @overload @@ -67575,12 +67665,6 @@ class SubCircuit(NetlistObject): """ ... @overload - def circuit_ref(self) -> Circuit: - r""" - @brief Gets the circuit referenced by the subcircuit. - """ - ... - @overload def connect_pin(self, pin: Pin, net: Net) -> None: r""" @brief Connects the given pin to the specified net. @@ -68247,8 +68331,7 @@ class Text: Setter: @brief Sets the horizontal alignment - This property specifies how the text is aligned relative to the anchor point. - This property has been introduced in version 0.22 and extended to enums in 0.28. + This is the version accepting integer values. It's provided for backward compatibility. """ size: int r""" @@ -68284,7 +68367,8 @@ class Text: Setter: @brief Sets the vertical alignment - This is the version accepting integer values. It's provided for backward compatibility. + This property specifies how the text is aligned relative to the anchor point. + This property has been introduced in version 0.22 and extended to enums in 0.28. """ x: int r""" diff --git a/src/pymod/distutils_src/klayout/pexcore.pyi b/src/pymod/distutils_src/klayout/pexcore.pyi index e69de29bb..a91d281cb 100644 --- a/src/pymod/distutils_src/klayout/pexcore.pyi +++ b/src/pymod/distutils_src/klayout/pexcore.pyi @@ -0,0 +1,5 @@ +from typing import Any, ClassVar, Dict, Sequence, List, Iterator, Optional +from typing import overload +from __future__ import annotations +import klayout.tl as tl +import klayout.db as db diff --git a/src/pymod/distutils_src/klayout/tlcore.pyi b/src/pymod/distutils_src/klayout/tlcore.pyi index 09483e06c..fd53c5008 100644 --- a/src/pymod/distutils_src/klayout/tlcore.pyi +++ b/src/pymod/distutils_src/klayout/tlcore.pyi @@ -2908,7 +2908,9 @@ class Timer: r""" @brief Gets the current memory usage of the process in Bytes - This method has been introduced in version 0.27. + The returned value is the resident memory size on Linux and MacOS and the working set size on Windows. + + This method has been introduced in version 0.27. The value has been changed to be resident size (instead of virtual size) on Linux in version 0.30. """ ... @classmethod diff --git a/testdata/algo/hm_decomposition_au5.gds b/testdata/algo/hm_decomposition_au5.gds new file mode 100644 index 0000000000000000000000000000000000000000..875e1be4911c7a43a7c1b0593150dda38aed88eb GIT binary patch literal 1314 zcmbW1zb^zq6vsceyEnJQuak>K&IwUmAt4dmB_eUTMvy2-M5$8xH?G%dX!sYDDiTqu z)pRPAh?(7aXT@@}wt2g=@AKZ9@4PoCJnw*N!7IHGFiONY1L=1{Pi=3upyC(TH#)0Z zw^y&t_VvQ~+2J15;vbw+?fsoyf)Lc8M*%hD0c#^ZT&#e94~-9PpP*ic=sEeWy+c#` z)bzjOOIQlJ{y~`^@#amu=rM^?y@B%4_jo40{r@W;>fDAi*3@C*h?RA1v-o&Fl2^Ft z6PWD4Yy+mWc=bD&c>Ai$`e((PGy4&L;g`pt_0V09xxPf#eygjRG3tiHn$}MxLuE(m zf4M|~w9>sR6y4K$xKZYGES+;-R#QhxPSkdWBPHi%`@oTsX;?gsyV;S-nvvcb%EaW$ lC39-~GiQZ%svRj&KA(nCq0v&`B#ybPx7<51Rnm7&z$cXNd-DJQ literal 0 HcmV?d00001 From dfe67178dc1c87327e88bff56a0a23dd630841c3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 19 Apr 2025 16:14:15 +0200 Subject: [PATCH 149/392] WIP --- src/pex/pex/pexRExtractor.cc | 94 +++++++++++++++++++----- src/pex/pex/pexRExtractor.h | 48 ++++++++---- src/pex/unit_tests/pexRExtractorTests.cc | 49 +++++++++++- 3 files changed, 158 insertions(+), 33 deletions(-) diff --git a/src/pex/pex/pexRExtractor.cc b/src/pex/pex/pexRExtractor.cc index 5d7a30eab..614e6f009 100644 --- a/src/pex/pex/pexRExtractor.cc +++ b/src/pex/pex/pexRExtractor.cc @@ -26,6 +26,50 @@ namespace pex { +// ----------------------------------------------------------------------------- + +std::string +RNode::to_string () const +{ + std::string res; + switch (type) { + default: + res += "$" + tl::to_string (port_index); + break; + case VertexPort: + res += "V" + tl::to_string (port_index); + break; + case PolygonPort: + res += "P" + tl::to_string (port_index); + break; + } + return res; +} + +// ----------------------------------------------------------------------------- + +std::string +RElement::to_string () const +{ + std::string res = "R "; + if (a ()) { + res += a ()->to_string (); + } else { + res += "(nil)"; + } + res += " "; + if (b ()) { + res += b ()->to_string (); + } else { + res += "(nil)"; + } + res += " "; + res += tl::sprintf ("%.6g", resistance ()); + return res; +} + +// ----------------------------------------------------------------------------- + RNetwork::RNetwork () { // .. nothing yet .. @@ -36,10 +80,23 @@ RNetwork::~RNetwork () clear (); } +std::string +RNetwork::to_string () const +{ + std::string res; + for (auto e = m_elements.begin (); e != m_elements.end (); ++e) { + if (! res.empty ()) { + res += "\n"; + } + res += e->to_string (); + } + return res; +} + void RNetwork::clear () { - m_elements.clear (); // must come before m_nodes + m_elements.clear (); // must happen before m_nodes m_nodes.clear (); m_elements_by_nodes.clear (); m_nodes_by_type.clear (); @@ -51,7 +108,7 @@ std::map, RNode *> m_nodes; RNode * RNetwork::create_node (RNode::node_type type, unsigned int port_index) { - if (type != RNode::INTERNAL) { + if (type != RNode::Internal) { auto i = m_nodes_by_type.find (std::make_pair (type, port_index)); if (i != m_nodes_by_type.end ()) { @@ -60,7 +117,7 @@ RNetwork::create_node (RNode::node_type type, unsigned int port_index) } else { - RNode *new_node = new RNode (type, db::DBox (), port_index); + RNode *new_node = new RNode (this, type, db::DBox (), port_index); m_nodes.push_back (new_node); m_nodes_by_type.insert (std::make_pair (std::make_pair (type, port_index), new_node)); @@ -69,7 +126,7 @@ RNetwork::create_node (RNode::node_type type, unsigned int port_index) } else { - RNode *new_node = new RNode (type, db::DBox (), port_index); + RNode *new_node = new RNode (this, type, db::DBox (), port_index); m_nodes.push_back (new_node); return new_node; @@ -87,13 +144,15 @@ RNetwork::create_element (double conductivity, RNode *a, RNode *b) } else { - RElement *element = new RElement (conductivity, a, b); - a->elements.push_back (element); - element->m_ia = --a->elements.end (); - b->elements.push_back (element); - element->m_ia = --b->elements.end (); - + RElement *element = new RElement (this, conductivity, a, b); + m_elements.push_back (element); m_elements_by_nodes.insert (std::make_pair (std::make_pair (a, b), element)); + + a->m_elements.push_back (element); + element->m_ia = --a->m_elements.end (); + b->m_elements.push_back (element); + element->m_ib = --b->m_elements.end (); + return element; } @@ -102,9 +161,9 @@ RNetwork::create_element (double conductivity, RNode *a, RNode *b) void RNetwork::remove_node (RNode *node) { - tl_assert (node->type == RNode::INTERNAL); - while (! node->elements.empty ()) { - remove_element (const_cast (node->elements.front ())); + tl_assert (node->type == RNode::Internal); + while (! node->m_elements.empty ()) { + delete const_cast (node->m_elements.front ()); } delete node; } @@ -112,19 +171,20 @@ RNetwork::remove_node (RNode *node) void RNetwork::remove_element (RElement *element) { - RNode *a = const_cast (element->a); - RNode *b = const_cast (element->b); + RNode *a = const_cast (element->a ()); + RNode *b = const_cast (element->b ()); delete element; - if (a && a->type == RNode::INTERNAL && a->elements.empty ()) { + if (a && a->type == RNode::Internal && a->m_elements.empty ()) { delete a; } - if (b && b->type == RNode::INTERNAL && b->elements.empty ()) { + if (b && b->type == RNode::Internal && b->m_elements.empty ()) { delete b; } } +// ----------------------------------------------------------------------------- RExtractor::RExtractor () { diff --git a/src/pex/pex/pexRExtractor.h b/src/pex/pex/pexRExtractor.h index dccc95616..87a2f2565 100644 --- a/src/pex/pex/pexRExtractor.h +++ b/src/pex/pex/pexRExtractor.h @@ -39,28 +39,36 @@ namespace pex class RElement; class RNode; +class RNetwork; struct PEX_PUBLIC RNode : public tl::list_node { public: enum node_type { - INTERNAL, - VERTEX_PORT, - POLYGON_PORT + Internal, + VertexPort, + PolygonPort }; node_type type; db::DBox location; unsigned int port_index; - mutable std::list elements; + + const std::list &elements () const + { + return m_elements; + } + + std::string to_string () const; protected: friend class RNetwork; + friend class RElement; friend class tl::list_impl; - RNode (node_type _type, const db::DBox &_location, unsigned int _port_index) - : type (_type), location (_location), port_index (_port_index) + RNode (RNetwork *network, node_type _type, const db::DBox &_location, unsigned int _port_index) + : type (_type), location (_location), port_index (_port_index), mp_network (network) { } ~RNode () { } @@ -68,39 +76,48 @@ protected: private: RNode (const RNode &other); RNode &operator= (const RNode &other); + + RNetwork *mp_network; + mutable std::list m_elements; }; struct PEX_PUBLIC RElement : public tl::list_node { double conductivity; - const RNode *a, *b; + + const RNode *a () const { return mp_a; } + const RNode *b () const { return mp_b; } double resistance () const { return 1.0 / conductivity; } + std::string to_string () const; + protected: friend class RNetwork; friend class tl::list_impl; - RElement (double _conductivity, const RNode *_a, const RNode *_b) - : conductivity (_conductivity), a (_a), b (_b) + RElement (RNetwork *network, double _conductivity, const RNode *a, const RNode *b) + : conductivity (_conductivity), mp_network (network), mp_a (a), mp_b (b) { } ~RElement () { - if (a) { - a->elements.erase (m_ia); + if (mp_a) { + mp_a->m_elements.erase (m_ia); } - if (b) { - b->elements.erase (m_ib); + if (mp_b) { + mp_b->m_elements.erase (m_ib); } - a = b = 0; + mp_a = mp_b = 0; } std::list::iterator m_ia, m_ib; + RNetwork *mp_network; + const RNode *mp_a, *mp_b; private: RElement (const RElement &other); @@ -108,6 +125,7 @@ private: }; class PEX_PUBLIC RNetwork + : public tl::Object { public: RNetwork (); @@ -119,6 +137,8 @@ public: void remove_node (RNode *node); void clear (); + std::string to_string () const; + private: tl::list m_nodes; tl::list m_elements; diff --git a/src/pex/unit_tests/pexRExtractorTests.cc b/src/pex/unit_tests/pexRExtractorTests.cc index c3ad86a08..3915e8337 100644 --- a/src/pex/unit_tests/pexRExtractorTests.cc +++ b/src/pex/unit_tests/pexRExtractorTests.cc @@ -24,8 +24,53 @@ #include "pexRExtractor.h" #include "tlUnitTest.h" -TEST(1) +TEST(network_basic) { - // @@@ + pex::RNetwork rn; + EXPECT_EQ (rn.to_string (), ""); + + pex::RNode *n1 = rn.create_node (pex::RNode::Internal, 1); + pex::RNode *n2 = rn.create_node (pex::RNode::Internal, 2); + /* pex::RElement *e12 = */ rn.create_element (0.5, n1, n2); + + EXPECT_EQ (rn.to_string (), + "R $1 $2 2" + ); + + pex::RNode *n3 = rn.create_node (pex::RNode::Internal, 3); + /* pex::RElement *e13 = */ rn.create_element (0.25, n1, n3); + pex::RElement *e23 = rn.create_element (1.0, n2, n3); + + EXPECT_EQ (rn.to_string (), + "R $1 $2 2\n" + "R $1 $3 4\n" + "R $2 $3 1" + ); + + pex::RElement *e23b = rn.create_element (4.0, n2, n3); + EXPECT_EQ (e23 == e23b, true); + + EXPECT_EQ (rn.to_string (), + "R $1 $2 2\n" + "R $1 $3 4\n" + "R $2 $3 0.2" + ); + + rn.remove_element (e23); + + EXPECT_EQ (rn.to_string (), + "R $1 $2 2\n" + "R $1 $3 4" + ); + + rn.remove_node (n3); + + EXPECT_EQ (rn.to_string (), + "R $1 $2 2" + ); + + rn.clear (); + + EXPECT_EQ (rn.to_string (), ""); } From 6c90da80072bc835f649b142218cc903ee28f940 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 19 Apr 2025 16:30:58 +0200 Subject: [PATCH 150/392] WIP --- src/pex/pex/gsiDeclRExtractor.cc | 2 +- src/pex/pex/pex.pro | 2 + src/pex/pex/pexRExtractor.cc | 32 --------- src/pex/pex/pexRExtractor.h | 31 --------- src/pex/pex/pexSquareCountingRExtractor.cc | 60 +++++++++++++++++ src/pex/pex/pexSquareCountingRExtractor.h | 66 +++++++++++++++++++ .../pexSquareCountingRExtractorTests.cc | 60 +++++++++++++++++ src/pex/unit_tests/unit_tests.pro | 1 + 8 files changed, 190 insertions(+), 64 deletions(-) create mode 100644 src/pex/pex/pexSquareCountingRExtractor.cc create mode 100644 src/pex/pex/pexSquareCountingRExtractor.h create mode 100644 src/pex/unit_tests/pexSquareCountingRExtractorTests.cc diff --git a/src/pex/pex/gsiDeclRExtractor.cc b/src/pex/pex/gsiDeclRExtractor.cc index a0bc54078..f74e8e657 100644 --- a/src/pex/pex/gsiDeclRExtractor.cc +++ b/src/pex/pex/gsiDeclRExtractor.cc @@ -22,7 +22,7 @@ #include "gsiDecl.h" -#include "pexRExtractor.h" +#include "pexSquareCountingRExtractor.h" namespace gsi { diff --git a/src/pex/pex/pex.pro b/src/pex/pex/pex.pro index 80b1b5a8b..251fb9935 100644 --- a/src/pex/pex/pex.pro +++ b/src/pex/pex/pex.pro @@ -10,10 +10,12 @@ SOURCES = \ pexForceLink.cc \ pexRExtractor.cc \ gsiDeclRExtractor.cc \ + pexSquareCountingRExtractor.cc HEADERS = \ pexForceLink.h \ pexRExtractor.h \ + pexSquareCountingRExtractor.h RESOURCES = \ diff --git a/src/pex/pex/pexRExtractor.cc b/src/pex/pex/pexRExtractor.cc index 614e6f009..de4a3585b 100644 --- a/src/pex/pex/pexRExtractor.cc +++ b/src/pex/pex/pexRExtractor.cc @@ -196,36 +196,4 @@ RExtractor::~RExtractor () // .. nothing yet .. } - -SquareCountingRExtractor::SquareCountingRExtractor (double dbu) -{ - m_dbu = dbu; - - m_decomp_param.split_edges = true; - m_decomp_param.with_segments = false; } - -void -SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector &vertex_ports, const std::vector &polygon_ports, RNetwork &rnetwork) -{ - db::CplxTrans trans = db::CplxTrans (m_dbu) * db::ICplxTrans (db::Trans (db::Point () - polygon.box ().center ())); - auto inv_trans = trans.inverted (); - - db::plc::Graph plc; - - db::plc::ConvexDecomposition decomp (&plc); - decomp.decompose (polygon, vertex_ports, m_decomp_param, trans); - - std::vector > decomp_polygons; - for (auto p = plc.begin (); p != plc.end (); ++p) { - // @@@decomp_polygons.push_back (db::Polygon ()); - // @@@decomp_polygons.back ().first = inv_trans * p->polygon (); - } - - // @@@ use box_scanner to find interactions between polygon_ports and decomp_polygons - -} - -} - - diff --git a/src/pex/pex/pexRExtractor.h b/src/pex/pex/pexRExtractor.h index 87a2f2565..96c704cbe 100644 --- a/src/pex/pex/pexRExtractor.h +++ b/src/pex/pex/pexRExtractor.h @@ -27,8 +27,6 @@ #include "dbPolygon.h" #include "dbPLC.h" -#include "dbPLCConvexDecomposition.h" // @@@ -#include "dbPLCTriangulation.h" // @@@ #include "tlList.h" #include @@ -168,35 +166,6 @@ public: virtual void extract (const db::Polygon &polygon, const std::vector &vertex_ports, const std::vector &polygon_ports, RNetwork &rnetwork) = 0; }; -// @@@ -class PEX_PUBLIC SquareCountingRExtractor - : public RExtractor -{ -public: - SquareCountingRExtractor (double dbu); - - db::plc::ConvexDecompositionParameters &decomposition_parameters () - { - return m_decomp_param; - } - - void set_dbu (double dbu) - { - m_dbu = dbu; - } - - double dbu () const - { - return m_dbu; - } - - virtual void extract (const db::Polygon &polygon, const std::vector &vertex_ports, const std::vector &polygon_ports, RNetwork &rnetwork); - -private: - db::plc::ConvexDecompositionParameters m_decomp_param; - double m_dbu; -}; - } #endif diff --git a/src/pex/pex/pexSquareCountingRExtractor.cc b/src/pex/pex/pexSquareCountingRExtractor.cc new file mode 100644 index 000000000..76f00b80a --- /dev/null +++ b/src/pex/pex/pexSquareCountingRExtractor.cc @@ -0,0 +1,60 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "pexSquareCountingRExtractor.h" + +namespace pex +{ + +SquareCountingRExtractor::SquareCountingRExtractor (double dbu) +{ + m_dbu = dbu; + + m_decomp_param.split_edges = true; + m_decomp_param.with_segments = false; +} + +void +SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector &vertex_ports, const std::vector &polygon_ports, RNetwork &rnetwork) +{ + db::CplxTrans trans = db::CplxTrans (m_dbu) * db::ICplxTrans (db::Trans (db::Point () - polygon.box ().center ())); + auto inv_trans = trans.inverted (); + + db::plc::Graph plc; + + db::plc::ConvexDecomposition decomp (&plc); + decomp.decompose (polygon, vertex_ports, m_decomp_param, trans); + + std::vector > decomp_polygons; + for (auto p = plc.begin (); p != plc.end (); ++p) { + // @@@decomp_polygons.push_back (db::Polygon ()); + // @@@decomp_polygons.back ().first = inv_trans * p->polygon (); + } + + // @@@ use box_scanner to find interactions between polygon_ports and decomp_polygons + +} + +} + + diff --git a/src/pex/pex/pexSquareCountingRExtractor.h b/src/pex/pex/pexSquareCountingRExtractor.h new file mode 100644 index 000000000..99881099b --- /dev/null +++ b/src/pex/pex/pexSquareCountingRExtractor.h @@ -0,0 +1,66 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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_pexSquareCountingRExtractor +#define HDR_pexSquareCountingRExtractor + +#include "pexCommon.h" +#include "pexRExtractor.h" + +#include "dbPLCConvexDecomposition.h" + +namespace pex +{ + +// @@@ doc +class PEX_PUBLIC SquareCountingRExtractor + : public RExtractor +{ +public: + SquareCountingRExtractor (double dbu); + + db::plc::ConvexDecompositionParameters &decomposition_parameters () + { + return m_decomp_param; + } + + void set_dbu (double dbu) + { + m_dbu = dbu; + } + + double dbu () const + { + return m_dbu; + } + + virtual void extract (const db::Polygon &polygon, const std::vector &vertex_ports, const std::vector &polygon_ports, RNetwork &rnetwork); + +private: + db::plc::ConvexDecompositionParameters m_decomp_param; + double m_dbu; +}; + +} + +#endif + diff --git a/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc b/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc new file mode 100644 index 000000000..613d630b9 --- /dev/null +++ b/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc @@ -0,0 +1,60 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "pexSquareCountingRExtractor.h" +#include "tlUnitTest.h" + +TEST(basic) +{ + db::Point contour[] = { + db::Point (0, 0), + db::Point (0, 100), + db::Point (1000, 100), + db::Point (1000, 1000), + db::Point (1100, 1000), + db::Point (1100, 100), + db::Point (1700, 100), + db::Point (1700, 0) + }; + + db::Polygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + + double dbu = 0.001; + + pex::RNetwork rn; + pex::SquareCountingRExtractor rex (dbu); + + std::vector vertex_ports; + vertex_ports.push_back (db::Point (0, 50)); + vertex_ports.push_back (db::Point (1650, 50)); + + std::vector polygon_ports; + polygon_ports.push_back (db::Polygon (db::Box (1000, 900, 1100, 1000))); + + rex.extract (poly, vertex_ports, polygon_ports, rn); + + EXPECT_EQ (rn.to_string (), + "" + ) +} diff --git a/src/pex/unit_tests/unit_tests.pro b/src/pex/unit_tests/unit_tests.pro index c6ccc1b74..6c380c340 100644 --- a/src/pex/unit_tests/unit_tests.pro +++ b/src/pex/unit_tests/unit_tests.pro @@ -8,6 +8,7 @@ include($$PWD/../../lib_ut.pri) SOURCES = \ pexRExtractorTests.cc \ + pexSquareCountingRExtractorTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC $$PEX_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC $$PEX_INC From 9cd29c9de70c316ca5fd8ecf1fc9bcbeff82a920 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 19 Apr 2025 18:45:31 +0200 Subject: [PATCH 151/392] WIP --- src/pex/pex/pexRExtractor.cc | 7 +- src/pex/pex/pexRExtractor.h | 8 +- src/pex/pex/pexSquareCountingRExtractor.cc | 283 ++++++++++++++++++++- 3 files changed, 290 insertions(+), 8 deletions(-) diff --git a/src/pex/pex/pexRExtractor.cc b/src/pex/pex/pexRExtractor.cc index de4a3585b..4913c90ab 100644 --- a/src/pex/pex/pexRExtractor.cc +++ b/src/pex/pex/pexRExtractor.cc @@ -139,7 +139,12 @@ RNetwork::create_element (double conductivity, RNode *a, RNode *b) auto i = m_elements_by_nodes.find (std::make_pair (a, b)); if (i != m_elements_by_nodes.end ()) { - i->second->conductivity += conductivity; + if (conductivity == pex::RElement::short_value () || i->second->conductivity == pex::RElement::short_value ()) { + i->second->conductivity = pex::RElement::short_value (); + } else { + i->second->conductivity += conductivity; + } + return i->second; } else { diff --git a/src/pex/pex/pexRExtractor.h b/src/pex/pex/pexRExtractor.h index 96c704cbe..890b6ad67 100644 --- a/src/pex/pex/pexRExtractor.h +++ b/src/pex/pex/pexRExtractor.h @@ -31,6 +31,7 @@ #include #include +#include namespace pex { @@ -87,9 +88,14 @@ struct PEX_PUBLIC RElement const RNode *a () const { return mp_a; } const RNode *b () const { return mp_b; } + static double short_value () + { + return std::numeric_limits::infinity (); + } + double resistance () const { - return 1.0 / conductivity; + return conductivity == short_value () ? 0.0 : 1.0 / conductivity; } std::string to_string () const; diff --git a/src/pex/pex/pexSquareCountingRExtractor.cc b/src/pex/pex/pexSquareCountingRExtractor.cc index 76f00b80a..cdfc0d183 100644 --- a/src/pex/pex/pexSquareCountingRExtractor.cc +++ b/src/pex/pex/pexSquareCountingRExtractor.cc @@ -22,6 +22,9 @@ #include "pexSquareCountingRExtractor.h" +#include "dbBoxScanner.h" +#include "dbPolygonTools.h" +#include "tlIntervalMap.h" namespace pex { @@ -34,9 +37,179 @@ SquareCountingRExtractor::SquareCountingRExtractor (double dbu) m_decomp_param.with_segments = false; } -void -SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector &vertex_ports, const std::vector &polygon_ports, RNetwork &rnetwork) +namespace { + +class PolygonPortInteractionReceiver + : public db::box_scanner_receiver2 +{ +public: + void add (const db::Polygon *obj1, const size_t &index1, const db::Polygon *obj2, const size_t &index2) + { + if (db::interact_pp (*obj1, *obj2)) { + m_interactions[index1].insert (index2); + } + } + + const std::set &interactions (size_t index) const + { + static std::set empty; + auto i = m_interactions.find (index); + if (i == m_interactions.end ()) { + return empty; + } else { + return i->second; + } + } + +private: + std::map > m_interactions; +}; + +struct PortDefinition +{ + PortDefinition () + : type (pex::RNode::Internal), port_index (0) + { } + + PortDefinition (pex::RNode::node_type _type, const db::Point &_location, unsigned int _port_index) + : type (_type), location (_location), port_index (_port_index) + { } + + bool operator< (const PortDefinition &other) const + { + if (type != other.type) { + return type < other.type; + } + if (port_index != other.port_index) { + return port_index < other.port_index; + } + return false; + } + + bool operator== (const PortDefinition &other) const + { + return type == other.type && port_index == other.port_index; + } + + pex::RNode::node_type type; + db::Point location; + unsigned int port_index; +}; + +struct JoinEdgeSets +{ + void operator() (std::set &a, const std::set &b) const + { + a.insert (b.begin (), b.end ()); + } +}; + +} + +static +double yatx (const db::Edge &e, int x) +{ + db::Point p1 = e.p1 (), p2 = e.p2 (); + if (p1.x () > p2.x ()) { + std::swap (p1, p2); + } + + return p1.y () + double (p2.y () - p1.y ()) * double (x - p1.x ()) / double (p2.x () - p1.x ()); +} + +static +double calculate_squares (db::Coord x1, db::Coord x2, const std::set &edges) +{ + tl_assert (edges.size () == 2); + + auto i = edges.begin (); + db::Edge e1 = *i++; + db::Edge e2 = *i; + + double w1 = fabs (yatx (e1, x1) - yatx (e2, x1)); + double w2 = fabs (yatx (e1, x2) - yatx (e2, x2)); + + // integrate the resistance along the axis x1->x2 with w=w1->w2 + + if (w1 < db::epsilon) { + return 1e9; // @@@ + } else if (fabs (w1 - w2) < db::epsilon) { + return (x2 - x1) / w1; + } else { + return (x2 - x1) / (w2 - w1) * log (w2 / w1); + } +} + +static +void rextract_square_counting (const db::Polygon &db_poly, const std::vector > &ports, pex::RNetwork &rnetwork, double /*dbu*/) +{ + // "trans" will orient the polygon to be flat rather than tall + db::Trans trans; + if (db_poly.box ().width () < db_poly.box ().height ()) { + trans = db::Trans (db::Trans::r90); + } + + // sort the edges into an interval map - as the polygons are convex, there + // can only be two edges in each interval. + + tl::interval_map > edges; + for (auto e = db_poly.begin_edge (); ! e.at_end (); ++e) { + db::Edge et = trans * *e; + if (et.x1 () != et.x2 ()) { + std::set es; + es.insert (et); + JoinEdgeSets jes; + edges.add (std::min (et.p1 ().x (), et.p2 ().x ()), std::max (et.p1 ().x (), et.p2 ().x ()), es, jes); + } + } + + // sort the port locations + + std::multimap port_locations; + for (auto p = ports.begin (); p != ports.end (); ++p) { + db::Coord c = (trans * p->first.location).x (); + port_locations.insert (std::make_pair (c, p->second)); + } + + // walk along the long axis of the polygon and compute the square count between the port locations + + for (auto pl = port_locations.begin (); pl != port_locations.end (); ++pl) { + + auto pl_next = pl; + ++pl_next; + if (pl_next == port_locations.end ()) { + break; + } + + db::Coord c = pl->first; + db::Coord cc = pl_next->first; + + double r = 0.0; + + auto em = edges.find (c); + while (em != edges.end () && em->first.first < cc) { + r += calculate_squares (em->first.first, std::min (cc, em->first.second), em->second); + ++em; + } + + // @@@ TODO: multiply with sheet rho! + // @@@ TODO: width dependency + if (r == 0) { + // @@@ TODO: join nodes later! + rnetwork.create_element (pex::RElement::short_value (), pl->second, pl_next->second); + } else { + rnetwork.create_element (r, pl->second, pl_next->second); + } + + } +} + +void +SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector &vertex_ports, const std::vector &polygon_ports, pex::RNetwork &rnetwork) +{ + rnetwork.clear (); + db::CplxTrans trans = db::CplxTrans (m_dbu) * db::ICplxTrans (db::Trans (db::Point () - polygon.box ().center ())); auto inv_trans = trans.inverted (); @@ -45,13 +218,111 @@ SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector db::plc::ConvexDecomposition decomp (&plc); decomp.decompose (polygon, vertex_ports, m_decomp_param, trans); - std::vector > decomp_polygons; + // Set up a scanner to detect interactions between polygon ports + // and decomposed polygons + + db::box_scanner2 scanner; + + std::vector > decomp_polygons; for (auto p = plc.begin (); p != plc.end (); ++p) { - // @@@decomp_polygons.push_back (db::Polygon ()); - // @@@decomp_polygons.back ().first = inv_trans * p->polygon (); + decomp_polygons.push_back (std::make_pair (db::Polygon (), p.operator-> ())); + decomp_polygons.back ().first = inv_trans * p->polygon (); } - // @@@ use box_scanner to find interactions between polygon_ports and decomp_polygons + for (auto i = decomp_polygons.begin (); i != decomp_polygons.end (); ++i) { + scanner.insert1 (&i->first, i - decomp_polygons.begin ()); + } + + for (auto i = polygon_ports.begin (); i != polygon_ports.end (); ++i) { + scanner.insert2 (i.operator-> (), i - polygon_ports.begin ()); + } + + PolygonPortInteractionReceiver interactions; + db::box_convert bc; + scanner.process (interactions, 1, bc, bc); + + // Generate the internal ports: those are defined by edges connecting two polygons + + std::vector internal_port_edges; + std::map internal_ports; + std::vector > internal_port_indexes; + + for (auto i = decomp_polygons.begin (); i != decomp_polygons.end (); ++i) { + + internal_port_indexes.push_back (std::vector ()); + auto p = i->second; + + for (size_t j = 0; j < p->size (); ++j) { + + const db::plc::Edge *e = p->edge (j); + if (e->left () && e->right ()) { + + auto ip = internal_ports.find (e); + if (ip == internal_ports.end ()) { + size_t n = internal_port_edges.size (); + internal_port_edges.push_back (e); + ip = internal_ports.insert (std::make_pair (e, n)).first; + } + internal_port_indexes.back ().push_back (ip->second); + + } + + } + + } + + // Now we can extract the resistors + + std::vector > ports; + std::map nodes_for_ports; + + for (auto p = decomp_polygons.begin (); p != decomp_polygons.end (); ++p) { + + ports.clear (); + + const db::Polygon &db_poly = p->first; + const db::plc::Polygon *plc_poly = p->second; + const std::set &pp_indexes = interactions.interactions (p - decomp_polygons.begin ()); + const std::vector &ip_indexes = internal_port_indexes [p - decomp_polygons.begin ()]; + + // set up the ports: + + // 1. internal ports + for (auto i = ip_indexes.begin (); i != ip_indexes.end (); ++i) { + db::Point loc = (inv_trans * internal_port_edges [*i]->edge ()).bbox ().center (); + ports.push_back (std::make_pair (PortDefinition (pex::RNode::Internal, loc, *i), (pex::RNode *) 0)); + } + + // 2. vertex ports + for (size_t i = 0; i < plc_poly->internal_vertexes (); ++i) { + db::Point loc = inv_trans * *plc_poly->internal_vertex (i); + ports.push_back (std::make_pair (PortDefinition (pex::RNode::VertexPort, loc, i), (pex::RNode *) 0)); + } + + // 3. polygon ports + // (NOTE: here we only take the center of the bounding box) + for (auto i = pp_indexes.begin (); i != pp_indexes.end (); ++i) { + db::Point loc = polygon_ports [*i].box ().center (); + ports.push_back (std::make_pair (PortDefinition (pex::RNode::PolygonPort, loc, *i), (pex::RNode *) 0)); + } + + // create nodes for the ports + // (we reuse nodes for existing ports in "nodes_for_ports", hence to establish the connection) + + for (auto p = ports.begin (); p != ports.end (); ++p) { + auto n4p = nodes_for_ports.find (p->first); + if (n4p == nodes_for_ports.end ()) { + pex::RNode *node = rnetwork.create_node (p->first.type, p->first.port_index); + db::DPoint loc = trans * p->first.location; + node->location = db::DBox (loc, loc); + n4p = nodes_for_ports.insert (std::make_pair (p->first, node)).first; + } + p->second = n4p->second; + } + + rextract_square_counting (db_poly, ports, rnetwork, dbu ()); + + } } From 1379d305020d2a938cc9e1b3b0c72ff37dafa744 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 19 Apr 2025 20:55:15 +0200 Subject: [PATCH 152/392] Debugging, Tests --- src/db/db/dbPLCConvexDecomposition.cc | 1 + src/pex/pex/pexSquareCountingRExtractor.cc | 55 +++--------- src/pex/pex/pexSquareCountingRExtractor.h | 37 ++++++++ .../pexSquareCountingRExtractorTests.cc | 90 ++++++++++++++++++- 4 files changed, 136 insertions(+), 47 deletions(-) diff --git a/src/db/db/dbPLCConvexDecomposition.cc b/src/db/db/dbPLCConvexDecomposition.cc index 2723eb796..426dfa06d 100644 --- a/src/db/db/dbPLCConvexDecomposition.cc +++ b/src/db/db/dbPLCConvexDecomposition.cc @@ -419,6 +419,7 @@ ConvexDecomposition::hertel_mehlhorn_decomposition (Triangulation &tris, const C for (auto i = iv->begin (); i != iv->end (); ++i) { poly->add_internal_vertex (*i); } + ++iv; } } diff --git a/src/pex/pex/pexSquareCountingRExtractor.cc b/src/pex/pex/pexSquareCountingRExtractor.cc index cdfc0d183..eaa865719 100644 --- a/src/pex/pex/pexSquareCountingRExtractor.cc +++ b/src/pex/pex/pexSquareCountingRExtractor.cc @@ -29,14 +29,6 @@ namespace pex { -SquareCountingRExtractor::SquareCountingRExtractor (double dbu) -{ - m_dbu = dbu; - - m_decomp_param.split_edges = true; - m_decomp_param.with_segments = false; -} - namespace { @@ -66,37 +58,6 @@ private: std::map > m_interactions; }; -struct PortDefinition -{ - PortDefinition () - : type (pex::RNode::Internal), port_index (0) - { } - - PortDefinition (pex::RNode::node_type _type, const db::Point &_location, unsigned int _port_index) - : type (_type), location (_location), port_index (_port_index) - { } - - bool operator< (const PortDefinition &other) const - { - if (type != other.type) { - return type < other.type; - } - if (port_index != other.port_index) { - return port_index < other.port_index; - } - return false; - } - - bool operator== (const PortDefinition &other) const - { - return type == other.type && port_index == other.port_index; - } - - pex::RNode::node_type type; - db::Point location; - unsigned int port_index; -}; - struct JoinEdgeSets { void operator() (std::set &a, const std::set &b) const @@ -107,6 +68,14 @@ struct JoinEdgeSets } +SquareCountingRExtractor::SquareCountingRExtractor (double dbu) +{ + m_dbu = dbu; + + m_decomp_param.split_edges = true; + m_decomp_param.with_segments = false; +} + static double yatx (const db::Edge &e, int x) { @@ -141,8 +110,8 @@ double calculate_squares (db::Coord x1, db::Coord x2, const std::set & } } -static -void rextract_square_counting (const db::Polygon &db_poly, const std::vector > &ports, pex::RNetwork &rnetwork, double /*dbu*/) +void +SquareCountingRExtractor::do_extract (const db::Polygon &db_poly, const std::vector > &ports, pex::RNetwork &rnetwork) { // "trans" will orient the polygon to be flat rather than tall db::Trans trans; @@ -189,7 +158,7 @@ void rextract_square_counting (const db::Polygon &db_poly, const std::vectorfirst.first < cc) { - r += calculate_squares (em->first.first, std::min (cc, em->first.second), em->second); + r += calculate_squares (std::max (c, em->first.first), std::min (cc, em->first.second), em->second); ++em; } @@ -320,7 +289,7 @@ SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector p->second = n4p->second; } - rextract_square_counting (db_poly, ports, rnetwork, dbu ()); + do_extract (db_poly, ports, rnetwork); } diff --git a/src/pex/pex/pexSquareCountingRExtractor.h b/src/pex/pex/pexSquareCountingRExtractor.h index 99881099b..b498bb1b2 100644 --- a/src/pex/pex/pexSquareCountingRExtractor.h +++ b/src/pex/pex/pexSquareCountingRExtractor.h @@ -55,6 +55,43 @@ public: virtual void extract (const db::Polygon &polygon, const std::vector &vertex_ports, const std::vector &polygon_ports, RNetwork &rnetwork); +protected: + /** + * @brief A helper structure defining a port + */ + struct PortDefinition + { + PortDefinition () + : type (pex::RNode::Internal), port_index (0) + { } + + PortDefinition (pex::RNode::node_type _type, const db::Point &_location, unsigned int _port_index) + : type (_type), location (_location), port_index (_port_index) + { } + + bool operator< (const PortDefinition &other) const + { + if (type != other.type) { + return type < other.type; + } + if (port_index != other.port_index) { + return port_index < other.port_index; + } + return false; + } + + bool operator== (const PortDefinition &other) const + { + return type == other.type && port_index == other.port_index; + } + + pex::RNode::node_type type; + db::Point location; + unsigned int port_index; + }; + + void do_extract (const db::Polygon &db_poly, const std::vector > &ports, pex::RNetwork &rnetwork); + private: db::plc::ConvexDecompositionParameters m_decomp_param; double m_dbu; diff --git a/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc b/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc index 613d630b9..1e4d7e326 100644 --- a/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc +++ b/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc @@ -24,7 +24,87 @@ #include "pexSquareCountingRExtractor.h" #include "tlUnitTest.h" +namespace +{ + +class TestableSquareCountingRExtractor + : public pex::SquareCountingRExtractor +{ +public: + TestableSquareCountingRExtractor () + : pex::SquareCountingRExtractor (0.001) + { } + + using pex::SquareCountingRExtractor::PortDefinition; + using pex::SquareCountingRExtractor::do_extract; +}; + +} + TEST(basic) +{ + db::Point contour[] = { + db::Point (0, 0), + db::Point (0, 100), + db::Point (1000, 1000), + db::Point (2100, 1000), + db::Point (2100, 0) + }; + + db::Polygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + TestableSquareCountingRExtractor rex; + pex::RNetwork rn; + + TestableSquareCountingRExtractor::PortDefinition pd1 (pex::RNode::Internal, db::Point (-50, 50), 0); + TestableSquareCountingRExtractor::PortDefinition pd2 (pex::RNode::Internal, db::Point (1000, 100), 1); + TestableSquareCountingRExtractor::PortDefinition pd3 (pex::RNode::Internal, db::Point (1000, 500), 2); + TestableSquareCountingRExtractor::PortDefinition pd4 (pex::RNode::Internal, db::Point (2000, 500), 3); + + std::vector pds; + pds.push_back (TestableSquareCountingRExtractor::PortDefinition (pex::RNode::Internal, db::Point (0, 50), 0)); + pds.push_back (TestableSquareCountingRExtractor::PortDefinition (pex::RNode::Internal, db::Point (1000, 100), 1)); + pds.push_back (TestableSquareCountingRExtractor::PortDefinition (pex::RNode::Internal, db::Point (1000, 500), 2)); + pds.push_back (TestableSquareCountingRExtractor::PortDefinition (pex::RNode::Internal, db::Point (2000, 500), 3)); + + std::vector > ports; + for (auto pd = pds.begin (); pd != pds.end (); ++pd) { + ports.push_back (std::make_pair (*pd, rn.create_node (pd->type, pd->port_index))); + } + + rex.do_extract (poly, ports, rn); + + EXPECT_EQ (rn.to_string (), + "R $0 $1 0.390865\n" // w ramp w=100 to 1000 over x=0 to 1000 (squares = (x2-x1)/(w2-w1)*log(w2/w1) by integration) + "R $1 $2 0\n" // transition from y=50 to y=500 parallel to current direction + "R $2 $3 1" // 1 square between x=1000 and 2000 (w=1000) + ); + + // After rotation + + rn.clear (); + + db::Trans r90 (db::Trans::r90); + + poly.transform (r90); + + ports.clear (); + for (auto pd = pds.begin (); pd != pds.end (); ++pd) { + ports.push_back (std::make_pair (*pd, rn.create_node (pd->type, pd->port_index))); + ports.back ().first.location.transform (r90); + } + + rex.do_extract (poly, ports, rn); + + // Same network, but opposite order. $1 and $2 are shorted, hence can be swapped. + EXPECT_EQ (rn.to_string (), + "R $3 $1 1\n" + "R $1 $2 0\n" + "R $2 $0 0.390865" + ); +} + +TEST(extraction) { db::Point contour[] = { db::Point (0, 0), @@ -46,15 +126,17 @@ TEST(basic) pex::SquareCountingRExtractor rex (dbu); std::vector vertex_ports; - vertex_ports.push_back (db::Point (0, 50)); - vertex_ports.push_back (db::Point (1650, 50)); + vertex_ports.push_back (db::Point (0, 50)); // V0 + vertex_ports.push_back (db::Point (1650, 50)); // V1 std::vector polygon_ports; - polygon_ports.push_back (db::Polygon (db::Box (1000, 900, 1100, 1000))); + polygon_ports.push_back (db::Polygon (db::Box (1000, 900, 1100, 1000))); // P0 rex.extract (poly, vertex_ports, polygon_ports, rn); EXPECT_EQ (rn.to_string (), - "" + "R V0 $0 0.0952381\n" + "R $0 V1 0.166667\n" + "R P0 $0 0.117647" ) } From fcd42bd0f11f0543f6899c7fdf71a838b81651ff Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 19 Apr 2025 23:48:58 +0200 Subject: [PATCH 153/392] R network simplify --- src/pex/pex/pexRExtractor.cc | 111 ++++++++++++++++++++- src/pex/pex/pexRExtractor.h | 13 +++ src/pex/pex/pexSquareCountingRExtractor.cc | 11 +- src/pex/pex/pexSquareCountingRExtractor.h | 6 +- src/pex/unit_tests/pexRExtractorTests.cc | 111 +++++++++++++++++++++ 5 files changed, 242 insertions(+), 10 deletions(-) diff --git a/src/pex/pex/pexRExtractor.cc b/src/pex/pex/pexRExtractor.cc index 4913c90ab..1df03457b 100644 --- a/src/pex/pex/pexRExtractor.cc +++ b/src/pex/pex/pexRExtractor.cc @@ -22,6 +22,7 @@ #include "pexRExtractor.h" +#include "tlEquivalenceClusters.h" namespace pex { @@ -102,9 +103,6 @@ RNetwork::clear () m_nodes_by_type.clear (); } -std::map, RElement *> m_elements_by_nodes; -std::map, RNode *> m_nodes; - RNode * RNetwork::create_node (RNode::node_type type, unsigned int port_index) { @@ -189,6 +187,113 @@ RNetwork::remove_element (RElement *element) } } +void +RNetwork::join_nodes (RNode *a, RNode *b) +{ + for (auto e = b->elements ().begin (); e != b->elements ().end (); ++e) { + RNode *on = const_cast ((*e)->other (b)); + if (on != a) { + create_element ((*e)->conductivity, on, a); + } + } + + remove_node (b); +} + +void +RNetwork::simplify () +{ + bool any_change = true; + + while (any_change) { + + any_change = false; + + // join shorted clusters - we take care to remove internal nodes only + + tl::equivalence_clusters clusters; + for (auto e = m_elements.begin (); e != m_elements.end (); ++e) { + if (e->conductivity == pex::RElement::short_value () && (e->a ()->type == pex::RNode::Internal || e->b ()->type == pex::RNode::Internal)) { + clusters.same (e->a (), e->b ()); + } + } + + for (size_t ic = 1; ic <= clusters.size (); ++ic) { + + RNode *remaining = 0; + RNode *first_node = 0; + for (auto c = clusters.begin_cluster (ic); c != clusters.end_cluster (ic); ++c) { + RNode *n = const_cast ((*c)->first); + if (! first_node) { + first_node = n; + } + if (n->type != pex::RNode::Internal) { + remaining = n; + break; + } + } + + if (! remaining) { + // Only internal nodes + remaining = first_node; + } + + for (auto c = clusters.begin_cluster (ic); c != clusters.end_cluster (ic); ++c) { + RNode *n = const_cast ((*c)->first); + if (n != remaining && n->type == pex::RNode::Internal) { + any_change = true; + join_nodes (remaining, n); + } + } + + } + + // combine serial resistors if connected through an internal node + + std::vector nodes_to_remove; + + for (auto n = m_nodes.begin (); n != m_nodes.end (); ++n) { + + size_t nres = n->elements ().size (); + + if (n->type == pex::RNode::Internal && nres <= 2) { + + any_change = true; + + if (nres == 2) { + + auto e = n->elements ().begin (); + + RNode *n1 = const_cast ((*e)->other (n.operator-> ())); + double r1 = (*e)->resistance (); + + ++e; + RNode *n2 = const_cast ((*e)->other (n.operator-> ())); + double r2 = (*e)->resistance (); + + double r = r1 + r2; + if (r == 0.0) { + create_element (pex::RElement::short_value (), n1, n2); + } else { + create_element (1.0 / r, n1, n2); + } + + } + + nodes_to_remove.push_back (n.operator-> ()); + + } + + } + + for (auto n = nodes_to_remove.begin (); n != nodes_to_remove.end (); ++n) { + remove_node (*n); + } + + } + +} + // ----------------------------------------------------------------------------- RExtractor::RExtractor () diff --git a/src/pex/pex/pexRExtractor.h b/src/pex/pex/pexRExtractor.h index 890b6ad67..f1c4f4659 100644 --- a/src/pex/pex/pexRExtractor.h +++ b/src/pex/pex/pexRExtractor.h @@ -88,6 +88,16 @@ struct PEX_PUBLIC RElement const RNode *a () const { return mp_a; } const RNode *b () const { return mp_b; } + const RNode *other (const RNode *n) const + { + if (mp_a == n) { + return mp_b; + } else if (mp_b == n) { + return mp_a; + } + tl_assert (false); + } + static double short_value () { return std::numeric_limits::infinity (); @@ -140,6 +150,7 @@ public: void remove_element (RElement *element); void remove_node (RNode *node); void clear (); + void simplify (); std::string to_string () const; @@ -151,6 +162,8 @@ private: RNetwork (const RNetwork &); RNetwork &operator= (const RNetwork &); + + void join_nodes (RNode *a, RNode *b); }; diff --git a/src/pex/pex/pexSquareCountingRExtractor.cc b/src/pex/pex/pexSquareCountingRExtractor.cc index eaa865719..5477abdb0 100644 --- a/src/pex/pex/pexSquareCountingRExtractor.cc +++ b/src/pex/pex/pexSquareCountingRExtractor.cc @@ -133,11 +133,11 @@ SquareCountingRExtractor::do_extract (const db::Polygon &db_poly, const std::vec } } - // sort the port locations + // sort the port locations - note that we take the port box centers for the location! std::multimap port_locations; for (auto p = ports.begin (); p != ports.end (); ++p) { - db::Coord c = (trans * p->first.location).x (); + db::Coord c = (trans * p->first.location).center ().x (); port_locations.insert (std::make_pair (c, p->second)); } @@ -258,7 +258,7 @@ SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector // 1. internal ports for (auto i = ip_indexes.begin (); i != ip_indexes.end (); ++i) { - db::Point loc = (inv_trans * internal_port_edges [*i]->edge ()).bbox ().center (); + db::Box loc = (inv_trans * internal_port_edges [*i]->edge ()).bbox (); ports.push_back (std::make_pair (PortDefinition (pex::RNode::Internal, loc, *i), (pex::RNode *) 0)); } @@ -271,7 +271,7 @@ SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector // 3. polygon ports // (NOTE: here we only take the center of the bounding box) for (auto i = pp_indexes.begin (); i != pp_indexes.end (); ++i) { - db::Point loc = polygon_ports [*i].box ().center (); + db::Box loc = polygon_ports [*i].box (); ports.push_back (std::make_pair (PortDefinition (pex::RNode::PolygonPort, loc, *i), (pex::RNode *) 0)); } @@ -282,8 +282,7 @@ SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector auto n4p = nodes_for_ports.find (p->first); if (n4p == nodes_for_ports.end ()) { pex::RNode *node = rnetwork.create_node (p->first.type, p->first.port_index); - db::DPoint loc = trans * p->first.location; - node->location = db::DBox (loc, loc); + node->location = trans * p->first.location; n4p = nodes_for_ports.insert (std::make_pair (p->first, node)).first; } p->second = n4p->second; diff --git a/src/pex/pex/pexSquareCountingRExtractor.h b/src/pex/pex/pexSquareCountingRExtractor.h index b498bb1b2..abacc2dfa 100644 --- a/src/pex/pex/pexSquareCountingRExtractor.h +++ b/src/pex/pex/pexSquareCountingRExtractor.h @@ -66,6 +66,10 @@ protected: { } PortDefinition (pex::RNode::node_type _type, const db::Point &_location, unsigned int _port_index) + : type (_type), location (_location, _location), port_index (_port_index) + { } + + PortDefinition (pex::RNode::node_type _type, const db::Box &_location, unsigned int _port_index) : type (_type), location (_location), port_index (_port_index) { } @@ -86,7 +90,7 @@ protected: } pex::RNode::node_type type; - db::Point location; + db::Box location; unsigned int port_index; }; diff --git a/src/pex/unit_tests/pexRExtractorTests.cc b/src/pex/unit_tests/pexRExtractorTests.cc index 3915e8337..d386ce621 100644 --- a/src/pex/unit_tests/pexRExtractorTests.cc +++ b/src/pex/unit_tests/pexRExtractorTests.cc @@ -74,3 +74,114 @@ TEST(network_basic) EXPECT_EQ (rn.to_string (), ""); } +TEST(network_simplify1) +{ + pex::RNetwork rn; + EXPECT_EQ (rn.to_string (), ""); + + pex::RNode *n1 = rn.create_node (pex::RNode::VertexPort, 1); + pex::RNode *n2 = rn.create_node (pex::RNode::Internal, 2); + pex::RNode *n3 = rn.create_node (pex::RNode::VertexPort, 3); + + rn.create_element (1, n1, n2); + rn.create_element (pex::RElement::short_value (), n2, n3); + rn.create_element (1, n1, n3); + + EXPECT_EQ (rn.to_string (), + "R V1 $2 1\n" + "R $2 V3 0\n" + "R V1 V3 1" + ); + + rn.simplify (); + + EXPECT_EQ (rn.to_string (), + "R V1 V3 0.5" + ); +} + +TEST(network_simplify2) +{ + pex::RNetwork rn; + EXPECT_EQ (rn.to_string (), ""); + + pex::RNode *n1 = rn.create_node (pex::RNode::VertexPort, 1); + pex::RNode *n2 = rn.create_node (pex::RNode::Internal, 2); + pex::RNode *n3 = rn.create_node (pex::RNode::Internal, 3); + pex::RNode *n4 = rn.create_node (pex::RNode::VertexPort, 4); + pex::RNode *n5 = rn.create_node (pex::RNode::VertexPort, 5); + + rn.create_element (1, n1, n2); + rn.create_element (pex::RElement::short_value (), n2, n3); + rn.create_element (1, n3, n4); + rn.create_element (1, n3, n5); + + EXPECT_EQ (rn.to_string (), + "R V1 $2 1\n" + "R $2 $3 0\n" + "R $3 V4 1\n" + "R $3 V5 1" + ); + + rn.simplify (); + + EXPECT_EQ (rn.to_string (), + "R V1 $2 1\n" + "R V4 $2 1\n" + "R V5 $2 1" + ); +} + +TEST(network_simplify3) +{ + pex::RNetwork rn; + EXPECT_EQ (rn.to_string (), ""); + + pex::RNode *n1 = rn.create_node (pex::RNode::VertexPort, 1); + pex::RNode *n2 = rn.create_node (pex::RNode::Internal, 2); + pex::RNode *n3 = rn.create_node (pex::RNode::Internal, 3); + pex::RNode *n4 = rn.create_node (pex::RNode::VertexPort, 4); + + rn.create_element (1, n1, n2); + rn.create_element (pex::RElement::short_value (), n2, n3); + rn.create_element (1, n3, n4); + + EXPECT_EQ (rn.to_string (), + "R V1 $2 1\n" + "R $2 $3 0\n" + "R $3 V4 1" + ); + + rn.simplify (); + + EXPECT_EQ (rn.to_string (), + "R V1 V4 2" + ); +} + +TEST(network_simplify4) +{ + pex::RNetwork rn; + EXPECT_EQ (rn.to_string (), ""); + + pex::RNode *n1 = rn.create_node (pex::RNode::VertexPort, 1); + pex::RNode *n2 = rn.create_node (pex::RNode::Internal, 2); + pex::RNode *n3 = rn.create_node (pex::RNode::Internal, 3); + pex::RNode *n4 = rn.create_node (pex::RNode::VertexPort, 4); + + rn.create_element (1, n1, n4); + rn.create_element (1, n2, n1); + rn.create_element (1, n4, n3); + + EXPECT_EQ (rn.to_string (), + "R V1 V4 1\n" + "R $2 V1 1\n" + "R V4 $3 1" + ); + + rn.simplify (); + + EXPECT_EQ (rn.to_string (), + "R V1 V4 1" + ); +} From e9c2320f5decd6961a37d1f5b650102f899f2fe0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 19 Apr 2025 23:59:45 +0200 Subject: [PATCH 154/392] WIP --- src/pex/pex/pexRExtractor.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pex/pex/pexRExtractor.cc b/src/pex/pex/pexRExtractor.cc index 1df03457b..d7d529dff 100644 --- a/src/pex/pex/pexRExtractor.cc +++ b/src/pex/pex/pexRExtractor.cc @@ -197,6 +197,7 @@ RNetwork::join_nodes (RNode *a, RNode *b) } } + a->location += b->location; remove_node (b); } From 88a1ccbcfb9bd546b90ed91a1e8c9d5dff4bdf9f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 20 Apr 2025 16:20:35 +0200 Subject: [PATCH 155/392] TriangulationRExtractor, some debugging, tests --- src/db/db/dbPLCTriangulation.cc | 2 +- src/pex/pex/pex.pro | 6 +- src/pex/pex/pexRExtractor.cc | 9 +- src/pex/pex/pexRExtractor.h | 50 +++- src/pex/pex/pexTriangulationRExtractor.cc | 221 ++++++++++++++++++ src/pex/pex/pexTriangulationRExtractor.h | 70 ++++++ src/pex/unit_tests/pexRExtractorTests.cc | 9 + .../pexTriangulationRExtractorTests.cc | 69 ++++++ src/pex/unit_tests/unit_tests.pro | 3 +- 9 files changed, 431 insertions(+), 8 deletions(-) create mode 100644 src/pex/pex/pexTriangulationRExtractor.cc create mode 100644 src/pex/pex/pexTriangulationRExtractor.h create mode 100644 src/pex/unit_tests/pexTriangulationRExtractorTests.cc diff --git a/src/db/db/dbPLCTriangulation.cc b/src/db/db/dbPLCTriangulation.cc index 3c9371fe4..07688f0c3 100644 --- a/src/db/db/dbPLCTriangulation.cc +++ b/src/db/db/dbPLCTriangulation.cc @@ -1043,7 +1043,7 @@ Triangulation::search_edges_crossing (Vertex *from, Vertex *to) if (e->has_vertex (vv)) { return result; } - if (e->crosses (edge)) { + if (e->crosses_including (edge)) { result.push_back (e); next_edge = e; break; diff --git a/src/pex/pex/pex.pro b/src/pex/pex/pex.pro index 251fb9935..145a1866e 100644 --- a/src/pex/pex/pex.pro +++ b/src/pex/pex/pex.pro @@ -10,12 +10,14 @@ SOURCES = \ pexForceLink.cc \ pexRExtractor.cc \ gsiDeclRExtractor.cc \ - pexSquareCountingRExtractor.cc + pexSquareCountingRExtractor.cc \ + pexTriangulationRExtractor.cc HEADERS = \ pexForceLink.h \ pexRExtractor.h \ - pexSquareCountingRExtractor.h + pexSquareCountingRExtractor.h \ + pexTriangulationRExtractor.h RESOURCES = \ diff --git a/src/pex/pex/pexRExtractor.cc b/src/pex/pex/pexRExtractor.cc index d7d529dff..db38fc948 100644 --- a/src/pex/pex/pexRExtractor.cc +++ b/src/pex/pex/pexRExtractor.cc @@ -134,7 +134,12 @@ RNetwork::create_node (RNode::node_type type, unsigned int port_index) RElement * RNetwork::create_element (double conductivity, RNode *a, RNode *b) { - auto i = m_elements_by_nodes.find (std::make_pair (a, b)); + std::pair key (a, b); + if (size_t (b) < size_t (a)) { + std::swap (key.first, key.second); + } + + auto i = m_elements_by_nodes.find (key); if (i != m_elements_by_nodes.end ()) { if (conductivity == pex::RElement::short_value () || i->second->conductivity == pex::RElement::short_value ()) { @@ -149,7 +154,7 @@ RNetwork::create_element (double conductivity, RNode *a, RNode *b) RElement *element = new RElement (this, conductivity, a, b); m_elements.push_back (element); - m_elements_by_nodes.insert (std::make_pair (std::make_pair (a, b), element)); + m_elements_by_nodes.insert (std::make_pair (key, element)); a->m_elements.push_back (element); element->m_ia = --a->m_elements.end (); diff --git a/src/pex/pex/pexRExtractor.h b/src/pex/pex/pexRExtractor.h index f1c4f4659..11e05e00d 100644 --- a/src/pex/pex/pexRExtractor.h +++ b/src/pex/pex/pexRExtractor.h @@ -142,6 +142,11 @@ class PEX_PUBLIC RNetwork : public tl::Object { public: + typedef tl::list node_list; + typedef node_list::const_iterator node_iterator; + typedef tl::list element_list; + typedef element_list::const_iterator element_iterator; + RNetwork (); ~RNetwork (); @@ -154,9 +159,50 @@ public: std::string to_string () const; + node_iterator begin_nodes () const + { + return m_nodes.begin (); + } + + node_iterator end_nodes () const + { + return m_nodes.end (); + } + + size_t num_nodes () const + { + return m_nodes.size (); + } + + size_t num_internal_nodes () const + { + size_t count = 0; + for (auto n = m_nodes.begin (); n != m_nodes.end (); ++n) { + if (n->type == pex::RNode::Internal) { + ++count; + } + } + return count; + } + + element_iterator begin_elements () const + { + return m_elements.begin (); + } + + element_iterator end_elements () const + { + return m_elements.end (); + } + + size_t num_elements () const + { + return m_elements.size (); + } + private: - tl::list m_nodes; - tl::list m_elements; + node_list m_nodes; + element_list m_elements; std::map, RElement *> m_elements_by_nodes; std::map, RNode *> m_nodes_by_type; diff --git a/src/pex/pex/pexTriangulationRExtractor.cc b/src/pex/pex/pexTriangulationRExtractor.cc new file mode 100644 index 000000000..d464e8fdb --- /dev/null +++ b/src/pex/pex/pexTriangulationRExtractor.cc @@ -0,0 +1,221 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "pexTriangulationRExtractor.h" +#include "dbBoxScanner.h" +#include "dbPolygonTools.h" +#include "tlIntervalMap.h" + +namespace pex +{ + +TriangulationRExtractor::TriangulationRExtractor (double dbu) +{ + m_dbu = dbu; + + m_tri_param.min_b = 0.3; + m_tri_param.max_area = 0.0; +} + +void +TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector &vertex_ports, const std::vector &polygon_ports, pex::RNetwork &rnetwork) +{ + rnetwork.clear (); + + db::CplxTrans trans = db::CplxTrans (m_dbu) * db::ICplxTrans (db::Trans (db::Point () - polygon.box ().center ())); + auto inv_trans = trans.inverted (); + + // NOTE: currently we treat polygon ports and points where the location is the center of the bounding box + std::vector vp = vertex_ports; + vp.reserve (vertex_ports.size () + polygon_ports.size ()); + for (auto pp = polygon_ports.begin (); pp != polygon_ports.end (); ++pp) { + vp.push_back (pp->box ().center ()); + } + + + db::plc::Graph plc; + db::plc::Triangulation tri (&plc); + + tri.triangulate (polygon, vp, m_tri_param, trans); + + // create a network node for each triangle node + + std::unordered_map vertex2node; + + size_t internal_node_id = 0; + + for (auto p = plc.begin (); p != plc.end (); ++p) { + + for (size_t iv = 0; iv < p->size (); ++iv) { + + const db::plc::Vertex *vertex = p->vertex (iv); + if (vertex2node.find (vertex) != vertex2node.end ()) { + continue; + } + + pex::RNode::node_type type = pex::RNode::Internal; + size_t port_index = 0; + + if (vertex->is_precious ()) { + size_t idx = vertex->id (); + if (idx >= vertex_ports.size ()) { + type = pex::RNode::PolygonPort; + port_index = size_t (idx) - vertex_ports.size (); + } else { + type = pex::RNode::VertexPort; + port_index = size_t (idx); + } + } else { + port_index = internal_node_id++; + } + + pex::RNode *n = rnetwork.create_node (type, port_index); + db::DPoint loc = *vertex; + n->location = db::DBox (loc, loc); + + vertex2node.insert (std::make_pair (vertex, n)); + + } + + } + + // produce the conductances for each triangle + + for (auto p = plc.begin (); p != plc.end (); ++p) { + create_conductances (*p, vertex2node, rnetwork); + } + + // eliminate internal nodes + + eliminate_all (rnetwork); +} + +void +TriangulationRExtractor::create_conductances (const db::plc::Polygon &tri, const std::unordered_map &vertex2node, RNetwork &rnetwork) +{ + tl_assert (tri.size () == 3); + + for (int i = 0; i < 3; ++i) { + + const db::plc::Vertex *pm1 = tri.vertex (i); + const db::plc::Vertex *p0 = tri.vertex (i + 1); + const db::plc::Vertex *p1 = tri.vertex (i + 2); + + double a = fabs (db::vprod (*pm1 - *p0, *p1 - *p0) * 0.5); + + double lm1 = (*p0 - *pm1).sq_length (); + double l0 = (*p1 - *p0).sq_length (); + double l1 = (*pm1 - *p1).sq_length (); + + double s = (l0 + l1 - lm1) / (8.0 * a); + + auto i0 = vertex2node.find (p0); + auto im1 = vertex2node.find (pm1); + rnetwork.create_element (s, i0->second, im1->second); + + } +} + +void +TriangulationRExtractor::eliminate_all (RNetwork &rnetwork) +{ + if (tl::verbosity () >= m_tri_param.base_verbosity + 10) { + tl::info << "Staring elimination with " << rnetwork.num_internal_nodes () << " internal nodes and " << rnetwork.num_elements () << " resistors"; + } + + unsigned int niter = 0; + std::vector to_eliminate; + + size_t nmax = 3; + while (nmax > 0) { + + bool another_loop = true; + while (another_loop) { + + size_t nmax_next = 0; + to_eliminate.clear (); + + for (auto n = rnetwork.begin_nodes (); n != rnetwork.end_nodes (); ++n) { + if (n->type == pex::RNode::Internal) { + size_t nn = n->elements ().size (); + if (nn <= nmax) { + to_eliminate.push_back (const_cast (n.operator-> ())); + } else if (nmax_next == 0 or nn < nmax_next) { + nmax_next = nn; + } + } + } + + if (to_eliminate.empty ()) { + + another_loop = false; + nmax = nmax_next; + + if (tl::verbosity () >= m_tri_param.base_verbosity + 10) { + tl::info << "Nothing left to eliminate with nmax=" << nmax; + } + + } else { + + for (auto n = to_eliminate.begin (); n != to_eliminate.end (); ++n) { + eliminate_node (*n, rnetwork); + } + + niter += 1; + + if (tl::verbosity () >= m_tri_param.base_verbosity + 10) { + tl::info << "Nodes left after iteration " << niter << " with nmax=" << nmax << ": " << rnetwork.num_internal_nodes () << " with " << rnetwork.num_elements () << " edges."; + } + + } + + } + + } +} + +void +TriangulationRExtractor::eliminate_node (pex::RNode *node, RNetwork &rnetwork) +{ + double s_sum = 0.0; + for (auto e = node->elements ().begin (); e != node->elements ().end (); ++e) { + s_sum += (*e)->conductivity; + } + + if (fabs (s_sum) > 1e-10) { + for (auto e = node->elements ().begin (); e != node->elements ().end (); ++e) { + auto ee = e; + ++ee; + for ( ; ee != node->elements ().end (); ++ee) { + pex::RNode *n1 = const_cast ((*e)->other (node)); + pex::RNode *n2 = const_cast ((*ee)->other (node)); + double c = (*e)->conductivity * (*ee)->conductivity / s_sum; + rnetwork.create_element (c, n1, n2); + } + } + } + + rnetwork.remove_node (node); +} + +} diff --git a/src/pex/pex/pexTriangulationRExtractor.h b/src/pex/pex/pexTriangulationRExtractor.h new file mode 100644 index 000000000..536d90f45 --- /dev/null +++ b/src/pex/pex/pexTriangulationRExtractor.h @@ -0,0 +1,70 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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_pexTriangulationRExtractor +#define HDR_pexTriangulationRExtractor + +#include "pexCommon.h" +#include "pexRExtractor.h" + +#include "dbPLCTriangulation.h" + +namespace pex +{ + +// @@@ doc +class PEX_PUBLIC TriangulationRExtractor + : public RExtractor +{ +public: + TriangulationRExtractor (double dbu); + + db::plc::TriangulationParameters &triangulation_parameters () + { + return m_tri_param; + } + + void set_dbu (double dbu) + { + m_dbu = dbu; + } + + double dbu () const + { + return m_dbu; + } + + virtual void extract (const db::Polygon &polygon, const std::vector &vertex_ports, const std::vector &polygon_ports, RNetwork &rnetwork); + +private: + db::plc::TriangulationParameters m_tri_param; + double m_dbu; + + void create_conductances (const db::plc::Polygon &tri, const std::unordered_map &vertex2node, RNetwork &rnetwork); + void eliminate_node (pex::RNode *node, RNetwork &rnetwork); + void eliminate_all (RNetwork &rnetwork); +}; + +} + +#endif + diff --git a/src/pex/unit_tests/pexRExtractorTests.cc b/src/pex/unit_tests/pexRExtractorTests.cc index d386ce621..1c2de17b7 100644 --- a/src/pex/unit_tests/pexRExtractorTests.cc +++ b/src/pex/unit_tests/pexRExtractorTests.cc @@ -56,6 +56,15 @@ TEST(network_basic) "R $2 $3 0.2" ); + pex::RElement *e23c = rn.create_element (5.0, n3, n2); + EXPECT_EQ (e23 == e23c, true); + + EXPECT_EQ (rn.to_string (), + "R $1 $2 2\n" + "R $1 $3 4\n" + "R $2 $3 0.1" + ); + rn.remove_element (e23); EXPECT_EQ (rn.to_string (), diff --git a/src/pex/unit_tests/pexTriangulationRExtractorTests.cc b/src/pex/unit_tests/pexTriangulationRExtractorTests.cc new file mode 100644 index 000000000..46609d1ec --- /dev/null +++ b/src/pex/unit_tests/pexTriangulationRExtractorTests.cc @@ -0,0 +1,69 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "pexTriangulationRExtractor.h" +#include "tlUnitTest.h" + +namespace +{ + +class TestableTriangulationRExtractor + : public pex::TriangulationRExtractor +{ +public: + TestableTriangulationRExtractor () + : pex::TriangulationRExtractor (0.001) + { } +}; + +} + +TEST(extraction) +{ + db::Point contour[] = { + db::Point (0, 0), + db::Point (0, 100), + db::Point (1000, 100), + db::Point (1000, 0) + }; + + db::Polygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + + double dbu = 0.001; + + pex::RNetwork rn; + pex::TriangulationRExtractor rex (dbu); + + std::vector vertex_ports; + vertex_ports.push_back (db::Point (0, 50)); // V0 + vertex_ports.push_back (db::Point (1000, 50)); // V1 + + std::vector polygon_ports; + + rex.extract (poly, vertex_ports, polygon_ports, rn); + + EXPECT_EQ (rn.to_string (), + "R V1 V0 10.0938" + ) +} diff --git a/src/pex/unit_tests/unit_tests.pro b/src/pex/unit_tests/unit_tests.pro index 6c380c340..ad99cd169 100644 --- a/src/pex/unit_tests/unit_tests.pro +++ b/src/pex/unit_tests/unit_tests.pro @@ -8,7 +8,8 @@ include($$PWD/../../lib_ut.pri) SOURCES = \ pexRExtractorTests.cc \ - pexSquareCountingRExtractorTests.cc + pexSquareCountingRExtractorTests.cc \ + pexTriangulationRExtractorTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC $$PEX_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC $$PEX_INC From f71210c64a5f76212b32745d3e752b76133da165 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 20 Apr 2025 19:09:24 +0200 Subject: [PATCH 156/392] WIP, more testcases, debugging --- src/db/db/dbPLC.cc | 10 +- src/db/db/dbPLCTriangulation.cc | 78 +++++--- src/db/db/dbPLCTriangulation.h | 64 ++++-- src/db/unit_tests/dbPLCTriangulationTests.cc | 28 +-- src/pex/pex/pexTriangulationRExtractor.cc | 166 ++++++++++++---- .../pexTriangulationRExtractorTests.cc | 188 ++++++++++++++++++ 6 files changed, 442 insertions(+), 92 deletions(-) diff --git a/src/db/db/dbPLC.cc b/src/db/db/dbPLC.cc index c44a96760..7a8be1455 100644 --- a/src/db/db/dbPLC.cc +++ b/src/db/db/dbPLC.cc @@ -300,8 +300,14 @@ Edge::crosses (const db::DEdge &e, const db::DEdge &other) bool Edge::crosses_including (const db::DEdge &e, const db::DEdge &other) { - return e.side_of (other.p1 ()) * e.side_of (other.p2 ()) <= 0 && - other.side_of (e.p1 ()) * other.side_of (e.p2 ()) <= 0; + int sa = e.side_of (other.p1 ()); + int sb = e.side_of (other.p2 ()); + int s1 = sa * sb; + + int s2 = other.side_of (e.p1 ()) * other.side_of (e.p2 ()); + + // e can end on other and so can other end on e, but both may not be coincident + return s1 <= 0 && s2 <= 0 && ! (sa == 0 && sb == 0); } db::DPoint diff --git a/src/db/db/dbPLCTriangulation.cc b/src/db/db/dbPLCTriangulation.cc index 07688f0c3..f8f4fdcc9 100644 --- a/src/db/db/dbPLCTriangulation.cc +++ b/src/db/db/dbPLCTriangulation.cc @@ -1099,14 +1099,19 @@ Triangulation::find_vertexes_along_line (const db::DPoint &p1, const db::DPoint } std::vector result; + result.push_back (v); while (v) { Vertex *vn = 0; for (auto e = v->begin_edges (); e != v->end_edges (); ++e) { Vertex *vv = (*e)->other (v); - if (db::vprod_sign (e12.d (), *vv - *v) == 0 && db::sprod_sign (e12.d (), *vv - *v) > 0 && db::sprod_sign (e12.d (), *vv - e12.p2 ()) < 0) { + int cs = 0; + if (db::vprod_sign (e12.d (), *vv - *v) == 0 && db::sprod_sign (e12.d (), *vv - *v) > 0 && (cs = db::sprod_sign (e12.d (), *vv - e12.p2 ())) <= 0) { result.push_back (vv); - vn = vv; + if (cs < 0) { + // continue searching + vn = vv; + } break; } } @@ -1377,8 +1382,6 @@ Triangulation::make_contours (const Poly &poly, const Trans &trans, std::vector< void Triangulation::create_constrained_delaunay (const db::Region ®ion, const CplxTrans &trans) { - clear (); - std::vector > edge_contours; for (auto p = region.begin_merged (); ! p.at_end (); ++p) { @@ -1389,15 +1392,8 @@ Triangulation::create_constrained_delaunay (const db::Region ®ion, const Cplx } void -Triangulation::create_constrained_delaunay (const db::Polygon &p, const std::vector &vertexes, const CplxTrans &trans) +Triangulation::create_constrained_delaunay (const db::Polygon &p, const CplxTrans &trans) { - clear (); - - unsigned int id = 0; - for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { - insert_point (trans * *v)->set_is_precious (true, id++); - } - std::vector > edge_contours; make_contours (p, trans, edge_contours); @@ -1405,15 +1401,8 @@ Triangulation::create_constrained_delaunay (const db::Polygon &p, const std::vec } void -Triangulation::create_constrained_delaunay (const db::DPolygon &p, const std::vector &vertexes, const DCplxTrans &trans) +Triangulation::create_constrained_delaunay (const db::DPolygon &p, const DCplxTrans &trans) { - clear (); - - unsigned int id = 0; - for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { - insert_point (trans * *v)->set_is_precious (true, id++); - } - std::vector > edge_contours; make_contours (p, trans, edge_contours); @@ -1458,6 +1447,8 @@ Triangulation::triangulate (const db::Region ®ion, const TriangulationParamet { tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate"); + clear (); + create_constrained_delaunay (region, db::CplxTrans (dbu)); refine (parameters); } @@ -1467,6 +1458,24 @@ Triangulation::triangulate (const db::Region ®ion, const TriangulationParamet { tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate"); + clear (); + + create_constrained_delaunay (region, trans); + refine (parameters); +} + +void +Triangulation::triangulate (const db::Region ®ion, const std::vector &vertexes, const TriangulationParameters ¶meters, const db::CplxTrans &trans) +{ + tl::SelfTimer timer (tl::verbosity () > parameters.base_verbosity, "Triangles::triangulate"); + + clear (); + + unsigned int id = 0; + for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { + insert_point (trans * *v)->set_is_precious (true, id++); + } + create_constrained_delaunay (region, trans); refine (parameters); } @@ -1482,7 +1491,16 @@ Triangulation::triangulate (const db::Polygon &poly, const std::vector parameters.base_verbosity, "Triangles::triangulate"); - create_constrained_delaunay (poly, vertexes, db::CplxTrans (dbu)); + db::CplxTrans trans (dbu); + + clear (); + + unsigned int id = 0; + for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { + insert_point (trans * *v)->set_is_precious (true, id++); + } + + create_constrained_delaunay (poly, trans); refine (parameters); } @@ -1497,7 +1515,14 @@ Triangulation::triangulate (const db::Polygon &poly, const std::vector parameters.base_verbosity, "Triangles::triangulate"); - create_constrained_delaunay (poly, vertexes, trans); + clear (); + + unsigned int id = 0; + for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { + insert_point (trans * *v)->set_is_precious (true, id++); + } + + create_constrained_delaunay (poly, trans); refine (parameters); } @@ -1512,7 +1537,14 @@ Triangulation::triangulate (const db::DPolygon &poly, const std::vector parameters.base_verbosity, "Triangles::triangulate"); - create_constrained_delaunay (poly, vertexes, trans); + clear (); + + unsigned int id = 0; + for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { + insert_point (trans * *v)->set_is_precious (true, id++); + } + + create_constrained_delaunay (poly, trans); refine (parameters); } diff --git a/src/db/db/dbPLCTriangulation.h b/src/db/db/dbPLCTriangulation.h index ea0830b00..db97f9651 100644 --- a/src/db/db/dbPLCTriangulation.h +++ b/src/db/db/dbPLCTriangulation.h @@ -142,6 +142,7 @@ public: // more versions void triangulate (const db::Region ®ion, const TriangulationParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ()); + void triangulate (const db::Region ®ion, const std::vector &vertexes, const TriangulationParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ()); void triangulate (const db::Polygon &poly, const TriangulationParameters ¶meters, double dbu = 1.0); void triangulate (const db::Polygon &poly, const std::vector &vertexes, const TriangulationParameters ¶meters, double dbu = 1.0); void triangulate (const db::Polygon &poly, const TriangulationParameters ¶meters, const db::CplxTrans &trans = db::CplxTrans ()); @@ -181,6 +182,17 @@ public: */ std::vector find_vertexes_along_line (const db::DPoint &p1, const db::DPoint &p2) const; + /** + * @brief Removes the outside triangles. + * + * This method is useful in combination with the "remove_outside_triangles = false" triangulation + * parameter. In this mode, outside triangles are not removed after triangulation (the + * triangulated area is convex). This enables use of the "find" functions. + * + * This method can be used to finally remove the outside triangles if no longer needed. + */ + void remove_outside_triangles (); + /** * @brief Statistics: number of flips (fixing) */ @@ -197,6 +209,37 @@ public: return m_hops; } + /** + * @brief Creates a constrained Delaunay triangulation from the given Region + * + * This method is used internally by the "triangulation" method to create the basic triangulation, + * followed by a "refine" step. + */ + void create_constrained_delaunay (const db::Region ®ion, const db::CplxTrans &trans = db::CplxTrans ()); + + /** + * @brief Creates a constrained Delaunay triangulation from the given Polygon + * + * This method is used internally by the "triangulation" method to create the basic triangulation, + * followed by a "refine" step. + */ + void create_constrained_delaunay (const db::Polygon &poly, const db::CplxTrans &trans = db::CplxTrans ()); + + /** + * @brief Creates a constrained Delaunay triangulation from the given DPolygon + * + * This method is used internally by the "triangulation" method to create the basic triangulation, + * followed by a "refine" step. + */ + void create_constrained_delaunay (const db::DPolygon &poly, const DCplxTrans &trans = db::DCplxTrans ()); + + /** + * @brief Refines the triangulation using the given parameters + * + * This method is used internally by the "triangulation" method after creating the basic triangulation. + */ + void refine (const TriangulationParameters ¶m); + protected: /** * @brief Checks the polygon graph for consistency @@ -254,26 +297,6 @@ protected: */ void constrain (const std::vector > &contours); - /** - * @brief Removes the outside triangles. - */ - void remove_outside_triangles (); - - /** - * @brief Creates a constrained Delaunay triangulation from the given Region - */ - void create_constrained_delaunay (const db::Region ®ion, const db::CplxTrans &trans = db::CplxTrans ()); - - /** - * @brief Creates a constrained Delaunay triangulation from the given Polygon - */ - void create_constrained_delaunay (const db::Polygon &poly, const std::vector &vertexes, const db::CplxTrans &trans = db::CplxTrans ()); - - /** - * @brief Creates a constrained Delaunay triangulation from the given DPolygon - */ - void create_constrained_delaunay (const db::DPolygon &poly, const std::vector &vertexes, const DCplxTrans &trans); - /** * @brief Returns a value indicating whether the edge is "illegal" (violates the Delaunay criterion) */ @@ -308,7 +331,6 @@ private: void insert_new_vertex(Vertex *vertex, std::list > *new_triangles_out); std::vector ensure_edge_inner (Vertex *from, Vertex *to); void join_edges (std::vector &edges); - void refine (const TriangulationParameters ¶m); }; } // namespace plc diff --git a/src/db/unit_tests/dbPLCTriangulationTests.cc b/src/db/unit_tests/dbPLCTriangulationTests.cc index 9253eb0ce..f31a049b2 100644 --- a/src/db/unit_tests/dbPLCTriangulationTests.cc +++ b/src/db/unit_tests/dbPLCTriangulationTests.cc @@ -155,24 +155,28 @@ TEST(collect_vertexes) tris.insert_point (0.5, 0.5); std::vector vertexes = tris.find_vertexes_along_line (db::DPoint (0, 0), db::DPoint (1.5, 1.5)); - EXPECT_EQ (vertexes.size (), size_t (3)); - if (vertexes.size () >= size_t (3)) { - EXPECT_EQ (vertexes [0]->to_string (), "(0.2, 0.2)"); - EXPECT_EQ (vertexes [1]->to_string (), "(0.5, 0.5)"); - EXPECT_EQ (vertexes [2]->to_string (), "(1, 1)"); + EXPECT_EQ (vertexes.size (), size_t (4)); + if (vertexes.size () >= size_t (4)) { + EXPECT_EQ (vertexes [0]->to_string (), "(0, 0)"); + EXPECT_EQ (vertexes [1]->to_string (), "(0.2, 0.2)"); + EXPECT_EQ (vertexes [2]->to_string (), "(0.5, 0.5)"); + EXPECT_EQ (vertexes [3]->to_string (), "(1, 1)"); } vertexes = tris.find_vertexes_along_line (db::DPoint (0, 0), db::DPoint (1.0, 1.0)); - EXPECT_EQ (vertexes.size (), size_t (2)); - if (vertexes.size () >= size_t (2)) { - EXPECT_EQ (vertexes [0]->to_string (), "(0.2, 0.2)"); - EXPECT_EQ (vertexes [1]->to_string (), "(0.5, 0.5)"); + EXPECT_EQ (vertexes.size (), size_t (4)); + if (vertexes.size () >= size_t (4)) { + EXPECT_EQ (vertexes [0]->to_string (), "(0, 0)"); + EXPECT_EQ (vertexes [1]->to_string (), "(0.2, 0.2)"); + EXPECT_EQ (vertexes [2]->to_string (), "(0.5, 0.5)"); + EXPECT_EQ (vertexes [3]->to_string (), "(1, 1)"); } vertexes = tris.find_vertexes_along_line (db::DPoint (1, 1), db::DPoint (0.25, 0.25)); - EXPECT_EQ (vertexes.size (), size_t (1)); - if (vertexes.size () >= size_t (1)) { - EXPECT_EQ (vertexes [0]->to_string (), "(0.5, 0.5)"); + EXPECT_EQ (vertexes.size (), size_t (2)); + if (vertexes.size () >= size_t (2)) { + EXPECT_EQ (vertexes [0]->to_string (), "(1, 1)"); + EXPECT_EQ (vertexes [1]->to_string (), "(0.5, 0.5)"); } } diff --git a/src/pex/pex/pexTriangulationRExtractor.cc b/src/pex/pex/pexTriangulationRExtractor.cc index d464e8fdb..cce1e4150 100644 --- a/src/pex/pex/pexTriangulationRExtractor.cc +++ b/src/pex/pex/pexTriangulationRExtractor.cc @@ -43,24 +43,74 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector< rnetwork.clear (); db::CplxTrans trans = db::CplxTrans (m_dbu) * db::ICplxTrans (db::Trans (db::Point () - polygon.box ().center ())); - auto inv_trans = trans.inverted (); - - // NOTE: currently we treat polygon ports and points where the location is the center of the bounding box - std::vector vp = vertex_ports; - vp.reserve (vertex_ports.size () + polygon_ports.size ()); - for (auto pp = polygon_ports.begin (); pp != polygon_ports.end (); ++pp) { - vp.push_back (pp->box ().center ()); - } - db::plc::Graph plc; db::plc::Triangulation tri (&plc); - tri.triangulate (polygon, vp, m_tri_param, trans); + std::unordered_map pp_vertexes; - // create a network node for each triangle node + if (polygon_ports.empty ()) { + + tri.triangulate (polygon, vertex_ports, m_tri_param, trans); + + } else { + + // Subtract the polygon ports from the original polygon and compute the intersection. + // Hence we have coincident edges that we can use to identify the nodes that are + // connected for the polygon ports + + db::Region org (polygon); + db::Region pp (polygon_ports.begin (), polygon_ports.end ()); + + db::Region residual_poly = org - pp; + + // We must not remove outside triangles yet, as we need them for "find_vertexes_along_line" + db::plc::TriangulationParameters param = m_tri_param; + param.remove_outside_triangles = false; + + tri.clear (); + + unsigned int id = 0; + for (auto v = vertex_ports.begin (); v != vertex_ports.end (); ++v) { + tri.insert_point (trans * *v)->set_is_precious (true, id++); + } + + for (auto p = polygon_ports.begin (); p != polygon_ports.end (); ++p) { + // create vertexes for the port polygon vertexes - this ensures we will find vertexes + // on the edges of the polygons - yet, they may be outside of the original polygon. + // In that case they will not be considered + for (auto e = p->begin_edge (); !e.at_end (); ++e) { + tri.insert_point (trans * (*e).p1 ())->set_is_precious (true, id); + } + } + + // perform the triangulation + + tri.create_constrained_delaunay (residual_poly, trans); + tri.refine (param); + + // identify the vertexes present for the polygon port -> store them inside pp_vertexes + + for (auto p = polygon_ports.begin (); p != polygon_ports.end (); ++p) { + for (auto e = p->begin_edge (); !e.at_end (); ++e) { + // NOTE: this currently only works if one of the end points is an actual + // vertex. + auto vport = tri.find_vertexes_along_line (trans * (*e).p1 (), trans * (*e).p2 ()); + for (auto v = vport.begin (); v != vport.end (); ++v) { + pp_vertexes.insert (std::make_pair (*v, p - polygon_ports.begin ())); + } + } + } + + tri.remove_outside_triangles (); + + } + + // Create a network node for each triangle node. std::unordered_map vertex2node; + std::unordered_set vports_present; + std::map pport_nodes; size_t internal_node_id = 0; @@ -73,28 +123,71 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector< continue; } - pex::RNode::node_type type = pex::RNode::Internal; - size_t port_index = 0; + pex::RNode *n = 0; - if (vertex->is_precious ()) { - size_t idx = vertex->id (); - if (idx >= vertex_ports.size ()) { - type = pex::RNode::PolygonPort; - port_index = size_t (idx) - vertex_ports.size (); + auto ipp = pp_vertexes.find (vertex); + if (ipp != pp_vertexes.end ()) { + + size_t port_index = ipp->second; + auto pn = pport_nodes.find (port_index); + if (pn != pport_nodes.end ()) { + n = pn->second; } else { - type = pex::RNode::VertexPort; - port_index = size_t (idx); + n = rnetwork.create_node (pex::RNode::PolygonPort, port_index); + pport_nodes.insert (std::make_pair (port_index, n)); + n->location = trans * polygon_ports [port_index].box (); } + + } else if (vertex->is_precious ()) { + + size_t port_index = size_t (vertex->id ()); + if (port_index < vertex_ports.size ()) { + n = rnetwork.create_node (pex::RNode::VertexPort, port_index); + n->location = db::DBox (*vertex, *vertex); + vports_present.insert (port_index); + } + } else { - port_index = internal_node_id++; + + n = rnetwork.create_node (pex::RNode::Internal, internal_node_id++); + n->location = db::DBox (*vertex, *vertex); + } - pex::RNode *n = rnetwork.create_node (type, port_index); - db::DPoint loc = *vertex; - n->location = db::DBox (loc, loc); + if (n) { + vertex2node.insert (std::make_pair (vertex, n)); + } - vertex2node.insert (std::make_pair (vertex, n)); + } + } + + // check for vertex ports not assigned to a node + // -> this may be an indication for a vertex port inside a polygon port + + for (size_t iv = 0; iv < vertex_ports.size (); ++iv) { + + if (vports_present.find (iv) != vports_present.end ()) { + continue; + } + + db::Point vp = vertex_ports [iv]; + + for (auto p = polygon_ports.begin (); p != polygon_ports.end (); ++p) { + + if (p->box ().contains (vp) && db::inside_poly_test (*p) (vp) >= 0) { + + auto ip = pport_nodes.find (p - polygon_ports.begin ()); + if (ip != pport_nodes.end ()) { + + // create a new vertex port and short it to the polygon port + auto n = rnetwork.create_node (pex::RNode::VertexPort, iv); + n->location = db::DBox (trans * vp, trans * vp); + rnetwork.create_element (pex::RElement::short_value (), n, ip->second); + + } + + } } } @@ -121,17 +214,22 @@ TriangulationRExtractor::create_conductances (const db::plc::Polygon &tri, const const db::plc::Vertex *p0 = tri.vertex (i + 1); const db::plc::Vertex *p1 = tri.vertex (i + 2); - double a = fabs (db::vprod (*pm1 - *p0, *p1 - *p0) * 0.5); - - double lm1 = (*p0 - *pm1).sq_length (); - double l0 = (*p1 - *p0).sq_length (); - double l1 = (*pm1 - *p1).sq_length (); - - double s = (l0 + l1 - lm1) / (8.0 * a); - auto i0 = vertex2node.find (p0); auto im1 = vertex2node.find (pm1); - rnetwork.create_element (s, i0->second, im1->second); + + if (i0->second != im1->second) { + + double a = fabs (db::vprod (*pm1 - *p0, *p1 - *p0) * 0.5); + + double lm1 = (*p0 - *pm1).sq_length (); + double l0 = (*p1 - *p0).sq_length (); + double l1 = (*pm1 - *p1).sq_length (); + + double s = (l0 + l1 - lm1) / (8.0 * a); + + rnetwork.create_element (s, i0->second, im1->second); + + } } } diff --git a/src/pex/unit_tests/pexTriangulationRExtractorTests.cc b/src/pex/unit_tests/pexTriangulationRExtractorTests.cc index 46609d1ec..de2074322 100644 --- a/src/pex/unit_tests/pexTriangulationRExtractorTests.cc +++ b/src/pex/unit_tests/pexTriangulationRExtractorTests.cc @@ -67,3 +67,191 @@ TEST(extraction) "R V1 V0 10.0938" ) } + +TEST(extraction_with_polygon_ports) +{ + db::Point contour[] = { + db::Point (0, 0), + db::Point (0, 100), + db::Point (1000, 100), + db::Point (1000, 0) + }; + + db::Polygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + + double dbu = 0.001; + + pex::RNetwork rn; + pex::TriangulationRExtractor rex (dbu); + + std::vector vertex_ports; + + std::vector polygon_ports; + polygon_ports.push_back (db::Polygon (db::Box (-100, 0, 0, 100))); + polygon_ports.push_back (db::Polygon (db::Box (1000, 0, 1100, 100))); + + rex.extract (poly, vertex_ports, polygon_ports, rn); + + EXPECT_EQ (rn.to_string (), + "R P1 P0 10" + ) +} + +TEST(extraction_with_polygon_ports_inside) +{ + db::Point contour[] = { + db::Point (-100, 0), + db::Point (-100, 100), + db::Point (1100, 100), + db::Point (1100, 0) + }; + + db::Polygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + + double dbu = 0.001; + + pex::RNetwork rn; + pex::TriangulationRExtractor rex (dbu); + + std::vector vertex_ports; + + std::vector polygon_ports; + polygon_ports.push_back (db::Polygon (db::Box (-100, 0, 0, 100))); + polygon_ports.push_back (db::Polygon (db::Box (1000, 0, 1100, 100))); + + rex.extract (poly, vertex_ports, polygon_ports, rn); + + EXPECT_EQ (rn.to_string (), + "R P1 P0 10" + ) +} + +TEST(extraction_split_by_ports) +{ + db::Point contour[] = { + db::Point (-100, 0), + db::Point (-100, 100), + db::Point (1100, 100), + db::Point (1100, 0) + }; + + db::Polygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + + double dbu = 0.001; + + pex::RNetwork rn; + pex::TriangulationRExtractor rex (dbu); + + std::vector vertex_ports; + + std::vector polygon_ports; + polygon_ports.push_back (db::Polygon (db::Box (-100, 0, 0, 100))); + polygon_ports.push_back (db::Polygon (db::Box (1100, 0, 1200, 100))); + polygon_ports.push_back (db::Polygon (db::Box (500, 0, 600, 100))); + + rex.extract (poly, vertex_ports, polygon_ports, rn); + + EXPECT_EQ (rn.to_string (), + "R P2 P0 5\n" + "R P1 P2 5" + ) +} + +TEST(extraction_split_by_butting_port) +{ + db::Point contour[] = { + db::Point (-100, 0), + db::Point (-100, 100), + db::Point (1100, 100), + db::Point (1100, 0) + }; + + db::Polygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + + double dbu = 0.001; + + pex::RNetwork rn; + pex::TriangulationRExtractor rex (dbu); + + std::vector vertex_ports; + + std::vector polygon_ports; + polygon_ports.push_back (db::Polygon (db::Box (-100, 0, 0, 100))); + polygon_ports.push_back (db::Polygon (db::Box (1100, 0, 1200, 100))); + polygon_ports.push_back (db::Polygon (db::Box (500, 100, 600, 200))); + + rex.extract (poly, vertex_ports, polygon_ports, rn); + + EXPECT_EQ (rn.to_string (), + "R P2 P0 4.84211\n" + "R P1 P2 4.84211\n" + "R P0 P1 281.111" + ) +} + +TEST(extraction_with_outside_polygon_port) +{ + db::Point contour[] = { + db::Point (-100, 0), + db::Point (-100, 100), + db::Point (1100, 100), + db::Point (1100, 0) + }; + + db::Polygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + + double dbu = 0.001; + + pex::RNetwork rn; + pex::TriangulationRExtractor rex (dbu); + + std::vector vertex_ports; + + std::vector polygon_ports; + polygon_ports.push_back (db::Polygon (db::Box (-100, 0, 0, 100))); + polygon_ports.push_back (db::Polygon (db::Box (1100, 0, 1200, 100))); + polygon_ports.push_back (db::Polygon (db::Box (500, 200, 600, 300))); + + rex.extract (poly, vertex_ports, polygon_ports, rn); + + EXPECT_EQ (rn.to_string (), + "R P1 P0 11" + ) +} + +TEST(extraction_with_polygon_ports_and_vertex_port_inside) +{ + db::Point contour[] = { + db::Point (-100, 0), + db::Point (-100, 100), + db::Point (1100, 100), + db::Point (1100, 0) + }; + + db::Polygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + + double dbu = 0.001; + + pex::RNetwork rn; + pex::TriangulationRExtractor rex (dbu); + + std::vector vertex_ports; + vertex_ports.push_back (db::Point (-50, 50)); + + std::vector polygon_ports; + polygon_ports.push_back (db::Polygon (db::Box (-100, 0, 0, 100))); + polygon_ports.push_back (db::Polygon (db::Box (1000, 0, 1100, 100))); + + rex.extract (poly, vertex_ports, polygon_ports, rn); + + EXPECT_EQ (rn.to_string (), + "R V0 P0 0\n" // shorted because V0 is inside P0 + "R P1 P0 10" + ) +} From 7f0b2d532de85dda05fc4ba88eeb3c9d688d6f2b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 20 Apr 2025 20:07:47 +0200 Subject: [PATCH 157/392] Bugfixes --- src/db/db/dbPLC.cc | 1 + src/pex/pex/pexSquareCountingRExtractor.cc | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/db/db/dbPLC.cc b/src/db/db/dbPLC.cc index 7a8be1455..7fbb26b44 100644 --- a/src/db/db/dbPLC.cc +++ b/src/db/db/dbPLC.cc @@ -443,6 +443,7 @@ Polygon::init () if (area > db::epsilon) { std::reverse (mp_v.begin (), mp_v.end ()); std::reverse (mp_e.begin (), mp_e.end ()); + std::rotate (mp_e.begin (), ++mp_e.begin (), mp_e.end ()); } // link the polygon to the edges diff --git a/src/pex/pex/pexSquareCountingRExtractor.cc b/src/pex/pex/pexSquareCountingRExtractor.cc index 5477abdb0..de6400491 100644 --- a/src/pex/pex/pexSquareCountingRExtractor.cc +++ b/src/pex/pex/pexSquareCountingRExtractor.cc @@ -264,8 +264,9 @@ SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector // 2. vertex ports for (size_t i = 0; i < plc_poly->internal_vertexes (); ++i) { - db::Point loc = inv_trans * *plc_poly->internal_vertex (i); - ports.push_back (std::make_pair (PortDefinition (pex::RNode::VertexPort, loc, i), (pex::RNode *) 0)); + auto v = plc_poly->internal_vertex (i); + db::Point loc = inv_trans * *v; + ports.push_back (std::make_pair (PortDefinition (pex::RNode::VertexPort, loc, v->id ()), (pex::RNode *) 0)); } // 3. polygon ports From 9bf03390de4a7aabbdf15ebc2e34e68ff0a954f8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 20 Apr 2025 21:23:01 +0200 Subject: [PATCH 158/392] More robust output for tests, some debugging --- src/db/db/gsiDeclDbTrans.cc | 4 +- src/pex/pex/pexRExtractor.cc | 20 +++-- src/pex/pex/pexTriangulationRExtractor.cc | 6 +- src/pex/unit_tests/pexRExtractorTests.cc | 14 ++-- .../pexSquareCountingRExtractorTests.cc | 8 +- .../pexTriangulationRExtractorTests.cc | 74 +++++++++++++++++-- 6 files changed, 97 insertions(+), 29 deletions(-) diff --git a/src/db/db/gsiDeclDbTrans.cc b/src/db/db/gsiDeclDbTrans.cc index f9c579986..27938b0d4 100644 --- a/src/db/db/gsiDeclDbTrans.cc +++ b/src/db/db/gsiDeclDbTrans.cc @@ -521,7 +521,7 @@ struct trans_defs method ("disp", (const vector_type &(C::*) () const) &C::disp, "@brief Gets to the displacement vector\n" "\n" - "Staring with version 0.25 the displacement type is a vector." + "Starting with version 0.25 the displacement type is a vector." ) + method ("rot", &C::rot, "@brief Gets the angle/mirror code\n" @@ -553,7 +553,7 @@ struct trans_defs "@param u The new displacement\n" "\n" "This method was introduced in version 0.20.\n" - "Staring with version 0.25 the displacement type is a vector." + "Starting with version 0.25 the displacement type is a vector." ) + method_ext ("mirror=", &set_mirror, arg ("m"), "@brief Sets the mirror flag\n" diff --git a/src/pex/pex/pexRExtractor.cc b/src/pex/pex/pexRExtractor.cc index db38fc948..02b5dbf59 100644 --- a/src/pex/pex/pexRExtractor.cc +++ b/src/pex/pex/pexRExtractor.cc @@ -52,19 +52,25 @@ RNode::to_string () const std::string RElement::to_string () const { - std::string res = "R "; + std::string na; if (a ()) { - res += a ()->to_string (); + na = a ()->to_string (); } else { - res += "(nil)"; + na = "(nil)"; } - res += " "; + + std::string nb; if (b ()) { - res += b ()->to_string (); + nb = b ()->to_string (); } else { - res += "(nil)"; + nb = "(nil)"; } - res += " "; + + if (nb < na) { + std::swap (na, nb); + } + + std::string res = "R " + na + " " + nb + " "; res += tl::sprintf ("%.6g", resistance ()); return res; } diff --git a/src/pex/pex/pexTriangulationRExtractor.cc b/src/pex/pex/pexTriangulationRExtractor.cc index cce1e4150..5a7a87047 100644 --- a/src/pex/pex/pexTriangulationRExtractor.cc +++ b/src/pex/pex/pexTriangulationRExtractor.cc @@ -42,6 +42,8 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector< { rnetwork.clear (); + tl::SelfTimer timer (tl::verbosity () >= m_tri_param.base_verbosity + 1, "Extracting resistor network from polygon (TriangulationRExtractor)"); + db::CplxTrans trans = db::CplxTrans (m_dbu) * db::ICplxTrans (db::Trans (db::Point () - polygon.box ().center ())); db::plc::Graph plc; @@ -55,6 +57,8 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector< } else { + tl::SelfTimer timer_tri (tl::verbosity () >= m_tri_param.base_verbosity + 11, "Triangulation step"); + // Subtract the polygon ports from the original polygon and compute the intersection. // Hence we have coincident edges that we can use to identify the nodes that are // connected for the polygon ports @@ -238,7 +242,7 @@ void TriangulationRExtractor::eliminate_all (RNetwork &rnetwork) { if (tl::verbosity () >= m_tri_param.base_verbosity + 10) { - tl::info << "Staring elimination with " << rnetwork.num_internal_nodes () << " internal nodes and " << rnetwork.num_elements () << " resistors"; + tl::info << "Starting elimination with " << rnetwork.num_internal_nodes () << " internal nodes and " << rnetwork.num_elements () << " resistors"; } unsigned int niter = 0; diff --git a/src/pex/unit_tests/pexRExtractorTests.cc b/src/pex/unit_tests/pexRExtractorTests.cc index 1c2de17b7..b0ace9056 100644 --- a/src/pex/unit_tests/pexRExtractorTests.cc +++ b/src/pex/unit_tests/pexRExtractorTests.cc @@ -97,7 +97,7 @@ TEST(network_simplify1) rn.create_element (1, n1, n3); EXPECT_EQ (rn.to_string (), - "R V1 $2 1\n" + "R $2 V1 1\n" "R $2 V3 0\n" "R V1 V3 1" ); @@ -126,7 +126,7 @@ TEST(network_simplify2) rn.create_element (1, n3, n5); EXPECT_EQ (rn.to_string (), - "R V1 $2 1\n" + "R $2 V1 1\n" "R $2 $3 0\n" "R $3 V4 1\n" "R $3 V5 1" @@ -135,9 +135,9 @@ TEST(network_simplify2) rn.simplify (); EXPECT_EQ (rn.to_string (), - "R V1 $2 1\n" - "R V4 $2 1\n" - "R V5 $2 1" + "R $2 V1 1\n" + "R $2 V4 1\n" + "R $2 V5 1" ); } @@ -156,7 +156,7 @@ TEST(network_simplify3) rn.create_element (1, n3, n4); EXPECT_EQ (rn.to_string (), - "R V1 $2 1\n" + "R $2 V1 1\n" "R $2 $3 0\n" "R $3 V4 1" ); @@ -185,7 +185,7 @@ TEST(network_simplify4) EXPECT_EQ (rn.to_string (), "R V1 V4 1\n" "R $2 V1 1\n" - "R V4 $3 1" + "R $3 V4 1" ); rn.simplify (); diff --git a/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc b/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc index 1e4d7e326..77af14a1e 100644 --- a/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc +++ b/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc @@ -98,9 +98,9 @@ TEST(basic) // Same network, but opposite order. $1 and $2 are shorted, hence can be swapped. EXPECT_EQ (rn.to_string (), - "R $3 $1 1\n" + "R $1 $3 1\n" "R $1 $2 0\n" - "R $2 $0 0.390865" + "R $0 $2 0.390865" ); } @@ -135,8 +135,8 @@ TEST(extraction) rex.extract (poly, vertex_ports, polygon_ports, rn); EXPECT_EQ (rn.to_string (), - "R V0 $0 0.0952381\n" + "R $0 V0 0.0952381\n" "R $0 V1 0.166667\n" - "R P0 $0 0.117647" + "R $0 P0 0.117647" ) } diff --git a/src/pex/unit_tests/pexTriangulationRExtractorTests.cc b/src/pex/unit_tests/pexTriangulationRExtractorTests.cc index de2074322..b2bfff50c 100644 --- a/src/pex/unit_tests/pexTriangulationRExtractorTests.cc +++ b/src/pex/unit_tests/pexTriangulationRExtractorTests.cc @@ -64,7 +64,7 @@ TEST(extraction) rex.extract (poly, vertex_ports, polygon_ports, rn); EXPECT_EQ (rn.to_string (), - "R V1 V0 10.0938" + "R V0 V1 10.0938" ) } @@ -94,7 +94,7 @@ TEST(extraction_with_polygon_ports) rex.extract (poly, vertex_ports, polygon_ports, rn); EXPECT_EQ (rn.to_string (), - "R P1 P0 10" + "R P0 P1 10" ) } @@ -124,7 +124,7 @@ TEST(extraction_with_polygon_ports_inside) rex.extract (poly, vertex_ports, polygon_ports, rn); EXPECT_EQ (rn.to_string (), - "R P1 P0 10" + "R P0 P1 10" ) } @@ -155,7 +155,7 @@ TEST(extraction_split_by_ports) rex.extract (poly, vertex_ports, polygon_ports, rn); EXPECT_EQ (rn.to_string (), - "R P2 P0 5\n" + "R P0 P2 5\n" "R P1 P2 5" ) } @@ -187,7 +187,7 @@ TEST(extraction_split_by_butting_port) rex.extract (poly, vertex_ports, polygon_ports, rn); EXPECT_EQ (rn.to_string (), - "R P2 P0 4.84211\n" + "R P0 P2 4.84211\n" "R P1 P2 4.84211\n" "R P0 P1 281.111" ) @@ -220,7 +220,7 @@ TEST(extraction_with_outside_polygon_port) rex.extract (poly, vertex_ports, polygon_ports, rn); EXPECT_EQ (rn.to_string (), - "R P1 P0 11" + "R P0 P1 11" ) } @@ -251,7 +251,65 @@ TEST(extraction_with_polygon_ports_and_vertex_port_inside) rex.extract (poly, vertex_ports, polygon_ports, rn); EXPECT_EQ (rn.to_string (), - "R V0 P0 0\n" // shorted because V0 is inside P0 - "R P1 P0 10" + "R P0 V0 0\n" // shorted because V0 is inside P0 + "R P0 P1 10" ) } + +static db::Polygon ellipse (const db::Box &box, int npoints) +{ + npoints = std::max (3, std::min (10000000, npoints)); + + std::vector pts; + pts.reserve (npoints); + + double da = M_PI * 2.0 / npoints; + for (int i = 0; i < npoints; ++i) { + double x = box.center ().x () - box.width () * 0.5 * cos (da * i); + double y = box.center ().y () + box.height () * 0.5 * sin (da * i); + pts.push_back (db::Point (x, y)); + } + + db::Polygon c; + c.assign_hull (pts.begin (), pts.end (), false); + return c; +} + +TEST(extraction_analytic_disc) +{ + db::Coord r1 = 2000; + db::Coord r2 = 10000; + db::Coord r2pin = 10000 + 1000; + + db::Polygon outer = ellipse (db::Box (-r2pin, -r2pin, r2pin, r2pin), 64); + db::Polygon disc = ellipse (db::Box (-r2, -r2, r2, r2), 64); + db::Polygon inner = ellipse (db::Box (-r1, -r1, r1, r1), 64); + + db::Polygon outer_port = *(db::Region (outer) - db::Region (disc)).nth (0); + + double dbu = 0.001; + + pex::RNetwork rn; + pex::TriangulationRExtractor rex (dbu); + + std::vector vertex_ports; + + std::vector polygon_ports; + polygon_ports.push_back (inner); + polygon_ports.push_back (outer_port); + + rex.extract (disc, vertex_ports, polygon_ports, rn); + + EXPECT_EQ (rn.to_string (), + "R P0 P1 0.245379" // theoretical: 1/(2*PI)*log(r2/r1) = 0.25615 with r2=10000, r1=2000 + ) + + rex.triangulation_parameters ().max_area = 100000 * dbu * dbu; + + rex.extract (disc, vertex_ports, polygon_ports, rn); + + EXPECT_EQ (rn.to_string (), + "R P0 P1 0.255614" // theoretical: 1/(2*PI)*log(r2/r1) = 0.25615 with r2=10000, r1=2000 + ) +} + From 93e10d1d720e263ceff7cc45d972939b4535bd77 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 20 Apr 2025 21:52:44 +0200 Subject: [PATCH 159/392] Doc and small fixes --- src/pex/pex/pexRExtractor.cc | 14 +- src/pex/pex/pexRExtractor.h | 176 +++++++++++++++++++-- src/pex/pex/pexSquareCountingRExtractor.cc | 2 +- src/pex/pex/pexSquareCountingRExtractor.h | 32 +++- src/pex/pex/pexTriangulationRExtractor.cc | 4 +- src/pex/pex/pexTriangulationRExtractor.h | 33 +++- 6 files changed, 240 insertions(+), 21 deletions(-) diff --git a/src/pex/pex/pexRExtractor.cc b/src/pex/pex/pexRExtractor.cc index 02b5dbf59..11c36c771 100644 --- a/src/pex/pex/pexRExtractor.cc +++ b/src/pex/pex/pexRExtractor.cc @@ -138,7 +138,7 @@ RNetwork::create_node (RNode::node_type type, unsigned int port_index) } RElement * -RNetwork::create_element (double conductivity, RNode *a, RNode *b) +RNetwork::create_element (double conductance, RNode *a, RNode *b) { std::pair key (a, b); if (size_t (b) < size_t (a)) { @@ -148,17 +148,17 @@ RNetwork::create_element (double conductivity, RNode *a, RNode *b) auto i = m_elements_by_nodes.find (key); if (i != m_elements_by_nodes.end ()) { - if (conductivity == pex::RElement::short_value () || i->second->conductivity == pex::RElement::short_value ()) { - i->second->conductivity = pex::RElement::short_value (); + if (conductance == pex::RElement::short_value () || i->second->conductance == pex::RElement::short_value ()) { + i->second->conductance = pex::RElement::short_value (); } else { - i->second->conductivity += conductivity; + i->second->conductance += conductance; } return i->second; } else { - RElement *element = new RElement (this, conductivity, a, b); + RElement *element = new RElement (this, conductance, a, b); m_elements.push_back (element); m_elements_by_nodes.insert (std::make_pair (key, element)); @@ -204,7 +204,7 @@ RNetwork::join_nodes (RNode *a, RNode *b) for (auto e = b->elements ().begin (); e != b->elements ().end (); ++e) { RNode *on = const_cast ((*e)->other (b)); if (on != a) { - create_element ((*e)->conductivity, on, a); + create_element ((*e)->conductance, on, a); } } @@ -225,7 +225,7 @@ RNetwork::simplify () tl::equivalence_clusters clusters; for (auto e = m_elements.begin (); e != m_elements.end (); ++e) { - if (e->conductivity == pex::RElement::short_value () && (e->a ()->type == pex::RNode::Internal || e->b ()->type == pex::RNode::Internal)) { + if (e->conductance == pex::RElement::short_value () && (e->a ()->type == pex::RNode::Internal || e->b ()->type == pex::RNode::Internal)) { clusters.same (e->a (), e->b ()); } } diff --git a/src/pex/pex/pexRExtractor.h b/src/pex/pex/pexRExtractor.h index 11e05e00d..0e7a2578e 100644 --- a/src/pex/pex/pexRExtractor.h +++ b/src/pex/pex/pexRExtractor.h @@ -40,25 +40,57 @@ class RElement; class RNode; class RNetwork; +/** + * @brief Represents a node in the R graph + * + * A node connects to multiple elements (resistors). + * Every element has two nodes. The nodes and elements form + * a graph. + * + * RNode object cannot be created directly. Use "create_node" + * from RNetwork. + */ struct PEX_PUBLIC RNode : public tl::list_node { public: + /** + * @brief The type of the node + */ enum node_type { - Internal, - VertexPort, - PolygonPort + Internal, // an internal node, not related to a port + VertexPort, // a node related to a vertex port + PolygonPort // a node related to a polygon port }; + /** + * @brief The node type + */ node_type type; + + /** + * @brief The location + extension of the node + */ db::DBox location; + + /** + * @brief An index locating the node in the vertex or polygon port lists + * + * For internal nodes, the index is a unique numbers. + */ unsigned int port_index; + /** + * @brief Gets the R elements connected to this node + */ const std::list &elements () const { return m_elements; } + /** + * @brief Returns a string representation of the node + */ std::string to_string () const; protected: @@ -80,14 +112,35 @@ private: mutable std::list m_elements; }; +/** + * @brief Represents an R element in the graph (an edge) + * + * An element has two nodes that form the ends of the edge and + * a conductance value (given in Siemens). + * + * The value can be RElement::short_value() indicating + * "infinite" conductance (a short). + * + * RElement objects cannot be created directly. Use "create_element" + * from RNetwork. + */ struct PEX_PUBLIC RElement : public tl::list_node { - double conductivity; + /** + * @brief The conductance value + */ + double conductance; + /** + * @brief The nodes the resistor connects + */ const RNode *a () const { return mp_a; } const RNode *b () const { return mp_b; } + /** + * @brief Gets the other node for n + */ const RNode *other (const RNode *n) const { if (mp_a == n) { @@ -98,16 +151,27 @@ struct PEX_PUBLIC RElement tl_assert (false); } + /** + * @brief Represents the conductance value for a short + */ static double short_value () { return std::numeric_limits::infinity (); } + /** + * @brief Gets the resistance value + * + * The resistance value is the inverse of the conducance. + */ double resistance () const { - return conductivity == short_value () ? 0.0 : 1.0 / conductivity; + return conductance == short_value () ? 0.0 : 1.0 / conductance; } + /** + * @brief Returns a string representation of the element + */ std::string to_string () const; protected: @@ -115,7 +179,7 @@ protected: friend class tl::list_impl; RElement (RNetwork *network, double _conductivity, const RNode *a, const RNode *b) - : conductivity (_conductivity), mp_network (network), mp_a (a), mp_b (b) + : conductance (_conductivity), mp_network (network), mp_a (a), mp_b (b) { } ~RElement () @@ -138,6 +202,9 @@ private: RElement &operator= (const RElement &other); }; +/** + * @brief Represents a R network (a graph of RNode and RElement) + */ class PEX_PUBLIC RNetwork : public tl::Object { @@ -147,33 +214,95 @@ public: typedef tl::list element_list; typedef element_list::const_iterator element_iterator; + /** + * @brief Constructor + */ RNetwork (); + + /** + * @brief Destructor + */ ~RNetwork (); + /** + * @brief Creates a node with the given type and port index + * + * If the node type is Internal, a new node is created always. + * If the node type is VertexPort or PolygonPort, an existing + * node is returned if one way created with the same type + * or port index already. This avoids creating duplicates + * for the same port. + */ RNode *create_node (RNode::node_type type, unsigned int port_index); - RElement *create_element (double conductivity, RNode *a, RNode *b); + + /** + * @brief Creates a new element between the given nodes + * + * If an element already exists between the specified nodes, the + * given value is added to the existing element and the existing + * object is returned. + */ + RElement *create_element (double conductance, RNode *a, RNode *b); + + /** + * @brief Removes the given element + * + * Removing the element will also remove any orphan nodes + * at the ends if they are of type Internal. + */ void remove_element (RElement *element); + + /** + * @brief Removes the node and the attached elements. + * + * Only nodes of type Internal can be removed. + */ void remove_node (RNode *node); + + /** + * @brief Clears the network + */ void clear (); + + /** + * @brief Simplifies the network + * + * This will: + * - Join serial resistors if connected by an internal node + * - Remove shorts and join the nodes, if one of them is + * an internal node. The non-internal node will persist. + * - Remove "dangling" resistors if the dangling node is + * an internal one + */ void simplify (); - std::string to_string () const; - + /** + * @brief Iterate the nodes (begin) + */ node_iterator begin_nodes () const { return m_nodes.begin (); } + /** + * @brief Iterate the nodes (end) + */ node_iterator end_nodes () const { return m_nodes.end (); } + /** + * @brief Gets the number of nodes + */ size_t num_nodes () const { return m_nodes.size (); } + /** + * @brief Gets the number of internal nodes + */ size_t num_internal_nodes () const { size_t count = 0; @@ -185,21 +314,35 @@ public: return count; } + /** + * @brief Iterate the elements (begin) + */ element_iterator begin_elements () const { return m_elements.begin (); } + /** + * @brief Iterate the elements (end) + */ element_iterator end_elements () const { return m_elements.end (); } + /** + * @brief Gets the number of elements + */ size_t num_elements () const { return m_elements.size (); } + /** + * @brief Returns a string representation of the graph + */ + std::string to_string () const; + private: node_list m_nodes; element_list m_elements; @@ -225,9 +368,24 @@ private: class PEX_PUBLIC RExtractor { public: + /** + * @brief Constructor + */ RExtractor (); + + /** + * @brief Destructor + */ virtual ~RExtractor (); + /** + * @brief Extracts the resistance network from the given polygon and ports + * + * The ports define specific locations where to connect to the resistance network. + * The network will contain corresponding nodes with the VertexPort for vertex ports + * and PolygonPort for polygon port. The node index is the index in the respective + * lists. + */ virtual void extract (const db::Polygon &polygon, const std::vector &vertex_ports, const std::vector &polygon_ports, RNetwork &rnetwork) = 0; }; diff --git a/src/pex/pex/pexSquareCountingRExtractor.cc b/src/pex/pex/pexSquareCountingRExtractor.cc index de6400491..97a96c1d1 100644 --- a/src/pex/pex/pexSquareCountingRExtractor.cc +++ b/src/pex/pex/pexSquareCountingRExtractor.cc @@ -165,7 +165,6 @@ SquareCountingRExtractor::do_extract (const db::Polygon &db_poly, const std::vec // @@@ TODO: multiply with sheet rho! // @@@ TODO: width dependency if (r == 0) { - // @@@ TODO: join nodes later! rnetwork.create_element (pex::RElement::short_value (), pl->second, pl_next->second); } else { rnetwork.create_element (r, pl->second, pl_next->second); @@ -293,6 +292,7 @@ SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector } + rnetwork.simplify (); } } diff --git a/src/pex/pex/pexSquareCountingRExtractor.h b/src/pex/pex/pexSquareCountingRExtractor.h index abacc2dfa..9ceb99d46 100644 --- a/src/pex/pex/pexSquareCountingRExtractor.h +++ b/src/pex/pex/pexSquareCountingRExtractor.h @@ -31,28 +31,58 @@ namespace pex { -// @@@ doc +/** + * @brief The Square Counting R Extractor + * + * The idea of that extractor is to first decompose the polygon into + * convex parts. Each convex part is taken as "thin" and the current + * flow being parallel and homogeneous to the long axis. + * + * Internal ports are created between the partial polygons where + * they touch. + * + * The ports are considered point-like (polygon ports are replaced + * by points in their bounding box centers) and inject current + * at their specific position only. The resistance is accumulated + * between ports by integrating the squares (length along + * the long axis / width). + */ class PEX_PUBLIC SquareCountingRExtractor : public RExtractor { public: + /** + * @brief The constructor + */ SquareCountingRExtractor (double dbu); + /** + * @brief Gets the decomposition parameters + */ db::plc::ConvexDecompositionParameters &decomposition_parameters () { return m_decomp_param; } + /** + * @brief Sets the database unit + */ void set_dbu (double dbu) { m_dbu = dbu; } + /** + * @brief Gets the database unit + */ double dbu () const { return m_dbu; } + /** + * @brief Implementation of the extraction function + */ virtual void extract (const db::Polygon &polygon, const std::vector &vertex_ports, const std::vector &polygon_ports, RNetwork &rnetwork); protected: diff --git a/src/pex/pex/pexTriangulationRExtractor.cc b/src/pex/pex/pexTriangulationRExtractor.cc index 5a7a87047..fabc3789e 100644 --- a/src/pex/pex/pexTriangulationRExtractor.cc +++ b/src/pex/pex/pexTriangulationRExtractor.cc @@ -301,7 +301,7 @@ TriangulationRExtractor::eliminate_node (pex::RNode *node, RNetwork &rnetwork) { double s_sum = 0.0; for (auto e = node->elements ().begin (); e != node->elements ().end (); ++e) { - s_sum += (*e)->conductivity; + s_sum += (*e)->conductance; } if (fabs (s_sum) > 1e-10) { @@ -311,7 +311,7 @@ TriangulationRExtractor::eliminate_node (pex::RNode *node, RNetwork &rnetwork) for ( ; ee != node->elements ().end (); ++ee) { pex::RNode *n1 = const_cast ((*e)->other (node)); pex::RNode *n2 = const_cast ((*ee)->other (node)); - double c = (*e)->conductivity * (*ee)->conductivity / s_sum; + double c = (*e)->conductance * (*ee)->conductance / s_sum; rnetwork.create_element (c, n1, n2); } } diff --git a/src/pex/pex/pexTriangulationRExtractor.h b/src/pex/pex/pexTriangulationRExtractor.h index 536d90f45..4215d0180 100644 --- a/src/pex/pex/pexTriangulationRExtractor.h +++ b/src/pex/pex/pexTriangulationRExtractor.h @@ -31,28 +31,59 @@ namespace pex { -// @@@ doc +/** + * @brief An R extractor based on a triangulation of the resistor area + * + * This resistor extractor starts with a triangulation of the + * polygon area and substitutes each triangle by a 3-resistor network. + * + * After this, it will eliminate nodes where possible. + * + * This extractor delivers a resistor matrix (there is a resistor + * between every specified port). + * + * Polygon ports are considered to be perfectly conductive and cover + * their given area, shorting all nodes at their boundary. + * + * This extractor delivers higher quality results than the square + * counting extractor, but is slower in general. + */ class PEX_PUBLIC TriangulationRExtractor : public RExtractor { public: + /** + * @brief The constructor + */ TriangulationRExtractor (double dbu); + /** + * @brief Gets the triangulation parameters + */ db::plc::TriangulationParameters &triangulation_parameters () { return m_tri_param; } + /** + * @brief Sets the database unit + */ void set_dbu (double dbu) { m_dbu = dbu; } + /** + * @brief Gets the database unit + */ double dbu () const { return m_dbu; } + /** + * @brief Implementation of the extraction function + */ virtual void extract (const db::Polygon &polygon, const std::vector &vertex_ports, const std::vector &polygon_ports, RNetwork &rnetwork); private: From 0e99ebc056279b412c022b6662f960333d8003c3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 20 Apr 2025 22:28:55 +0200 Subject: [PATCH 160/392] Another test case, triangulation extractor behaves strange. --- src/pex/pex/pexSquareCountingRExtractor.cc | 2 +- src/pex/pex/pexTriangulationRExtractor.cc | 2 + .../pexSquareCountingRExtractorTests.cc | 56 +++++++++++++++++-- .../pexTriangulationRExtractorTests.cc | 48 ++++++++++++++++ 4 files changed, 102 insertions(+), 6 deletions(-) diff --git a/src/pex/pex/pexSquareCountingRExtractor.cc b/src/pex/pex/pexSquareCountingRExtractor.cc index 97a96c1d1..0ad1bfa27 100644 --- a/src/pex/pex/pexSquareCountingRExtractor.cc +++ b/src/pex/pex/pexSquareCountingRExtractor.cc @@ -167,7 +167,7 @@ SquareCountingRExtractor::do_extract (const db::Polygon &db_poly, const std::vec if (r == 0) { rnetwork.create_element (pex::RElement::short_value (), pl->second, pl_next->second); } else { - rnetwork.create_element (r, pl->second, pl_next->second); + rnetwork.create_element (1.0 / r, pl->second, pl_next->second); } } diff --git a/src/pex/pex/pexTriangulationRExtractor.cc b/src/pex/pex/pexTriangulationRExtractor.cc index fabc3789e..e52a40f88 100644 --- a/src/pex/pex/pexTriangulationRExtractor.cc +++ b/src/pex/pex/pexTriangulationRExtractor.cc @@ -55,6 +55,8 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector< tri.triangulate (polygon, vertex_ports, m_tri_param, trans); + plc.dump ("debug.gds"); + } else { tl::SelfTimer timer_tri (tl::verbosity () >= m_tri_param.base_verbosity + 11, "Triangulation step"); diff --git a/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc b/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc index 77af14a1e..3da3d7b58 100644 --- a/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc +++ b/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc @@ -75,7 +75,7 @@ TEST(basic) rex.do_extract (poly, ports, rn); EXPECT_EQ (rn.to_string (), - "R $0 $1 0.390865\n" // w ramp w=100 to 1000 over x=0 to 1000 (squares = (x2-x1)/(w2-w1)*log(w2/w1) by integration) + "R $0 $1 2.55843\n" // w ramp w=100 to 1000 over x=0 to 1000 (squares = (x2-x1)/(w2-w1)*log(w2/w1) by integration) "R $1 $2 0\n" // transition from y=50 to y=500 parallel to current direction "R $2 $3 1" // 1 square between x=1000 and 2000 (w=1000) ); @@ -100,7 +100,7 @@ TEST(basic) EXPECT_EQ (rn.to_string (), "R $1 $3 1\n" "R $1 $2 0\n" - "R $0 $2 0.390865" + "R $0 $2 2.55843" ); } @@ -135,8 +135,54 @@ TEST(extraction) rex.extract (poly, vertex_ports, polygon_ports, rn); EXPECT_EQ (rn.to_string (), - "R $0 V0 0.0952381\n" - "R $0 V1 0.166667\n" - "R $0 P0 0.117647" + "R $0 V0 10.5\n" + "R $0 V1 6\n" + "R $0 P0 8.5" + ) +} + +TEST(extraction_meander) +{ + db::Point contour[] = { + db::Point (0, 0), + db::Point (0, 1000), + db::Point (1600, 1000), + db::Point (1600, 600), + db::Point (2000, 600), + db::Point (2000, 1000), + db::Point (3600, 1000), + db::Point (3600, 600), + db::Point (4000, 600), + db::Point (4000, 1000), + db::Point (4600, 1000), + db::Point (4600, 0), + db::Point (3000, 0), + db::Point (3000, 400), + db::Point (2600, 400), + db::Point (2600, 0), + db::Point (1000, 0), + db::Point (1000, 400), + db::Point (600, 400), + db::Point (600, 0) + }; + + db::Polygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + + double dbu = 0.001; + + pex::RNetwork rn; + pex::SquareCountingRExtractor rex (dbu); + + std::vector vertex_ports; + vertex_ports.push_back (db::Point (300, 0)); // V0 + vertex_ports.push_back (db::Point (4300, 1000)); // V1 + + std::vector polygon_ports; + + rex.extract (poly, vertex_ports, polygon_ports, rn); + + EXPECT_EQ (rn.to_string (), + "R V0 V1 10.0544" // that is pretty much the length of the center line / width :) ) } diff --git a/src/pex/unit_tests/pexTriangulationRExtractorTests.cc b/src/pex/unit_tests/pexTriangulationRExtractorTests.cc index b2bfff50c..09f1fce8f 100644 --- a/src/pex/unit_tests/pexTriangulationRExtractorTests.cc +++ b/src/pex/unit_tests/pexTriangulationRExtractorTests.cc @@ -313,3 +313,51 @@ TEST(extraction_analytic_disc) ) } +TEST(extraction_meander) +{ + db::Point contour[] = { + db::Point (0, 0), + db::Point (0, 1000), + db::Point (1600, 1000), + db::Point (1600, 600), + db::Point (2000, 600), + db::Point (2000, 1000), + db::Point (3600, 1000), + db::Point (3600, 600), + db::Point (4000, 600), + db::Point (4000, 1000), + db::Point (4600, 1000), + db::Point (4600, 0), + db::Point (3000, 0), + db::Point (3000, 400), + db::Point (2600, 400), + db::Point (2600, 0), + db::Point (1000, 0), + db::Point (1000, 400), + db::Point (600, 400), + db::Point (600, 0) + }; + + db::Polygon poly; + poly.assign_hull (contour + 0, contour + sizeof (contour) / sizeof (contour[0])); + + double dbu = 0.001; + + pex::RNetwork rn; + pex::TriangulationRExtractor rex (dbu); + rex.triangulation_parameters ().max_area = 10000 * dbu * dbu; + rex.triangulation_parameters ().min_b = 0.3; + + std::vector vertex_ports; + vertex_ports.push_back (db::Point (300, 0)); // V0 + vertex_ports.push_back (db::Point (4300, 1000)); // V1 + + std::vector polygon_ports; + + rex.extract (poly, vertex_ports, polygon_ports, rn); + + EXPECT_EQ (rn.to_string (), + "R V0 V1 8.75751" // what is the "real" value? + ) +} + From 8ece7bcce1cd42f6e41455b0a74d9dd3ce5365f0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 22 Apr 2025 22:37:23 +0200 Subject: [PATCH 161/392] Activate pex module, GSI bindings --- src/buddies/src/bd/bd.pro | 6 +- src/buddies/src/bd/strmrun.cc | 1 + src/klayout_main/klayout_main/klayout.cc | 3 +- src/pex/pex/gsiDeclRExtractor.cc | 451 ++++++++++++++++++++++- src/pex/pex/pexRExtractor.h | 16 + src/rba/unit_tests/rbaTests.cc | 1 + src/unit_tests/unit_test_main.cc | 1 + testdata/ruby/pexTests.rb | 127 +++++++ 8 files changed, 599 insertions(+), 7 deletions(-) create mode 100644 testdata/ruby/pexTests.rb diff --git a/src/buddies/src/bd/bd.pro b/src/buddies/src/bd/bd.pro index ef71f239e..2deeb870f 100644 --- a/src/buddies/src/bd/bd.pro +++ b/src/buddies/src/bd/bd.pro @@ -32,9 +32,9 @@ HEADERS = \ RESOURCES = \ -INCLUDEPATH += $$TL_INC $$GSI_INC $$VERSION_INC $$DB_INC $$LIB_INC $$RDB_INC $$LYM_INC -DEPENDPATH += $$TL_INC $$GSI_INC $$VERSION_INC $$DB_INC $$LIB_INC $$RDB_INC $$LYM_INC -LIBS += -L$$DESTDIR -lklayout_tl -lklayout_db -lklayout_gsi -lklayout_lib -lklayout_rdb -lklayout_lym +INCLUDEPATH += $$TL_INC $$GSI_INC $$VERSION_INC $$DB_INC $$LIB_INC $$RDB_INC $$PEX_INC $$LYM_INC +DEPENDPATH += $$TL_INC $$GSI_INC $$VERSION_INC $$DB_INC $$LIB_INC $$RDB_INC $$PEX_INC $$LYM_INC +LIBS += -L$$DESTDIR -lklayout_tl -lklayout_db -lklayout_gsi -lklayout_lib -lklayout_rdb -lklayout_pex -lklayout_lym INCLUDEPATH += $$RBA_INC DEPENDPATH += $$RBA_INC diff --git a/src/buddies/src/bd/strmrun.cc b/src/buddies/src/bd/strmrun.cc index fa15cf018..8f5faf784 100644 --- a/src/buddies/src/bd/strmrun.cc +++ b/src/buddies/src/bd/strmrun.cc @@ -34,6 +34,7 @@ #include "gsiExpression.h" #include "libForceLink.h" #include "rdbForceLink.h" +#include "pexForceLink.h" #include "lymMacro.h" #include "lymMacroCollection.h" diff --git a/src/klayout_main/klayout_main/klayout.cc b/src/klayout_main/klayout_main/klayout.cc index db01f3c52..5f0ebaac8 100644 --- a/src/klayout_main/klayout_main/klayout.cc +++ b/src/klayout_main/klayout_main/klayout.cc @@ -38,8 +38,9 @@ #include "version.h" -// required to force linking of the "ext" and "lib" module +// required to force linking of the "lib" and other modules #include "libForceLink.h" +#include "pexForceLink.h" #include "antForceLink.h" #include "imgForceLink.h" #include "docForceLink.h" diff --git a/src/pex/pex/gsiDeclRExtractor.cc b/src/pex/pex/gsiDeclRExtractor.cc index f74e8e657..626575092 100644 --- a/src/pex/pex/gsiDeclRExtractor.cc +++ b/src/pex/pex/gsiDeclRExtractor.cc @@ -22,22 +22,467 @@ #include "gsiDecl.h" +#include "pexRExtractor.h" #include "pexSquareCountingRExtractor.h" +#include "pexTriangulationRExtractor.h" +#include "gsiEnums.h" namespace gsi { -// @@@ +class RNode +{ +public: + ~RNode () { } + + pex::RNode::node_type type () const { return checked_pointer ()->type; } + db::DBox location () const { return checked_pointer ()->location; } + unsigned int port_index () const { return checked_pointer ()->port_index; } + std::string to_string () const { return checked_pointer ()->to_string (); } + + size_t obj_id () const + { + return size_t (mp_ptr); + } + + static RNode *make_node_object (const pex::RNode *node) + { + return new RNode (node); + } + + const pex::RNode *checked_pointer () const + { + if (! mp_graph.get ()) { + throw tl::Exception (tl::to_string (tr ("Network graph has been destroyed - RNode object no longer is valid"))); + } + return mp_ptr; + } + + pex::RNode *checked_pointer () + { + if (! mp_graph.get ()) { + throw tl::Exception (tl::to_string (tr ("Network graph has been destroyed - RNode object no longer is valid"))); + } + return const_cast (mp_ptr); + } + +private: + tl::weak_ptr mp_graph; + const pex::RNode *mp_ptr; + + RNode (const pex::RNode *node) + : mp_graph (node->graph ()), + mp_ptr (node) + { + // .. nothing yet .. + } +}; + +class RElement +{ +public: + ~RElement () { } + + double conductance () const { return checked_pointer ()->conductance; } + double resistance () const { return checked_pointer ()->resistance (); } + + RNode *a () const { return RNode::make_node_object (checked_pointer ()->a ()); } + RNode *b () const { return RNode::make_node_object (checked_pointer ()->b ()); } + + std::string to_string () const { return checked_pointer ()->to_string (); } + + size_t obj_id () const + { + return size_t (mp_ptr); + } + + static RElement *make_element_object (const pex::RElement *element) + { + return new RElement (element); + } + + const pex::RElement *checked_pointer () const + { + if (! mp_graph.get ()) { + throw tl::Exception (tl::to_string (tr ("Network graph has been destroyed - RElement object no longer is valid"))); + } + return mp_ptr; + } + + pex::RElement *checked_pointer () + { + if (! mp_graph.get ()) { + throw tl::Exception (tl::to_string (tr ("Network graph has been destroyed - RElement object no longer is valid"))); + } + return const_cast (mp_ptr); + } + +private: + tl::weak_ptr mp_graph; + const pex::RElement *mp_ptr; + + RElement (const pex::RElement *node) + : mp_graph (node->graph ()), + mp_ptr (node) + { + // .. nothing yet .. + } +}; + +class RElementIterator +{ +public: + typedef std::list::const_iterator basic_iter; + typedef std::forward_iterator_tag iterator_category; + typedef RElement *value_type; + typedef RElement *reference; + typedef void pointer; + typedef void difference_type; + + RElementIterator (basic_iter it) + : m_it (it) + { } + + bool operator== (const RElementIterator &it) const { return m_it == it.m_it; } + void operator++ () { ++m_it; } + + RElement *operator* () const + { + return RElement::make_element_object (*m_it); + } + +private: + basic_iter m_it; +}; + +static RElementIterator begin_node_elements (RNode *node) +{ + return RElementIterator (node->checked_pointer ()->elements ().begin ()); +} + +static RElementIterator end_network_elements (RNode *node) +{ + return RElementIterator (node->checked_pointer ()->elements ().end ()); +} + +gsi::Enum decl_NodeType ("pex", "RNodeType", + gsi::enum_const ("Internal", pex::RNode::Internal, + "@brief Specifies an internal node in a R network\n" + "Internal nodes are generated during the R extraction process. " + "The port index of such a node is an arbitrary index." + ) + + gsi::enum_const ("VertexPort", pex::RNode::VertexPort, + "@brief Specifies a vertex port node in a R network\n" + "Vertex port nodes are generated for vertex ports in \\RExtractor#extract, see 'vertex_ports' argument. " + "The port index of such a node refers to the position in that list." + ) + + gsi::enum_const ("PolygonPort", pex::RNode::PolygonPort, + "@brief Specifies a polygon port node in a R network\n" + "Polygon port nodes are generated for polygon ports in \\RExtractor#extract, see 'polygon_ports' argument. " + "The port index of such a node refers to the position in that list." + ), + "@brief This class represents the node type for RNode.\n" + "\n" + "This class has been introduced in version 0.30.1" +); + +Class decl_RNode ("pex", "RNode", + gsi::method ("object_id", &RNode::obj_id, + "@brief Returns an ID representing the actual object\n" + "For every call, a new instance of this object is created, while multiple " + "ones may represent the same internal object. The 'object_id' is a ID that " + "indicates the internal object. Same object_id means same node." + ) + + gsi::method ("to_s", &RNode::to_string, + "@brief Returns a string representation of this object\n" + ) + + gsi::iterator_ext ("each_element", gsi::return_new_object (), &begin_node_elements, &end_network_elements, + "@brief Iterates the \\RElement objects attached to the node\n" + ) + + gsi::method ("type", &RNode::type, + "@brief Gets the type attribute of the node\n" + ) + + gsi::method ("location", &RNode::location, + "@brief Gets the location attribute of the node\n" + "The location defined the original position of the node" + ) + + gsi::method ("port_index", &RNode::port_index, + "@brief Gets the port index of the node\n" + "The port index associates a node with a original port definition." + ), + "@brief Represents a node in a R network graph\n" + "See \\RNetwork for a description of this object\n" + "\n" + "This class has been introduced in version 0.30.1" +); + +// Inject the RNode::node_type declarations into RNode +gsi::ClassExt inject_NodeType_in_RNode (decl_NodeType.defs ()); + +Class decl_RElement ("pex", "RElement", + gsi::method ("object_id", &RElement::obj_id, + "@brief Returns an ID representing the actual object\n" + "For every call, a new instance of this object is created, while multiple " + "ones may represent the same internal object. The 'object_id' is a ID that " + "indicates the internal object. Same object_id means same element." + ) + + gsi::method ("to_s", &RElement::to_string, + "@brief Returns a string representation of this object\n" + ) + + gsi::method ("resistance", &RElement::resistance, + "@brief Gets the resistance value of the object\n" + ) + + gsi::factory ("a", &RElement::a, + "@brief Gets the first node the element connects\n" + ) + + gsi::factory ("b", &RElement::b, + "@brief Gets the second node the element connects\n" + ), + "@brief Represents an edge (also called element) in a R network graph\n" + "See \\RNetwork for a description of this object" + "\n" + "This class has been introduced in version 0.30.1" +); + +static RNode *create_node (pex::RNetwork *network, pex::RNode::node_type type, unsigned int port_index) +{ + return RNode::make_node_object (network->create_node (type, port_index)); +} + +static RElement *create_element (pex::RNetwork *network, double r, RNode *a, RNode *b) +{ + double s = fabs (r) < 1e-10 ? pex::RElement::short_value () : 1.0 / r; + return RElement::make_element_object (network->create_element (s, a->checked_pointer (), b->checked_pointer ())); +} + +static void remove_element (pex::RNetwork *network, RElement *element) +{ + network->remove_element (element->checked_pointer ()); +} + +static void remove_node (pex::RNetwork *network, RNode *node) +{ + network->remove_node (node->checked_pointer ()); +} + +class NetworkElementIterator +{ +public: + typedef pex::RNetwork::element_iterator basic_iter; + typedef std::forward_iterator_tag iterator_category; + typedef RElement *value_type; + typedef RElement *reference; + typedef void pointer; + typedef void difference_type; + + NetworkElementIterator (basic_iter it) + : m_it (it) + { } + + bool operator== (const NetworkElementIterator &it) const { return m_it == it.m_it; } + void operator++ () { ++m_it; } + + RElement *operator* () const + { + return RElement::make_element_object (m_it.operator-> ()); + } + +private: + basic_iter m_it; +}; + +static NetworkElementIterator begin_network_elements (pex::RNetwork *network) +{ + return NetworkElementIterator (network->begin_elements ()); +} + +static NetworkElementIterator end_network_elements (pex::RNetwork *network) +{ + return NetworkElementIterator (network->end_elements ()); +} + +class NetworkNodeIterator +{ +public: + typedef pex::RNetwork::node_iterator basic_iter; + typedef std::forward_iterator_tag iterator_category; + typedef RNode *value_type; + typedef RNode *reference; + typedef void pointer; + typedef void difference_type; + + NetworkNodeIterator (basic_iter it) + : m_it (it) + { } + + bool operator== (const NetworkNodeIterator &it) const { return m_it == it.m_it; } + void operator++ () { ++m_it; } + + RNode *operator* () const + { + return RNode::make_node_object (m_it.operator-> ()); + } + +private: + basic_iter m_it; +}; + +static NetworkNodeIterator begin_network_nodes (pex::RNetwork *network) +{ + return NetworkNodeIterator (network->begin_nodes ()); +} + +static NetworkNodeIterator end_network_nodes (pex::RNetwork *network) +{ + return NetworkNodeIterator (network->end_nodes ()); +} + +Class decl_RNetwork ("pex", "RNetwork", + gsi::factory_ext ("create_node", &create_node, gsi::arg ("type"), gsi::arg ("port_index"), + "@brief Creates a new node with the given type and index'.\n" + "@return A reference to the new nbode object." + ) + + gsi::factory_ext ("create_element", &create_element, gsi::arg ("resistance"), gsi::arg ("a"), gsi::arg ("b"), + "@brief Creates a new element between the nodes given by 'a' abd 'b'.\n" + "If a resistor already exists between the two nodes, both resistors are combined into one.\n" + "@return A reference to the new resistor object." + ) + + gsi::method_ext ("remove_element", &remove_element, gsi::arg ("element"), + "@brief Removes the given element\n" + "If removing the element renders an internal node orphan (i.e. without elements), this " + "node is removed too." + ) + + gsi::method_ext ("remove_node", &remove_node, gsi::arg ("node"), + "@brief Removes the given node\n" + "Only internal nodes can be removed. Removing a node will also remove the " + "elements attached to this node." + ) + + gsi::method ("clear", &pex::RNetwork::clear, + "@brief Clears the network\n" + ) + + gsi::method ("simplify", &pex::RNetwork::simplify, + "@brief Simplifies the network\n" + "\n" + "This will:\n" + "@ul\n" + "@li Join serial resistors if connected by an internal node @/li\n" + "@li Remove shorts and join the nodes, if one of them is\n" + " an internal node. The non-internal node will persist @/li\n" + "@li Remove \"dangling\" resistors if the dangling node is\n" + " an internal one @/li\n" + "@/ul\n" + ) + + gsi::iterator_ext ("each_element", gsi::return_new_object (), &begin_network_elements, &end_network_elements, + "@brief Iterates the \\RElement objects inside the network\n" + ) + + gsi::iterator_ext ("each_node", gsi::return_new_object (), &begin_network_nodes, &end_network_nodes, + "@brief Iterates the \\RNode objects inside the network\n" + ) + + gsi::method ("num_nodes", &pex::RNetwork::num_nodes, + "@brief Gets the total number of nodes in the network\n" + ) + + gsi::method ("num_internal_nodes", &pex::RNetwork::num_internal_nodes, + "@brief Gets the number of internal nodes in the network\n" + ) + + gsi::method ("num_elements", &pex::RNetwork::num_elements, + "@brief Gets the number of elements in the network\n" + ) + + gsi::method ("to_s", &pex::RNetwork::to_string, + "@brief Returns a string representation of the network\n" + ), + "@brief Represents a network of resistors\n" + "\n" + "The network is basically a graph with nodes and edges (the resistors). " + "The resistors are called 'elements' and are represented by \\RElement objects. " + "The nodes are represented by \\RNode objects. " + "The network is created by \\RExtractor#extract, which turns a polygon into a resistor network.\n" + "\n" + "This class has been introduced in version 0.30.1\n" +); + static pex::RExtractor *new_sqc_rextractor (double dbu) { return new pex::SquareCountingRExtractor (dbu); } +static pex::RExtractor *new_tesselation_rextractor (double dbu, double min_b, double max_area) +{ + auto res = new pex::TriangulationRExtractor (dbu); + res->triangulation_parameters ().min_b = min_b; + res->triangulation_parameters ().max_area = max_area; + return res; +} + +static pex::RNetwork *extract_ipolygon (pex::RExtractor *rex, const db::Polygon &poly, const std::vector &vertex_ports, const std::vector &polygon_ports) +{ + std::unique_ptr p_network (new pex::RNetwork ()); + rex->extract (poly, vertex_ports, polygon_ports, *p_network); + return p_network.release (); +} + Class decl_RExtractor ("pex", "RExtractor", - gsi::constructor ("square_counting", &new_sqc_rextractor, gsi::arg ("dbu"), + gsi::constructor ("square_counting_extractor", &new_sqc_rextractor, gsi::arg ("dbu"), "@brief Creates a square counting R extractor\n" + "The square counting extractor extracts resistances from a polygon with ports using the following approach:\n" + "\n" + "@ul\n" + "@li Split the original polygon into convex parts using a Hertel-Mehlhorn decomposition @/li\n" + "@li Create internal nodes at the locations where the parts touch @/li\n" + "@li For each part, extract the resistance along the horizonal or vertical axis, whichever is longer @/li" + "@/ul\n" + "\n" + "The square counting extractor assumes the parts are 'thin' - i.e. the long axis is much longer than the short " + "axis - and the parts are either oriented horizontally or vertically. The current flow is assumed to be linear and " + "homogenous along the long axis. Ports define probe points for the voltages along the long long axis. " + "Polygon ports are considered points located at the center of the polygon's bounding box.\n" + "\n" + "The results of the extraction is normalized to a sheet resistance of 1 Ohm/square - i.e. to obtain the actual resistor " + "values, multiply the element resistance values by the sheet resistance.\n" + "\n" + "@param dbu The database unit of the polygons the extractor will work on\n" + "@return A new \\RExtractor object that implements the square counting extractor\n" + ) + + gsi::constructor ("tesselation_extractor", &new_tesselation_rextractor, gsi::arg ("dbu"), gsi::arg ("min_b", 0.3), gsi::arg ("max_area", 0.0), + "@brief Creates a tesselation R extractor\n" + "The tesselation extractor starts with a triangulation of the original polygon. The triangulation is " + "turned into a resistor network and simplified.\n" + "\n" + "The tesselation extractor is well suited for homogeneous geometries, but does not properly consider " + "the boundary conditions at the borders of the region. It is good for extracting resistance networks of " + "substrate or large sheet layers.\n" + "\n" + "The square counting extractor assumes the parts are 'thin' - i.e. the long axis is much longer than the short " + "axis - and the parts are either oriented horizontally or vertically. The current flow is assumed to be linear and " + "homogenous along the long axis. Ports define probe points for the voltages along the long long axis. " + "Polygon ports are considered points located at the center of the polygon's bounding box.\n" + "\n" + "The tesselation extractor delivers a full matrix of resistors - there is a resistor between every pair of ports.\n" + "\n" + "The results of the extraction is normalized to a sheet resistance of 1 Ohm/square - i.e. to obtain the actual resistor " + "values, multiply the element resistance values by the sheet resistance.\n" + "\n" + "@param dbu The database unit of the polygons the extractor will work on\n" + "@param min_b Defines the min 'b' value of the refined Delaunay triangulation (see \\Polygon#delaunay)\n" + "@param max_area Defines maximum area value of the refined Delaunay triangulation (see \\Polygon#delaunay). The value is given in square micrometer units.\n" + "@return A new \\RExtractor object that implements the square counting extractor\n" + ) + + gsi::factory_ext ("extract", &extract_ipolygon, gsi::arg ("polygon"), gsi::arg ("vertex_ports", std::vector (), "[]"), gsi::arg ("polygon_ports", std::vector (), "[]"), + "@brief Runs the extraction on the given polygon\n" + "This method will create a new \\RNetwork object from the given polygon.\n" + "\n" + "'vertex_ports' is an array of points that define point-like ports. A port will create a \\RNode object in the " + "resistor graph. This node object carries the type \\VertexPort and the index of the vertex in this array.\n" + "\n" + "'polygon_ports' is an array of polygons that define distributed ports. The polygons should be inside the resistor polygon " + "and convex. A port will create a \\RNode object in the resistor graph. " + "For polygon ports, this node object carries the type \\PolygonPort and the index of the polygon in this array.\n" ), - "@brief A base class for the R extractor\n" + "@brief The basic R extractor class\n" + "\n" + "Use \\tesselation_extractor and \\square_counting_extractor to create an actual extractor object.\n" + "To use the extractor, call the \\extract method on a given polygon with ports that define the network attachment points.\n" + "\n" + "This class has been introduced in version 0.30.1\n" ); } diff --git a/src/pex/pex/pexRExtractor.h b/src/pex/pex/pexRExtractor.h index 0e7a2578e..b9da173bc 100644 --- a/src/pex/pex/pexRExtractor.h +++ b/src/pex/pex/pexRExtractor.h @@ -93,6 +93,14 @@ public: */ std::string to_string () const; + /** + * @brief Gets the network the node lives in + */ + RNetwork *graph () const + { + return mp_network; + } + protected: friend class RNetwork; friend class RElement; @@ -174,6 +182,14 @@ struct PEX_PUBLIC RElement */ std::string to_string () const; + /** + * @brief Gets the network the node lives in + */ + RNetwork *graph () const + { + return mp_network; + } + protected: friend class RNetwork; friend class tl::list_impl; diff --git a/src/rba/unit_tests/rbaTests.cc b/src/rba/unit_tests/rbaTests.cc index 7b23e3940..e2ff18d3a 100644 --- a/src/rba/unit_tests/rbaTests.cc +++ b/src/rba/unit_tests/rbaTests.cc @@ -142,6 +142,7 @@ RUBYTEST (dbTransTest, "dbTransTest.rb") RUBYTEST (dbVectorTest, "dbVectorTest.rb") RUBYTEST (dbUtilsTests, "dbUtilsTests.rb") RUBYTEST (dbTechnologies, "dbTechnologies.rb") +RUBYTEST (pexTests, "pexTests.rb") RUBYTEST (edtTest, "edtTest.rb") RUBYTEST (extNetTracer, "extNetTracer.rb") RUBYTEST (imgObject, "imgObject.rb") diff --git a/src/unit_tests/unit_test_main.cc b/src/unit_tests/unit_test_main.cc index 20c8696dc..6391b619c 100644 --- a/src/unit_tests/unit_test_main.cc +++ b/src/unit_tests/unit_test_main.cc @@ -71,6 +71,7 @@ // and the plugins/auxiliary modules (some in non-Qt case) #include "libForceLink.h" #include "rdbForceLink.h" +#include "pexForceLink.h" #include "antForceLink.h" #include "imgForceLink.h" #include "edtForceLink.h" diff --git a/testdata/ruby/pexTests.rb b/testdata/ruby/pexTests.rb new file mode 100644 index 000000000..c18de6e03 --- /dev/null +++ b/testdata/ruby/pexTests.rb @@ -0,0 +1,127 @@ +# encoding: UTF-8 + +# KLayout Layout Viewer +# Copyright (C) 2006-2025 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 PEX_TestClass < TestBase + + # PEX basics + def test_1_Basic + + rn = RBA::RNetwork::new + + a = rn.create_node(RBA::RNode::VertexPort, 1) + b = rn.create_node(RBA::RNode::Internal, 2) + c = rn.create_node(RBA::RNode::PolygonPort, 3) + + assert_equal(a.type, RBA::RNode::VertexPort) + assert_equal(a.port_index, 1) + assert_equal(a.object_id, a.object_id) + assert_not_equal(a.object_id, b.object_id) + assert_equal(a.to_s, "V1") + + assert_equal(b.to_s, "$2") + assert_equal(c.to_s, "P3") + + rab = rn.create_element(1.0, a, b) + assert_equal(rab.a.object_id, a.object_id) + assert_equal(rab.b.object_id, b.object_id) + assert_equal(rab.to_s, "R $2 V1 1") + + rn.create_element(1.0, a, b) + assert_equal(rab.to_s, "R $2 V1 0.5") + + rbc = rn.create_element(1.0, b, c) + + assert_equal(rn.to_s, "R $2 V1 0.5\n" + "R $2 P3 1") + + assert_equal(b.each_element.collect(&:to_s).sort.join(";"), "R $2 P3 1;R $2 V1 0.5") + assert_equal(rn.each_element.collect(&:to_s).sort.join(";"), "R $2 P3 1;R $2 V1 0.5") + assert_equal(rn.each_node.collect(&:to_s).sort.join(";"), "$2;P3;V1") + + rn.simplify + assert_equal(rn.to_s, "R P3 V1 1.5") + + rn.clear + assert_equal(rn.to_s, "") + + end + + def test_2_Destroy + + rn = RBA::RNetwork::new + + a = rn.create_node(RBA::RNode::VertexPort, 1) + b = rn.create_node(RBA::RNode::Internal, 2) + rab = rn.create_element(1.0, a, b) + + # this should invalid the pointers + rn._destroy + + begin + assert_equal(a.to_s, "") + rescue => ex + # graph has been destroyed already + end + + begin + assert_equal(rab.to_s, "") + rescue => ex + # graph has been destroyed already + end + + end + + def test_3_SQC + + poly = RBA::Polygon::new(RBA::Box::new(0, 0, 1100, 100)) + + vp = [ RBA::Point::new(50, 50) ] + pp = [ RBA::Polygon::new(RBA::Box::new(1000, 0, 1100, 100)) ] + + rex = RBA::RExtractor::square_counting_extractor(0.001) + rn = rex.extract(poly, vp, pp) + + assert_equal(rn.to_s, "R P0 V0 10") + + end + + def test_3_TX + + poly = RBA::Polygon::new(RBA::Box::new(0, 0, 1100, 100)) + + vp = [ RBA::Point::new(50, 50) ] + pp = [ RBA::Polygon::new(RBA::Box::new(1000, 0, 1100, 100)) ] + + rex = RBA::RExtractor::tesselation_extractor(0.001, 0.8) + rn = rex.extract(poly, vp, pp) + + assert_equal(rn.to_s, "R P0 V0 9.44") + + end + +end + +load("test_epilogue.rb") + From f0943dea53600e2e05b13587bb64df3cf9cadc02 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 24 Apr 2025 22:01:56 +0200 Subject: [PATCH 162/392] More debugging support --- src/pex/pex/gsiDeclRExtractor.cc | 15 ++++++++++----- src/pex/pex/pexSquareCountingRExtractor.cc | 8 +++++--- src/pex/pex/pexSquareCountingRExtractor.h | 17 +++++++++++++++++ src/pex/pex/pexTriangulationRExtractor.cc | 5 ++++- src/pex/pex/pexTriangulationRExtractor.h | 17 +++++++++++++++++ 5 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/pex/pex/gsiDeclRExtractor.cc b/src/pex/pex/gsiDeclRExtractor.cc index 626575092..740765e33 100644 --- a/src/pex/pex/gsiDeclRExtractor.cc +++ b/src/pex/pex/gsiDeclRExtractor.cc @@ -400,16 +400,19 @@ Class decl_RNetwork ("pex", "RNetwork", "This class has been introduced in version 0.30.1\n" ); -static pex::RExtractor *new_sqc_rextractor (double dbu) +static pex::RExtractor *new_sqc_rextractor (double dbu, bool skip_simplify) { - return new pex::SquareCountingRExtractor (dbu); + auto res = new pex::SquareCountingRExtractor (dbu); + res->set_skip_simplfy (skip_simplify); + return res; } -static pex::RExtractor *new_tesselation_rextractor (double dbu, double min_b, double max_area) +static pex::RExtractor *new_tesselation_rextractor (double dbu, double min_b, double max_area, bool skip_reduction) { auto res = new pex::TriangulationRExtractor (dbu); res->triangulation_parameters ().min_b = min_b; res->triangulation_parameters ().max_area = max_area; + res->set_skip_reduction (skip_reduction); return res; } @@ -421,7 +424,7 @@ static pex::RNetwork *extract_ipolygon (pex::RExtractor *rex, const db::Polygon } Class decl_RExtractor ("pex", "RExtractor", - gsi::constructor ("square_counting_extractor", &new_sqc_rextractor, gsi::arg ("dbu"), + gsi::constructor ("square_counting_extractor", &new_sqc_rextractor, gsi::arg ("dbu"), gsi::arg ("skip_simplify", false), "@brief Creates a square counting R extractor\n" "The square counting extractor extracts resistances from a polygon with ports using the following approach:\n" "\n" @@ -440,9 +443,10 @@ Class decl_RExtractor ("pex", "RExtractor", "values, multiply the element resistance values by the sheet resistance.\n" "\n" "@param dbu The database unit of the polygons the extractor will work on\n" + "@param skip_simplify If true, the final step to simplify the netlist will be skipped. This feature is for testing mainly.\n" "@return A new \\RExtractor object that implements the square counting extractor\n" ) + - gsi::constructor ("tesselation_extractor", &new_tesselation_rextractor, gsi::arg ("dbu"), gsi::arg ("min_b", 0.3), gsi::arg ("max_area", 0.0), + gsi::constructor ("tesselation_extractor", &new_tesselation_rextractor, gsi::arg ("dbu"), gsi::arg ("min_b", 0.3), gsi::arg ("max_area", 0.0), gsi::arg ("skip_reduction", false), "@brief Creates a tesselation R extractor\n" "The tesselation extractor starts with a triangulation of the original polygon. The triangulation is " "turned into a resistor network and simplified.\n" @@ -464,6 +468,7 @@ Class decl_RExtractor ("pex", "RExtractor", "@param dbu The database unit of the polygons the extractor will work on\n" "@param min_b Defines the min 'b' value of the refined Delaunay triangulation (see \\Polygon#delaunay)\n" "@param max_area Defines maximum area value of the refined Delaunay triangulation (see \\Polygon#delaunay). The value is given in square micrometer units.\n" + "@param skip_reduction If true, the reduction step for the netlist will be skipped. This feature is for testing mainly. The resulting R graph will contain all the original triangles and the internal nodes representing the vertexes.\n" "@return A new \\RExtractor object that implements the square counting extractor\n" ) + gsi::factory_ext ("extract", &extract_ipolygon, gsi::arg ("polygon"), gsi::arg ("vertex_ports", std::vector (), "[]"), gsi::arg ("polygon_ports", std::vector (), "[]"), diff --git a/src/pex/pex/pexSquareCountingRExtractor.cc b/src/pex/pex/pexSquareCountingRExtractor.cc index 0ad1bfa27..9170239d0 100644 --- a/src/pex/pex/pexSquareCountingRExtractor.cc +++ b/src/pex/pex/pexSquareCountingRExtractor.cc @@ -71,6 +71,7 @@ struct JoinEdgeSets SquareCountingRExtractor::SquareCountingRExtractor (double dbu) { m_dbu = dbu; + m_skip_simplify = false; m_decomp_param.split_edges = true; m_decomp_param.with_segments = false; @@ -162,8 +163,7 @@ SquareCountingRExtractor::do_extract (const db::Polygon &db_poly, const std::vec ++em; } - // @@@ TODO: multiply with sheet rho! - // @@@ TODO: width dependency + // TODO: width dependency? if (r == 0) { rnetwork.create_element (pex::RElement::short_value (), pl->second, pl_next->second); } else { @@ -292,7 +292,9 @@ SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector } - rnetwork.simplify (); + if (! m_skip_simplify) { + rnetwork.simplify (); + } } } diff --git a/src/pex/pex/pexSquareCountingRExtractor.h b/src/pex/pex/pexSquareCountingRExtractor.h index 9ceb99d46..2d95b3e26 100644 --- a/src/pex/pex/pexSquareCountingRExtractor.h +++ b/src/pex/pex/pexSquareCountingRExtractor.h @@ -64,6 +64,22 @@ public: return m_decomp_param; } + /** + * @brief Sets a value indicating whether to skip the simplify step + */ + void set_skip_simplfy (bool f) + { + m_skip_simplify = f; + } + + /** + * @brief Gets a value indicating whether to skip the simplify step + */ + bool skip_simplify () const + { + return m_skip_simplify; + } + /** * @brief Sets the database unit */ @@ -129,6 +145,7 @@ protected: private: db::plc::ConvexDecompositionParameters m_decomp_param; double m_dbu; + bool m_skip_simplify; }; } diff --git a/src/pex/pex/pexTriangulationRExtractor.cc b/src/pex/pex/pexTriangulationRExtractor.cc index e52a40f88..b39a6a37d 100644 --- a/src/pex/pex/pexTriangulationRExtractor.cc +++ b/src/pex/pex/pexTriangulationRExtractor.cc @@ -32,6 +32,7 @@ namespace pex TriangulationRExtractor::TriangulationRExtractor (double dbu) { m_dbu = dbu; + m_skip_reduction = false; m_tri_param.min_b = 0.3; m_tri_param.max_area = 0.0; @@ -206,7 +207,9 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector< // eliminate internal nodes - eliminate_all (rnetwork); + if (! m_skip_reduction) { + eliminate_all (rnetwork); + } } void diff --git a/src/pex/pex/pexTriangulationRExtractor.h b/src/pex/pex/pexTriangulationRExtractor.h index 4215d0180..6749c7f84 100644 --- a/src/pex/pex/pexTriangulationRExtractor.h +++ b/src/pex/pex/pexTriangulationRExtractor.h @@ -65,6 +65,22 @@ public: return m_tri_param; } + /** + * @brief Sets a value indicating whether to skip the reduction step + */ + void set_skip_reduction (bool f) + { + m_skip_reduction = f; + } + + /** + * @brief Gets a value indicating whether to skip the reduction step + */ + bool skip_reduction () const + { + return m_skip_reduction; + } + /** * @brief Sets the database unit */ @@ -89,6 +105,7 @@ public: private: db::plc::TriangulationParameters m_tri_param; double m_dbu; + bool m_skip_reduction; void create_conductances (const db::plc::Polygon &tri, const std::unordered_map &vertex2node, RNetwork &rnetwork); void eliminate_node (pex::RNode *node, RNetwork &rnetwork); From d317dc2fe3a97d00f2153528e3e169a9918d898f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 24 Apr 2025 23:30:50 +0200 Subject: [PATCH 163/392] Fixed issue #2025 (brackets get added on PCell parameters) --- src/edt/edt/edtPCellParametersPage.cc | 50 +++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/src/edt/edt/edtPCellParametersPage.cc b/src/edt/edt/edtPCellParametersPage.cc index 27ca1a261..8f27d312b 100644 --- a/src/edt/edt/edtPCellParametersPage.cc +++ b/src/edt/edt/edtPCellParametersPage.cc @@ -47,6 +47,31 @@ namespace edt { +static std::string variant_list_to_string (const tl::Variant &value) +{ + if (! value.is_list ()) { + tl::Variant v = tl::Variant::empty_list (); + v.push (value); + return v.to_parsable_string (); + } + + for (auto i = value.begin (); i != value.end (); ++i) { + if (! i->is_a_string () || std::string (i->to_string ()).find (",") != std::string::npos) { + return value.to_parsable_string (); + } + } + + // otherwise we can plainly combine the strings with "," + std::string res; + for (auto i = value.begin (); i != value.end (); ++i) { + if (i != value.begin ()) { + res += ","; + } + res += i->to_string (); + } + return res; +} + static void set_value (const db::PCellParameterDeclaration &p, QWidget *widget, const tl::Variant &value) { if (p.get_choices ().empty ()) { @@ -91,7 +116,7 @@ static void set_value (const db::PCellParameterDeclaration &p, QWidget *widget, QLineEdit *le = dynamic_cast (widget); if (le) { le->blockSignals (true); - le->setText (value.to_qstring ()); + le->setText (tl::to_qstring (variant_list_to_string (value))); le->blockSignals (false); } } @@ -905,8 +930,29 @@ PCellParametersPage::get_parameters_internal (db::ParameterStates &states, bool { QLineEdit *le = dynamic_cast (m_widgets [r]); if (le) { - std::vector values = tl::split (tl::to_string (le->text ()), ","); + + std::string s = tl::to_string (le->text ()); + + // try parsing a bracketed expression + tl::Extractor ex (s.c_str ()); + if (*ex.skip () == '(') { + tl::Variant v; + try { + ex.read (v); + ps.set_value (v); + break; + } catch (...) { + // ignore errors + } + } else if (ex.at_end ()) { + ps.set_value (tl::Variant::empty_list ()); + break; + } + + // otherwise: plain splitting at comma + std::vector values = tl::split (s, ","); ps.set_value (tl::Variant (values.begin (), values.end ())); + } } break; From 148498f8405751eca5a23905ca248c8f33b4a6ca Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 26 Apr 2025 16:15:25 +0200 Subject: [PATCH 164/392] Fixing issue #2026 (after 2.5D display main 2D layout does not display anymore with visible shapes) --- .../view_25d/lay_plugin/layD25ViewWidget.cc | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index b3b0a0e31..7a29a7ff0 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -661,15 +661,15 @@ D25ViewWidget::enter (const db::RecursiveShapeIterator *iter, double zstart, dou void D25ViewWidget::entry (const db::Region &data, double dbu, double zstart, double zstop) { - // try to establish a default color from the region's origin if required - const db::RecursiveShapeIterator *iter = 0; const db::OriginalLayerRegion *original = dynamic_cast (data.delegate ()); if (original) { - iter = original->iter (); + // try to establish a default color from the region's origin if required + auto it = original->begin_iter (); + enter (&it.first, zstart, zstop); + } else { + enter (0, zstart, zstop); } - enter (iter, zstart, zstop); - tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ..."))); render_region (progress, *m_layers.back ().vertex_chunk, *m_layers.back ().normals_chunk, *m_layers.back ().line_chunk, data, dbu, db::CplxTrans (dbu).inverted () * m_bbox, zstart, zstop); } @@ -677,15 +677,15 @@ D25ViewWidget::entry (const db::Region &data, double dbu, double zstart, double void D25ViewWidget::entry (const db::Edges &data, double dbu, double zstart, double zstop) { - // try to establish a default color from the region's origin if required - const db::RecursiveShapeIterator *iter = 0; const db::OriginalLayerEdges *original = dynamic_cast (data.delegate ()); if (original) { - iter = original->iter (); + // try to establish a default color from the region's origin if required + auto it = original->begin_iter (); + enter (&it.first, zstart, zstop); + } else { + enter (0, zstart, zstop); } - enter (iter, zstart, zstop); - tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ..."))); render_edges (progress, *m_layers.back ().vertex_chunk, *m_layers.back ().normals_chunk, *m_layers.back ().line_chunk, data, dbu, db::CplxTrans (dbu).inverted () * m_bbox, zstart, zstop); } @@ -693,15 +693,15 @@ D25ViewWidget::entry (const db::Edges &data, double dbu, double zstart, double z void D25ViewWidget::entry (const db::EdgePairs &data, double dbu, double zstart, double zstop) { - // try to establish a default color from the region's origin if required - const db::RecursiveShapeIterator *iter = 0; const db::OriginalLayerEdgePairs *original = dynamic_cast (data.delegate ()); if (original) { - iter = original->iter (); + // try to establish a default color from the region's origin if required + auto it = original->begin_iter (); + enter (&it.first, zstart, zstop); + } else { + enter (0, zstart, zstop); } - enter (iter, zstart, zstop); - tl::AbsoluteProgress progress (tl::to_string (tr ("Rendering ..."))); render_edge_pairs (progress, *m_layers.back ().vertex_chunk, *m_layers.back ().normals_chunk, *m_layers.back ().line_chunk, data, dbu, db::CplxTrans (dbu).inverted () * m_bbox, zstart, zstop); } From 2bd82af6feb476ffbb83abe2022d76564dc17655 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 26 Apr 2025 16:52:39 +0200 Subject: [PATCH 165/392] Provide a solution of feature request #2024 - There is a new configuration page entry called "Min spacing" for the grid. The default value is 4. The value specifies the grid min spacing in units of UI font height. - A bugfix is included: the ruler now is drawn after the grid, hence is not hidden by it (specifically in checkerboard pattern mode) - To allow bigger grid spacing, the ruler now is allowed to grow bigger than before. --- src/laybasic/laybasic/laybasicConfig.h | 1 + src/layview/layview/GridNetConfigPage.ui | 336 +++++++++++--------- src/layview/layview/layGridNet.cc | 129 +++++--- src/layview/layview/layGridNet.h | 8 + src/layview/layview/layGridNetConfigPage.cc | 5 + 5 files changed, 271 insertions(+), 208 deletions(-) diff --git a/src/laybasic/laybasic/laybasicConfig.h b/src/laybasic/laybasic/laybasicConfig.h index 1b33e6455..eb99c9d4b 100644 --- a/src/laybasic/laybasic/laybasicConfig.h +++ b/src/laybasic/laybasic/laybasicConfig.h @@ -43,6 +43,7 @@ static const std::string cfg_grid_grid_color ("grid-grid-color"); static const std::string cfg_grid_style0 ("grid-style0"); static const std::string cfg_grid_style1 ("grid-style1"); static const std::string cfg_grid_style2 ("grid-style2"); +static const std::string cfg_grid_density ("grid-density"); static const std::string cfg_grid_visible ("grid-visible"); static const std::string cfg_grid_micron ("grid-micron"); static const std::string cfg_grid_show_ruler ("grid-show-ruler"); diff --git a/src/layview/layview/GridNetConfigPage.ui b/src/layview/layview/GridNetConfigPage.ui index 34db49143..427129ba0 100644 --- a/src/layview/layview/GridNetConfigPage.ui +++ b/src/layview/layview/GridNetConfigPage.ui @@ -7,7 +7,7 @@ 0 0 483 - 341 + 361 @@ -59,6 +59,23 @@ 6 + + + + Far style + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Show ruler + + + @@ -108,7 +125,122 @@ - + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + + + + + + + + + + + Grid + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + + + + + + + Ruler + + + + + + + + + + + + + + Axis + + + + + + + + + + + + + + Close style + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Color (all) + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + Color @@ -118,14 +250,17 @@ - - - - Qt::Horizontal + + + + Style + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + @@ -169,30 +304,23 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - + + - + Color + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - Qt::Horizontal + + + + Color + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -245,141 +373,37 @@ - - - - Far style - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Close style - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Color (all) - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - - - - - - - - Grid - - - - - - - Show Ruler - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Style - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Color - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - - - - - - - Axis - - - - - - - Color - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Ruler - - - - + Qt::Horizontal + + + + 1 + + + + + + + Min. spacing + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + (Font height units) + + + diff --git a/src/layview/layview/layGridNet.cc b/src/layview/layview/layGridNet.cc index 2cc8c85a2..b960579b1 100644 --- a/src/layview/layview/layGridNet.cc +++ b/src/layview/layview/layGridNet.cc @@ -41,6 +41,8 @@ namespace lay // ------------------------------------------------------------ // Helper functions to get and set the configuration +int default_density = 4; + static struct { lay::GridNet::GridStyle style; const char *string; @@ -79,6 +81,20 @@ GridNetStyleConverter::to_string (lay::GridNet::GridStyle style) return ""; } +void +GridNetDensityConverter::from_string (const std::string &value, int &density) +{ + density = default_density; // original default + tl::Extractor ex (value.c_str ()); + ex.try_read (density); +} + +std::string +GridNetDensityConverter::to_string (int density) +{ + return tl::to_string (density); +} + // ------------------------------------------------------------ // Implementation of the GridNetPluginDeclaration @@ -92,6 +108,7 @@ GridNetPluginDeclaration::get_options (std::vector < std::pair (cfg_grid_style0, GridNetStyleConverter ().to_string (lay::GridNet::Invisible))); options.push_back (std::pair (cfg_grid_style1, GridNetStyleConverter ().to_string (lay::GridNet::Dots))); options.push_back (std::pair (cfg_grid_style2, GridNetStyleConverter ().to_string (lay::GridNet::TenthDottedLines))); + options.push_back (std::pair (cfg_grid_density, "")); options.push_back (std::pair (cfg_grid_visible, tl::to_string (true))); options.push_back (std::pair (cfg_grid_show_ruler, tl::to_string (true))); // grid-micron is not configured here since some other entity is supposed to do this. @@ -122,7 +139,8 @@ GridNet::GridNet (LayoutViewBase *view) lay::Plugin (view), mp_view (view), m_visible (false), m_show_ruler (true), m_grid (1.0), - m_style0 (Invisible), m_style1 (Invisible), m_style2 (Invisible) + m_style0 (Invisible), m_style1 (Invisible), m_style2 (Invisible), + m_density (default_density) { // .. nothing yet .. } @@ -175,6 +193,12 @@ GridNet::configure (const std::string &name, const std::string &value) GridNetStyleConverter ().from_string (value, style); need_update = test_and_set (m_style2, style); + } else if (name == cfg_grid_density) { + + int density = 0; + GridNetDensityConverter ().from_string (value, density); + need_update = test_and_set (m_density, density); + } else if (name == cfg_grid_show_ruler) { bool sr = false; @@ -246,13 +270,14 @@ GridNet::render_bg (const lay::Viewport &vp, ViewObjectCanvas &canvas) // fw is the basic unit of the ruler geometry int fwr = lay::FixedFont::get_font (bmp_canvas->font_resolution ()).width (); + int threshold = std::min (1000, m_density * fwr); double dgrid = trans.ctrans (m_grid); GridStyle style = m_style1; // compute major grid and switch to secondary style if necessary int s = 0; - while (dgrid < fwr * 4) { + while (dgrid < threshold) { if (s == 0) { dgrid *= 2.0; } else if (s == 1) { @@ -279,56 +304,6 @@ GridNet::render_bg (const lay::Viewport &vp, ViewObjectCanvas &canvas) int nx = int (dbworld.width () / grid + eps) + 2; int ny = int (dbworld.height () / grid + eps) + 2; - if (m_show_ruler && dgrid < vp.width () * 0.2) { - - int rh = int (floor (0.5 + fwr * 0.8)); - int xoffset = int (floor (0.5 + fwr * 2.5)); - int yoffset = int (floor (0.5 + fwr * 2.5)); - - painter.fill_rect (db::Point (xoffset, vp.height () - yoffset - rh / 2), - db::Point (xoffset + int (floor (0.5 + dgrid)), vp.height () - yoffset + rh / 2), - ruler_color); - - painter.draw_rect (db::Point (xoffset + int (floor (0.5 + dgrid)), vp.height () - yoffset - rh / 2), - db::Point (xoffset + int (floor (0.5 + 2 * dgrid)), vp.height () - yoffset + rh / 2), - ruler_color); - - painter.draw_text (tl::sprintf ("%g \265m", grid * 2).c_str (), - db::Point (xoffset + int (floor (0.5 + trans.ctrans (2 * grid))), vp.height () - yoffset - rh / 2 - 2), - ruler_color, -1, 1); - - if (mp_view->global_trans ().fp_trans () != db::DFTrans ()) { - - // draw a small "F" indicating any global transformation - db::Point pts[] = { - db::Point (-4, -5), - db::Point (-4, 5), - db::Point (4, 5), - db::Point (4, 3), - db::Point (-2, 3), - db::Point (-2, 1), - db::Point (3, 1), - db::Point (3, -1), - db::Point (-2, -1), - db::Point (-2, -5), - db::Point (-4, -5) - }; - - db::Polygon poly; - poly.assign_hull (&pts[0], &pts[0] + (sizeof (pts) / sizeof (pts[0]))); - poly.transform (db::FTrans (mp_view->global_trans ().fp_trans ())); - - for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); !e.at_end (); ++e) { - db::Point p0 (xoffset + 2 * rh, vp.height () - yoffset - rh * 5); - db::Point p1 = p0 + db::Vector (int (floor (0.5 + (*e).p1 ().x () * 0.1 * rh * 4)), -int (floor (0.5 + (*e).p1 ().y () * 0.1 * rh * 4))); - db::Point p2 = p0 + db::Vector (int (floor (0.5 + (*e).p2 ().x () * 0.1 * rh * 4)), -int (floor (0.5 + (*e).p2 ().y () * 0.1 * rh * 4))); - painter.draw_line (p1, p2, ruler_color); - } - - } - - } - // draw grid if (style == Dots || style == TenthDottedLines || style == DottedLines || style == LightDottedLines) { @@ -549,6 +524,56 @@ GridNet::render_bg (const lay::Viewport &vp, ViewObjectCanvas &canvas) } + if (m_show_ruler && dgrid < vp.width () * 0.4) { + + int rh = int (floor (0.5 + fwr * 0.8)); + int xoffset = int (floor (0.5 + fwr * 2.5)); + int yoffset = int (floor (0.5 + fwr * 2.5)); + + painter.fill_rect (db::Point (xoffset, vp.height () - yoffset - rh / 2), + db::Point (xoffset + int (floor (0.5 + dgrid)), vp.height () - yoffset + rh / 2), + ruler_color); + + painter.draw_rect (db::Point (xoffset + int (floor (0.5 + dgrid)), vp.height () - yoffset - rh / 2), + db::Point (xoffset + int (floor (0.5 + 2 * dgrid)), vp.height () - yoffset + rh / 2), + ruler_color); + + painter.draw_text (tl::sprintf ("%g \265m", grid * 2).c_str (), + db::Point (xoffset + int (floor (0.5 + trans.ctrans (2 * grid))), vp.height () - yoffset - rh / 2 - 2), + ruler_color, -1, 1); + + if (mp_view->global_trans ().fp_trans () != db::DFTrans ()) { + + // draw a small "F" indicating any global transformation + db::Point pts[] = { + db::Point (-4, -5), + db::Point (-4, 5), + db::Point (4, 5), + db::Point (4, 3), + db::Point (-2, 3), + db::Point (-2, 1), + db::Point (3, 1), + db::Point (3, -1), + db::Point (-2, -1), + db::Point (-2, -5), + db::Point (-4, -5) + }; + + db::Polygon poly; + poly.assign_hull (&pts[0], &pts[0] + (sizeof (pts) / sizeof (pts[0]))); + poly.transform (db::FTrans (mp_view->global_trans ().fp_trans ())); + + for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); !e.at_end (); ++e) { + db::Point p0 (xoffset + 2 * rh, vp.height () - yoffset - rh * 5); + db::Point p1 = p0 + db::Vector (int (floor (0.5 + (*e).p1 ().x () * 0.1 * rh * 4)), -int (floor (0.5 + (*e).p1 ().y () * 0.1 * rh * 4))); + db::Point p2 = p0 + db::Vector (int (floor (0.5 + (*e).p2 ().x () * 0.1 * rh * 4)), -int (floor (0.5 + (*e).p2 ().y () * 0.1 * rh * 4))); + painter.draw_line (p1, p2, ruler_color); + } + + } + + } + } } diff --git a/src/layview/layview/layGridNet.h b/src/layview/layview/layGridNet.h index 89b63c3f0..d75dcde37 100644 --- a/src/layview/layview/layGridNet.h +++ b/src/layview/layview/layGridNet.h @@ -87,6 +87,7 @@ private: GridStyle m_style0; GridStyle m_style1; GridStyle m_style2; + int m_density; }; class GridNetStyleConverter @@ -96,6 +97,13 @@ public: std::string to_string (lay::GridNet::GridStyle style); }; +class GridNetDensityConverter +{ +public: + void from_string (const std::string &value, int &density); + std::string to_string (int density); +}; + } #endif diff --git a/src/layview/layview/layGridNetConfigPage.cc b/src/layview/layview/layGridNetConfigPage.cc index 6736f72f1..900a31b53 100644 --- a/src/layview/layview/layGridNetConfigPage.cc +++ b/src/layview/layview/layGridNetConfigPage.cc @@ -94,6 +94,10 @@ GridNetConfigPage::setup (lay::Dispatcher *root) style = lay::GridNet::Invisible; root->config_get (cfg_grid_style2, style, GridNetStyleConverter ()); mp_ui->style2_cbx->setCurrentIndex (int (style)); + + int density = 0; + root->config_get (cfg_grid_density, density, GridNetDensityConverter ()); + mp_ui->grid_density_sb->setValue (density); } void @@ -108,6 +112,7 @@ GridNetConfigPage::commit (lay::Dispatcher *root) root->config_set (cfg_grid_style0, lay::GridNet::GridStyle (mp_ui->style0_cbx->currentIndex ()), GridNetStyleConverter ()); root->config_set (cfg_grid_style1, lay::GridNet::GridStyle (mp_ui->style1_cbx->currentIndex ()), GridNetStyleConverter ()); root->config_set (cfg_grid_style2, lay::GridNet::GridStyle (mp_ui->style2_cbx->currentIndex ()), GridNetStyleConverter ()); + root->config_set (cfg_grid_density, mp_ui->grid_density_sb->value (), GridNetDensityConverter ()); } } // namespace lay From 2435e774f45597e2e3f9a0affed8951ab7c8ba81 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 26 Apr 2025 19:05:40 +0200 Subject: [PATCH 166/392] Preventing an internal error when using report after 'input' --- src/drc/drc/built-in-macros/_drc_engine.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index fa5cf402c..15c6356a4 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -1585,7 +1585,8 @@ module DRC self._context("report") do # finish what we got so far - _finish(false) + view = RBA::LayoutView::current + @def_output && @def_output.finish(false, view) @def_output = nil @def_output = _make_report(description, filename, cellname) From ffa42653feb97acfa4e665bc09a35db08771de9e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 26 Apr 2025 22:04:50 +0200 Subject: [PATCH 167/392] Addressing issue #2011 - "report" can now be late in DRC without internal error Yet, the report will only capture the output layers after the report statement has been called. - Text objects don't create zero-area polygons in deep mode XOR now. --- src/db/db/dbLocalOperationUtils.cc | 8 +++++-- src/db/db/dbPolygon.h | 18 ++++++++++++++++ src/db/db/dbRegionLocalOperations.cc | 22 +++++++++++++++---- src/db/unit_tests/dbPolygonTests.cc | 29 ++++++++++++++++++++++++++ src/drc/unit_tests/drcSimpleTests.cc | 10 +++++++++ testdata/drc/drcSimpleTests_94.drc | 26 +++++++++++++++++++++++ testdata/drc/drcSimpleTests_94.gds | Bin 0 -> 606 bytes testdata/drc/drcSimpleTests_au94.gds | Bin 0 -> 734 bytes testdata/drc/drcSimpleTests_au94d.gds | Bin 0 -> 692 bytes 9 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 testdata/drc/drcSimpleTests_94.drc create mode 100644 testdata/drc/drcSimpleTests_94.gds create mode 100644 testdata/drc/drcSimpleTests_au94.gds create mode 100644 testdata/drc/drcSimpleTests_au94d.gds diff --git a/src/db/db/dbLocalOperationUtils.cc b/src/db/db/dbLocalOperationUtils.cc index c338dbe6d..51d98807d 100644 --- a/src/db/db/dbLocalOperationUtils.cc +++ b/src/db/db/dbLocalOperationUtils.cc @@ -39,7 +39,9 @@ PolygonRefToShapesGenerator::PolygonRefToShapesGenerator (db::Layout *layout, db void PolygonRefToShapesGenerator::put (const db::Polygon &polygon) { tl::MutexLocker locker (&mp_layout->lock ()); - if (m_prop_id != 0) { + if (polygon.is_empty ()) { + // ignore empty polygons + } else if (m_prop_id != 0) { mp_shapes->insert (db::PolygonRefWithProperties (db::PolygonRef (polygon, mp_layout->shape_repository ()), m_prop_id)); } else { mp_shapes->insert (db::PolygonRef (polygon, mp_layout->shape_repository ())); @@ -58,7 +60,9 @@ PolygonSplitter::PolygonSplitter (PolygonSink &sink, double max_area_ratio, size void PolygonSplitter::put (const db::Polygon &poly) { - if (db::suggest_split_polygon (poly, m_max_vertex_count, m_max_area_ratio)) { + if (poly.is_empty ()) { + // ignore empty polygons + } else if (db::suggest_split_polygon (poly, m_max_vertex_count, m_max_area_ratio)) { std::vector split_polygons; db::split_polygon (poly, split_polygons); diff --git a/src/db/db/dbPolygon.h b/src/db/db/dbPolygon.h index 134412e8e..b56a36271 100644 --- a/src/db/db/dbPolygon.h +++ b/src/db/db/dbPolygon.h @@ -1771,6 +1771,14 @@ public: return true; } + /** + * @brief Returns a value indicating that the polygon is an empty one + */ + bool is_empty () const + { + return m_ctrs.size () == size_t (1) && m_ctrs[0].size () == 0; + } + /** * @brief Returns the number of points in the polygon */ @@ -1879,6 +1887,7 @@ public: for (typename contour_list_type::iterator h = m_ctrs.begin (); h != m_ctrs.end (); ++h) { h->transform (db::unit_trans (), true /*compress*/, remove_reflected); } + m_bbox = m_ctrs [0].bbox (); return *this; } @@ -2804,6 +2813,7 @@ public: { // compress the polygon by employing the transform method m_hull.transform (db::unit_trans (), true, remove_reflected); + m_bbox = m_hull.bbox (); return *this; } @@ -3022,6 +3032,14 @@ public: return m_hull.is_halfmanhattan (); } + /** + * @brief Returns a value indicating that the polygon is an empty one + */ + bool is_empty () const + { + return m_hull.size () == 0; + } + /** * @brief The number of holes * diff --git a/src/db/db/dbRegionLocalOperations.cc b/src/db/db/dbRegionLocalOperations.cc index 48d974563..64849b712 100644 --- a/src/db/db/dbRegionLocalOperations.cc +++ b/src/db/db/dbRegionLocalOperations.cc @@ -1618,18 +1618,34 @@ bool_and_or_not_local_operation::do_compute_local (db::Layout *layou } } + db::polygon_ref_generator pr (layout, result); + db::PolygonSplitter splitter (pr, proc->area_ratio (), proc->max_vertex_count ()); + for (auto i = interactions.begin (); i != interactions.end (); ++i) { const TR &subject = interactions.subject_shape (i->first); if (others.find (subject) != others.end ()) { + + // shortcut (and: keep, not: drop) + // Note that we still normalize and split the polygon, so we get a uniform + // behavior. if (m_is_and) { - result.insert (subject); + db::Polygon poly; + subject.instantiate (poly); + splitter.put (poly); } + } else if (i->second.empty ()) { + // shortcut (not: keep, and: drop) + // Note that we still normalize and split the polygon, so we get a uniform + // behavior. if (! m_is_and) { - result.insert (subject); + db::Polygon poly; + subject.instantiate (poly); + splitter.put (poly); } + } else { for (auto e = subject.begin_edge (); ! e.at_end(); ++e) { ep.insert (*e, p1); @@ -1649,8 +1665,6 @@ bool_and_or_not_local_operation::do_compute_local (db::Layout *layou } db::BooleanOp op (m_is_and ? db::BooleanOp::And : db::BooleanOp::ANotB); - db::polygon_ref_generator pr (layout, result); - db::PolygonSplitter splitter (pr, proc->area_ratio (), proc->max_vertex_count ()); db::PolygonGenerator pg (splitter, true, true); ep.set_base_verbosity (50); ep.process (pg, op); diff --git a/src/db/unit_tests/dbPolygonTests.cc b/src/db/unit_tests/dbPolygonTests.cc index c9e277d77..dc518c7bd 100644 --- a/src/db/unit_tests/dbPolygonTests.cc +++ b/src/db/unit_tests/dbPolygonTests.cc @@ -69,6 +69,7 @@ TEST(1) EXPECT_EQ (empty == p, true); EXPECT_EQ (p.is_box (), false); + EXPECT_EQ (p.is_empty (), true); std::vector c1, c2, c3; c1.push_back (db::Point (0, 0)); @@ -76,6 +77,7 @@ TEST(1) c1.push_back (db::Point (100, 1000)); c1.push_back (db::Point (100, 0)); p.assign_hull (c1.begin (), c1.end ()); + EXPECT_EQ (p.is_empty (), false); b = p.box (); EXPECT_EQ (p.holes (), size_t (0)); EXPECT_EQ (p.area (), 1000*100); @@ -1404,3 +1406,30 @@ TEST(28) db::Polygon b (db::Box (-1000000000, -1000000000, 1000000000, 1000000000)); EXPECT_EQ (b.perimeter (), 8000000000.0); } + +TEST(29) +{ + // Degenerated boxes and compress + + db::Polygon b (db::Box (10, 20, 10, 20)); + EXPECT_EQ (b.is_empty (), false); + EXPECT_EQ (b == db::Polygon (), false); + EXPECT_EQ (b.to_string (), "(10,20;10,20;10,20;10,20)"); + EXPECT_EQ (double (b.area ()), 0.0); + + b.compress (true); + + EXPECT_EQ (b.is_empty (), true); + EXPECT_EQ (b == db::Polygon (), true); + + db::SimplePolygon sb (db::Box (10, 20, 10, 20)); + EXPECT_EQ (sb.is_empty (), false); + EXPECT_EQ (sb == db::SimplePolygon (), false); + EXPECT_EQ (sb.to_string (), "(10,20;10,20;10,20;10,20)"); + EXPECT_EQ (double (sb.area ()), 0.0); + + sb.compress (true); + + EXPECT_EQ (sb.is_empty (), true); + EXPECT_EQ (sb == db::SimplePolygon (), true); +} diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index f88392542..c618af869 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -1694,6 +1694,16 @@ TEST(93d_withAngle) run_test (_this, "93", true); } +TEST(94_texts_in_region_xor) +{ + run_test (_this, "94", false); +} + +TEST(94d_texts_in_region_xor) +{ + run_test (_this, "94", true); +} + TEST(100_edge_interaction_with_count) { run_test (_this, "100", false); diff --git a/testdata/drc/drcSimpleTests_94.drc b/testdata/drc/drcSimpleTests_94.drc new file mode 100644 index 000000000..5809b64af --- /dev/null +++ b/testdata/drc/drcSimpleTests_94.drc @@ -0,0 +1,26 @@ + +source $drc_test_source +target $drc_test_target + +if $drc_test_deep + deep +end + +l1 = input(1, 0) +l2 = input(2, 0) + +l1.output(1, 0) +l2.output(2, 0) + +x = l1 ^ l2 + +# we detect point-like polygons here by explicitly +# iterating. The enlargement makes sure, we have something +# to write to the output layout. +boxes = x.data.each.collect do |p| + RBA::Box::new(p.bbox.enlarged(10, 10)) +end +x.data = RBA::Region::new(boxes) + +x.output(10, 0) + diff --git a/testdata/drc/drcSimpleTests_94.gds b/testdata/drc/drcSimpleTests_94.gds new file mode 100644 index 0000000000000000000000000000000000000000..7e3a7cd5206cda09c06762097b7c95bc20b1b6ba GIT binary patch literal 606 zcmZQzV_;&6V31*CVt>iN!XU*U#=yY9gUn{&U}E#}bYfr-VP>^+>@@d2w)}&o%MSeo zv!g;7WLWX&V`B^P4`5(m;b353<7HxCWMJcC0-CNP!2JLJe-#D>rUnSjybUPY#J~Wf zVd6{;3@m~`{XB5}Vj%r&;*1O&3_KuH{!UZ{lq9~Xmg;@F#DJ57bSlHWG2>yXwkwQ}V z0sezUy8J*~=eS#M*POv>j=7n=nc0JZ$ORH5^$8-h(S$)Ju*ZtwK{ zWxC%@cb*>?;sjd;Bi3Sv6|BRZL+v{`SP*=`+PbW9a z_s!`=a{M|Q^buFK_>Ae_-2+h#FuTI z{q@xU{Q{^jm?P7pqFhAR0J}KV{xap!#=XPvy7AclF!zRAueud+h|hG?{C}y@>jg hIiCx3yo`cgLO=UdN!F_j>E|!w(D5qC{8grn@Cy^pZF2ws literal 0 HcmV?d00001 From 5efcf836402770b3b446d6c2d7efb3716951198e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 26 Apr 2025 23:24:26 +0200 Subject: [PATCH 168/392] Bugfix: Deep mode XOR needs to maintain the layout origin of the first argument even if it is empty --- src/db/db/dbDeepRegion.cc | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index a86e2ef4a..aaa6e5fb3 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -1054,12 +1054,7 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const { const DeepRegion *other_deep = dynamic_cast (other.delegate ()); - if (empty ()) { - - // Nothing to do - return other.delegate ()->clone (); - - } else if (other.empty ()) { + if (other.empty ()) { // Nothing to do return clone (); @@ -1068,6 +1063,18 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const return AsIfFlatRegion::xor_with (other, property_constraint); + } else if (empty ()) { + + // Nothing to do, but to maintain the normal behavior, we have to map the other + // input to our layout if neccessary + if (&other_deep->deep_layer ().layout () == &deep_layer ().layout ()) { + return other.delegate ()->clone (); + } else { + std::unique_ptr other_deep_mapped (dynamic_cast (clone ())); + other_deep_mapped->deep_layer ().add_from (other_deep->deep_layer ()); + return other_deep_mapped.release (); + } + } else if (other_deep->deep_layer () == deep_layer () && pc_skip (property_constraint)) { return new DeepRegion (deep_layer ().derived ()); From 5077b22963f70179aadc31e36a30e8e3faf9800e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 27 Apr 2025 00:30:50 +0200 Subject: [PATCH 169/392] Bugfix: deep, empty layers still need a layout attached --- src/buddies/src/bd/strmxor.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 26cf4e3f9..1a5965e83 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -848,13 +848,17 @@ public: if (m_la >= 0) { ri_a = db::RecursiveShapeIterator (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), m_la); - ri_a.set_for_merged_input (true); + } else { + ri_a = db::RecursiveShapeIterator (*mp_xor_data->layout_a, mp_xor_data->layout_a->cell (mp_xor_data->cell_a), std::vector ()); } + ri_a.set_for_merged_input (true); if (m_lb >= 0) { ri_b = db::RecursiveShapeIterator (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), m_lb); - ri_b.set_for_merged_input (true); + } else { + ri_b = db::RecursiveShapeIterator (*mp_xor_data->layout_b, mp_xor_data->layout_b->cell (mp_xor_data->cell_b), std::vector ()); } + ri_b.set_for_merged_input (true); db::Region in_a (ri_a, worker->dss (), db::ICplxTrans (mp_xor_data->layout_a->dbu () / m_dbu)); db::Region in_b (ri_b, worker->dss (), db::ICplxTrans (mp_xor_data->layout_b->dbu () / m_dbu)); From 07eb49d482e92f71365ca85f649bc3beb08e21fa Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 27 Apr 2025 01:11:38 +0200 Subject: [PATCH 170/392] Initializing MALY plugin --- .../streamers/maly/db_plugin/dbMALY.cc | 123 ++++++++++++ src/plugins/streamers/maly/db_plugin/dbMALY.h | 62 ++++++ .../streamers/maly/db_plugin/dbMALYFormat.h | 162 ++++++++++++++++ .../streamers/maly/db_plugin/dbMALYReader.cc | 142 ++++++++++++++ .../streamers/maly/db_plugin/dbMALYReader.h | 148 ++++++++++++++ .../streamers/maly/db_plugin/db_plugin.pro | 15 ++ .../streamers/maly/db_plugin/gsiDeclDbMALY.cc | 133 +++++++++++++ .../maly/lay_plugin/MALYReaderOptionPage.ui | 183 ++++++++++++++++++ .../maly/lay_plugin/layMALYReaderPlugin.cc | 110 +++++++++++ .../maly/lay_plugin/layMALYReaderPlugin.h | 59 ++++++ .../streamers/maly/lay_plugin/lay_plugin.pro | 22 +++ src/plugins/streamers/maly/maly.pro | 10 + .../streamers/maly/unit_tests/dbMALYReader.cc | 108 +++++++++++ .../streamers/maly/unit_tests/unit_tests.pro | 19 ++ 14 files changed, 1296 insertions(+) create mode 100644 src/plugins/streamers/maly/db_plugin/dbMALY.cc create mode 100644 src/plugins/streamers/maly/db_plugin/dbMALY.h create mode 100644 src/plugins/streamers/maly/db_plugin/dbMALYFormat.h create mode 100644 src/plugins/streamers/maly/db_plugin/dbMALYReader.cc create mode 100644 src/plugins/streamers/maly/db_plugin/dbMALYReader.h create mode 100644 src/plugins/streamers/maly/db_plugin/db_plugin.pro create mode 100644 src/plugins/streamers/maly/db_plugin/gsiDeclDbMALY.cc create mode 100644 src/plugins/streamers/maly/lay_plugin/MALYReaderOptionPage.ui create mode 100644 src/plugins/streamers/maly/lay_plugin/layMALYReaderPlugin.cc create mode 100644 src/plugins/streamers/maly/lay_plugin/layMALYReaderPlugin.h create mode 100644 src/plugins/streamers/maly/lay_plugin/lay_plugin.pro create mode 100644 src/plugins/streamers/maly/maly.pro create mode 100644 src/plugins/streamers/maly/unit_tests/dbMALYReader.cc create mode 100644 src/plugins/streamers/maly/unit_tests/unit_tests.pro diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.cc b/src/plugins/streamers/maly/db_plugin/dbMALY.cc new file mode 100644 index 000000000..57d67e416 --- /dev/null +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.cc @@ -0,0 +1,123 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbMALY.h" +#include "dbMALYReader.h" +#include "dbStream.h" + +#include "tlClassRegistry.h" + +namespace db +{ + +// --------------------------------------------------------------- +// MALYDiagnostics implementation + +MALYDiagnostics::~MALYDiagnostics () +{ + // .. nothing yet .. +} + +// --------------------------------------------------------------- +// MALY format declaration + +class MALYFormatDeclaration + : public db::StreamFormatDeclaration +{ +public: + MALYFormatDeclaration () + { + // .. nothing yet .. + } + + virtual std::string format_name () const { return "MALY"; } + virtual std::string format_desc () const { return "MALY jobdeck"; } + virtual std::string format_title () const { return "MALY (MALY jobdeck format)"; } + virtual std::string file_format () const { return "MALY jobdeck files (*.maly *.MALY)"; } + + virtual bool detect (tl::InputStream &s) const + { + return false; // @@@ + } + + virtual ReaderBase *create_reader (tl::InputStream &s) const + { + return new db::MALYReader (s); + } + + virtual WriterBase *create_writer () const + { + return 0; + // @@@ return new db::MALYWriter (); + } + + virtual bool can_read () const + { + return true; + } + + virtual bool can_write () const + { + return false; + // @@@ return true; + } + + virtual tl::XMLElementBase *xml_reader_options_element () const + { + return new db::ReaderOptionsXMLElement ("mag", + tl::make_member (&db::MALYReaderOptions::dbu, "dbu") + /* @@@ + tl::make_member (&db::MALYReaderOptions::lambda, "lambda") + + tl::make_member (&db::MALYReaderOptions::layer_map, "layer-map") + + tl::make_member (&db::MALYReaderOptions::create_other_layers, "create-other-layers") + + tl::make_member (&db::MALYReaderOptions::keep_layer_names, "keep-layer-names") + + tl::make_member (&db::MALYReaderOptions::merge, "merge") + + tl::make_element, db::MALYReaderOptions> (&db::MALYReaderOptions::lib_paths, "lib-paths", + tl::make_member::const_iterator, std::vector > (&std::vector::begin, &std::vector::end, &std::vector::push_back, "lib-path") + ) + */ + ); + } + + /* @@@ + virtual tl::XMLElementBase *xml_writer_options_element () const + { + return new db::WriterOptionsXMLElement ("mag", + tl::make_member (&db::MALYWriterOptions::lambda, "lambda") + + tl::make_member (&db::MALYWriterOptions::tech, "tech") + + tl::make_member (&db::MALYWriterOptions::write_timestamp, "write-timestamp") + ); + } + @@@ */ +}; + +// NOTE: Because MALY has such a high degree of syntactic freedom, the detection is somewhat +// fuzzy: do MALY at the very end of the detection chain +static tl::RegisteredClass reader_decl (new MALYFormatDeclaration (), 2300, "MALY"); + +// provide a symbol to force linking against +int force_link_MALY = 0; + +} + + diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.h b/src/plugins/streamers/maly/db_plugin/dbMALY.h new file mode 100644 index 000000000..acd5f1777 --- /dev/null +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.h @@ -0,0 +1,62 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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_dbMALY +#define HDR_dbMALY + +#include "dbPoint.h" + +#include "tlException.h" +#include "tlInternational.h" +#include "tlString.h" +#include "tlAssert.h" + +#include +#include + +namespace db +{ + +/** + * @brief The diagnostics interface for reporting problems in the reader or writer + */ +class MALYDiagnostics +{ +public: + virtual ~MALYDiagnostics (); + + /** + * @brief Issue an error with positional information + */ + virtual void error (const std::string &txt) = 0; + + /** + * @brief Issue a warning with positional information + */ + virtual void warn (const std::string &txt, int warn_level) = 0; +}; + +} + +#endif + diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYFormat.h b/src/plugins/streamers/maly/db_plugin/dbMALYFormat.h new file mode 100644 index 000000000..e2e47b8cb --- /dev/null +++ b/src/plugins/streamers/maly/db_plugin/dbMALYFormat.h @@ -0,0 +1,162 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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_dbMALYFormat +#define HDR_dbMALYFormat + +#include "dbSaveLayoutOptions.h" +#include "dbLoadLayoutOptions.h" +#include "dbPluginCommon.h" + +namespace db +{ + +/** + * @brief Structure that holds the MALY specific options for the reader + * NOTE: this structure is non-public linkage by intention. This way it's instantiated + * in all compile units and the shared object does not need to be linked. + */ +class DB_PLUGIN_PUBLIC MALYReaderOptions + : public FormatSpecificReaderOptions +{ +public: + /** + * @brief The constructor + */ + MALYReaderOptions () + : dbu (0.001), + create_other_layers (true) + { + // .. nothing yet .. + } + + /** + * @brief Specify the database unit to produce + * + * Specify the database unit which the resulting layout will receive. + */ + double dbu; + + /** + * @brief Specifies a layer mapping + * + * If a layer mapping is specified, only the given layers are read. + * Otherwise, all layers are read. + * Setting "create_other_layers" to true will make the reader + * create other layers for all layers not given in the layer map. + * Setting an empty layer map and create_other_layers to true effectively + * enables all layers for reading. + */ + db::LayerMap layer_map; + + /** + * @brief A flag indicating that a new layers shall be created + * + * If this flag is set to true, layers not listed in the layer map a created + * too. + */ + bool create_other_layers; + + /** + * @brief Implementation of FormatSpecificReaderOptions + */ + virtual FormatSpecificReaderOptions *clone () const + { + return new MALYReaderOptions (*this); + } + + /** + * @brief Implementation of FormatSpecificReaderOptions + */ + virtual const std::string &format_name () const + { + static const std::string n ("MALY"); + return n; + } +}; + +#if 0 // @@@ +/** + * @brief Structure that holds the MALY specific options for the Writer + * NOTE: this structure is non-public linkage by intention. This way it's instantiated + * in all compile units and the shared object does not need to be linked. + */ +class DB_PLUGIN_PUBLIC MALYWriterOptions + : public FormatSpecificWriterOptions +{ +public: + /** + * @brief The constructor + */ + MALYWriterOptions () + : lambda (0.0), write_timestamp (true) + { + // .. nothing yet .. + } + + /** + * @brief Specifies the lambda value for writing + * + * The lambda value is the basic scaling parameter. + * If this value is set to 0 or negative, the lambda value stored in the layout + * is used (meta data "lambda"). + */ + double lambda; + + /** + * @brief Specifies the technology value for writing Magic files + * + * If this value is set an empty string, the technology store in the layout's + * "technology" meta data is used. + */ + std::string tech; + + /** + * @brief A value indicating whether the real (true) or fake (false) timestamp is written + * + * A fake, static timestamp is useful for comparing files. + */ + bool write_timestamp; + + /** + * @brief Implementation of FormatSpecificWriterOptions + */ + virtual FormatSpecificWriterOptions *clone () const + { + return new MALYWriterOptions (*this); + } + + /** + * @brief Implementation of FormatSpecificWriterOptions + */ + virtual const std::string &format_name () const + { + static std::string n ("MALY"); + return n; + } +}; +#endif + +} + +#endif + diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc new file mode 100644 index 000000000..1d550ca9e --- /dev/null +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -0,0 +1,142 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbMALYReader.h" +#include "dbStream.h" +#include "dbObjectWithProperties.h" +#include "dbArray.h" +#include "dbStatic.h" +#include "dbShapeProcessor.h" +#include "dbTechnology.h" + +#include "tlException.h" +#include "tlString.h" +#include "tlClassRegistry.h" +#include "tlFileUtils.h" +#include "tlUri.h" + +#include +#include + +namespace db +{ + +// --------------------------------------------------------------- +// MALYReader + + +MALYReader::MALYReader (tl::InputStream &s) + : m_stream (s), + m_progress (tl::to_string (tr ("Reading MALY file")), 1000), + m_dbu (0.001) +{ + m_progress.set_format (tl::to_string (tr ("%.0fk lines"))); + m_progress.set_format_unit (1000.0); + m_progress.set_unit (100000.0); +} + +MALYReader::~MALYReader () +{ + // .. nothing yet .. +} + +const LayerMap & +MALYReader::read (db::Layout &layout) +{ + return read (layout, db::LoadLayoutOptions ()); +} + +const LayerMap & +MALYReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) +{ + init (options); + + prepare_layers (layout); + + // @@@ + + finish_layers (layout); + return layer_map_out (); +} + +void +MALYReader::error (const std::string &msg) +{ + throw MALYReaderException (msg, m_stream.line_number (), m_stream.source ()); +} + +void +MALYReader::warn (const std::string &msg, int wl) +{ + if (warn_level () < wl) { + return; + } + + if (first_warning ()) { + tl::warn << tl::sprintf (tl::to_string (tr ("In file %s:")), m_stream.source ()); + } + + int ws = compress_warning (msg); + if (ws < 0) { + tl::warn << msg + << tl::to_string (tr (" (line=")) << m_stream.line_number () + << tl::to_string (tr (", file=")) << m_stream.source () + << ")"; + } else if (ws == 0) { + tl::warn << tl::to_string (tr ("... further warnings of this kind are not shown")); + } +} + +std::string +MALYReader::resolve_path (const std::string &path) +{ + tl::URI path_uri (path); + + if (tl::is_absolute (path_uri.path ())) { + + return path_uri.to_string (); + + } else { + + tl::URI source_uri (m_stream.source ()); + source_uri.set_path (tl::dirname (source_uri.path ())); + return source_uri.resolved (tl::URI (path)).to_string (); + + } +} + +void +MALYReader::do_read (db::Layout &layout, db::cell_index_type cell_index, tl::TextInputStream &stream) +{ + try { + + // @@@ + + } catch (tl::Exception &ex) { + error (ex.msg ()); + } +} + +} + diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h new file mode 100644 index 000000000..f60f7ebb8 --- /dev/null +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h @@ -0,0 +1,148 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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_dbMALYReader +#define HDR_dbMALYReader + +#include "dbPluginCommon.h" +#include "dbNamedLayerReader.h" +#include "dbLayout.h" +#include "dbMALY.h" +#include "dbMALYFormat.h" +#include "dbStreamLayers.h" +#include "dbPropertiesRepository.h" + +#include "tlException.h" +#include "tlInternational.h" +#include "tlProgress.h" +#include "tlString.h" +#include "tlStream.h" + +#include +#include + +namespace db +{ + +class Technology; + +/** + * @brief Generic base class of MALY reader exceptions + */ +class DB_PLUGIN_PUBLIC MALYReaderException + : public ReaderException +{ +public: + MALYReaderException (const std::string &msg, size_t l, const std::string &file) + : ReaderException (tl::sprintf (tl::to_string (tr ("%s (line=%ld, file=%s)")), msg, l, file)) + { } +}; + +/** + * @brief The MALY format stream reader + */ +class DB_PLUGIN_PUBLIC MALYReader + : public NamedLayerReader, + public MALYDiagnostics +{ +public: + typedef std::vector property_value_list; + + /** + * @brief Construct a stream reader object + * + * @param s The stream delegate from which to read stream data from + */ + MALYReader (tl::InputStream &s); + + /** + * @brief Destructor + */ + ~MALYReader (); + + /** + * @brief The basic read method + * + * This method will read the stream data and translate this to + * insert calls into the layout object. This will not do much + * on the layout object beside inserting the objects. + * A set of options can be specified with the LoadLayoutOptions + * object. + * The returned map will contain all layers, the passed + * ones and the newly created ones. + * + * @param layout The layout object to write to + * @param map The LayerMap object + * @param create true, if new layers should be created + * @return The LayerMap object that tells where which layer was loaded + */ + virtual const LayerMap &read (db::Layout &layout, const LoadLayoutOptions &options); + + /** + * @brief The basic read method (without mapping) + * + * This method will read the stream data and translate this to + * insert calls into the layout object. This will not do much + * on the layout object beside inserting the objects. + * This version will read all input layers and return a map + * which tells which MALY layer has been read into which logical + * layer. + * + * @param layout The layout object to write to + * @return The LayerMap object + */ + virtual const LayerMap &read (db::Layout &layout); + + /** + * @brief Format + */ + virtual const char *format () const { return "MALY"; } + + /** + * @brief Issue an error with positional information + * + * Reimplements MALYDiagnostics + */ + virtual void error (const std::string &txt); + + /** + * @brief Issue a warning with positional information + * + * Reimplements MALYDiagnostics + */ + virtual void warn (const std::string &txt, int wl = 1); + +private: + tl::TextInputStream m_stream; + tl::AbsoluteProgress m_progress; + double m_dbu; + + void do_read (db::Layout &layout, db::cell_index_type to_cell, tl::TextInputStream &stream); + std::string resolve_path(const std::string &path); +}; + +} + +#endif + diff --git a/src/plugins/streamers/maly/db_plugin/db_plugin.pro b/src/plugins/streamers/maly/db_plugin/db_plugin.pro new file mode 100644 index 000000000..60b947560 --- /dev/null +++ b/src/plugins/streamers/maly/db_plugin/db_plugin.pro @@ -0,0 +1,15 @@ + +TARGET = maly +DESTDIR = $$OUT_PWD/../../../../db_plugins + +include($$PWD/../../../db_plugin.pri) + +HEADERS = \ + dbMALY.h \ + dbMALYReader.h \ + dbMALYFormat.h \ + +SOURCES = \ + dbMALY.cc \ + dbMALYReader.cc \ + gsiDeclDbMALY.cc \ diff --git a/src/plugins/streamers/maly/db_plugin/gsiDeclDbMALY.cc b/src/plugins/streamers/maly/db_plugin/gsiDeclDbMALY.cc new file mode 100644 index 000000000..c9ebcd37e --- /dev/null +++ b/src/plugins/streamers/maly/db_plugin/gsiDeclDbMALY.cc @@ -0,0 +1,133 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbMALY.h" +#include "dbMALYReader.h" +#include "dbLoadLayoutOptions.h" +#include "dbSaveLayoutOptions.h" +#include "gsiDecl.h" + +namespace gsi +{ + +// --------------------------------------------------------------- +// gsi Implementation of specific methods + +static void set_maly_dbu (db::LoadLayoutOptions *options, double dbu) +{ + options->get_options ().dbu = dbu; +} + +static double get_maly_dbu (const db::LoadLayoutOptions *options) +{ + return options->get_options ().dbu; +} + +static void set_layer_map (db::LoadLayoutOptions *options, const db::LayerMap &lm, bool f) +{ + options->get_options ().layer_map = lm; + options->get_options ().create_other_layers = f; +} + +static void set_layer_map1 (db::LoadLayoutOptions *options, const db::LayerMap &lm) +{ + options->get_options ().layer_map = lm; +} + +static db::LayerMap &get_layer_map (db::LoadLayoutOptions *options) +{ + return options->get_options ().layer_map; +} + +static void select_all_layers (db::LoadLayoutOptions *options) +{ + options->get_options ().layer_map = db::LayerMap (); + options->get_options ().create_other_layers = true; +} + +static bool create_other_layers (const db::LoadLayoutOptions *options) +{ + return options->get_options ().create_other_layers; +} + +static void set_create_other_layers (db::LoadLayoutOptions *options, bool l) +{ + options->get_options ().create_other_layers = l; +} + +// extend lay::LoadLayoutOptions with the MALY options +static +gsi::ClassExt maly_reader_options ( + gsi::method_ext ("maly_set_layer_map", &set_layer_map, gsi::arg ("map"), gsi::arg ("create_other_layers"), + "@brief Sets the layer map\n" + "This sets a layer mapping for the reader. The layer map allows selection and translation of the original layers, for example to assign layer/datatype numbers to the named layers.\n" + "@param map The layer map to set.\n" + "@param create_other_layers The flag indicating whether other layers will be created as well. Set to false to read only the layers in the layer map.\n" + "\n" + "This method has been added in version 0.26.2." + ) + + gsi::method_ext ("maly_layer_map=", &set_layer_map1, gsi::arg ("map"), + "@brief Sets the layer map\n" + "This sets a layer mapping for the reader. Unlike \\maly_set_layer_map, the 'create_other_layers' flag is not changed.\n" + "@param map The layer map to set.\n" + "\n" + "This method has been added in version 0.26.2." + ) + + gsi::method_ext ("maly_select_all_layers", &select_all_layers, + "@brief Selects all layers and disables the layer map\n" + "\n" + "This disables any layer map and enables reading of all layers.\n" + "New layers will be created when required.\n" + "\n" + "This method has been added in version 0.26.2." + ) + + gsi::method_ext ("maly_layer_map", &get_layer_map, + "@brief Gets the layer map\n" + "@return A reference to the layer map\n" + "\n" + "This method has been added in version 0.26.2." + ) + + gsi::method_ext ("maly_create_other_layers?", &create_other_layers, + "@brief Gets a value indicating whether other layers shall be created\n" + "@return True, if other layers will be created.\n" + "This attribute acts together with a layer map (see \\maly_layer_map=). Layers not listed in this map are created as well when " + "\\maly_create_other_layers? is true. Otherwise they are ignored.\n" + "\n" + "This method has been added in version 0.26.2." + ) + + gsi::method_ext ("maly_create_other_layers=", &set_create_other_layers, gsi::arg ("create"), + "@brief Specifies whether other layers shall be created\n" + "@param create True, if other layers will be created.\n" + "See \\maly_create_other_layers? for a description of this attribute.\n" + "\n" + "This method has been added in version 0.26.2." + ) + + gsi::method_ext ("maly_dbu", &get_maly_dbu, + "@brief Specifies the database unit which the reader uses and produces\n" + "See \\maly_dbu= method for a description of this property.\n" + "\nThis property has been added in version 0.26.2.\n" + ), + "" +); + +} + diff --git a/src/plugins/streamers/maly/lay_plugin/MALYReaderOptionPage.ui b/src/plugins/streamers/maly/lay_plugin/MALYReaderOptionPage.ui new file mode 100644 index 000000000..46ec332d2 --- /dev/null +++ b/src/plugins/streamers/maly/lay_plugin/MALYReaderOptionPage.ui @@ -0,0 +1,183 @@ + + + MALYReaderOptionPage + + + + 0 + 0 + 584 + 530 + + + + Form + + + + + + Input Options + + + + 9 + + + 9 + + + 9 + + + 9 + + + 6 + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Micron + + + + + + + Database unit + + + + + + + This is the database unit of the resulting layout. Mask pattern with a different grid are adapted to this database unit through scaling. + + + true + + + + + + + + + + + 1 + 1 + + + + + + + Layer Subset And Layer Mapping + + + false + + + + 9 + + + 9 + + + 9 + + + 9 + + + 6 + + + + + Read all layers (additionally to the ones in the mapping table) + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + + + + + + + + lay::LayerMappingWidget + QFrame +

layLayerMappingWidget.h
+ 1 + + enable_all_layers(bool) + + + + + dbu_le + read_all_cbx + + + + + layer_map + enable_all_layers(bool) + read_all_cbx + setChecked(bool) + + + 122 + 186 + + + 109 + 147 + + + + + diff --git a/src/plugins/streamers/maly/lay_plugin/layMALYReaderPlugin.cc b/src/plugins/streamers/maly/lay_plugin/layMALYReaderPlugin.cc new file mode 100644 index 000000000..c606f8a92 --- /dev/null +++ b/src/plugins/streamers/maly/lay_plugin/layMALYReaderPlugin.cc @@ -0,0 +1,110 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbMALY.h" +#include "dbMALYReader.h" +#include "dbLoadLayoutOptions.h" +#include "layMALYReaderPlugin.h" +#include "ui_MALYReaderOptionPage.h" +#include "gsiDecl.h" + +#include +#include + +namespace lay +{ + +// --------------------------------------------------------------- +// MALYReaderOptionPage definition and implementation + +MALYReaderOptionPage::MALYReaderOptionPage (QWidget *parent) + : StreamReaderOptionsPage (parent) +{ + mp_ui = new Ui::MALYReaderOptionPage (); + mp_ui->setupUi (this); +} + +MALYReaderOptionPage::~MALYReaderOptionPage () +{ + delete mp_ui; + mp_ui = 0; +} + +void +MALYReaderOptionPage::setup (const db::FormatSpecificReaderOptions *o, const db::Technology * /*tech*/) +{ + static const db::MALYReaderOptions default_options; + const db::MALYReaderOptions *options = dynamic_cast (o); + if (!options) { + options = &default_options; + } + + mp_ui->dbu_le->setText (tl::to_qstring (tl::to_string (options->dbu))); + mp_ui->layer_map->set_layer_map (options->layer_map); + mp_ui->read_all_cbx->setChecked (options->create_other_layers); +} + +void +MALYReaderOptionPage::commit (db::FormatSpecificReaderOptions *o, const db::Technology * /*tech*/) +{ + db::MALYReaderOptions *options = dynamic_cast (o); + if (options) { + + tl::from_string_ext (tl::to_string (mp_ui->dbu_le->text ()), options->dbu); + if (options->dbu > 1000.0 || options->dbu < 1e-9) { + throw tl::Exception (tl::to_string (QObject::tr ("Invalid value for database unit"))); + } + + options->layer_map = mp_ui->layer_map->get_layer_map (); + options->create_other_layers = mp_ui->read_all_cbx->isChecked (); + + } +} + +// --------------------------------------------------------------- +// MALYReaderPluginDeclaration definition and implementation + +class MALYReaderPluginDeclaration + : public StreamReaderPluginDeclaration +{ +public: + MALYReaderPluginDeclaration () + : StreamReaderPluginDeclaration (db::MALYReaderOptions ().format_name ()) + { + // .. nothing yet .. + } + + StreamReaderOptionsPage *format_specific_options_page (QWidget *parent) const + { + return new MALYReaderOptionPage (parent); + } + + db::FormatSpecificReaderOptions *create_specific_options () const + { + return new db::MALYReaderOptions (); + } +}; + +static tl::RegisteredClass plugin_decl (new lay::MALYReaderPluginDeclaration (), 10000, "MALYReader"); + +} + diff --git a/src/plugins/streamers/maly/lay_plugin/layMALYReaderPlugin.h b/src/plugins/streamers/maly/lay_plugin/layMALYReaderPlugin.h new file mode 100644 index 000000000..9de96c41a --- /dev/null +++ b/src/plugins/streamers/maly/lay_plugin/layMALYReaderPlugin.h @@ -0,0 +1,59 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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_layMALYReaderPlugin_h +#define HDR_layMALYReaderPlugin_h + +#include "layStream.h" +#include + +namespace Ui +{ + class MALYReaderOptionPage; +} + +namespace lay +{ + +class MALYReaderOptionPage + : public StreamReaderOptionsPage +{ +Q_OBJECT + +public: + MALYReaderOptionPage (QWidget *parent); + ~MALYReaderOptionPage (); + + void setup (const db::FormatSpecificReaderOptions *options, const db::Technology *tech); + void commit (db::FormatSpecificReaderOptions *options, const db::Technology *tech); + +private: + Ui::MALYReaderOptionPage *mp_ui; +}; + +} + +#endif + + diff --git a/src/plugins/streamers/maly/lay_plugin/lay_plugin.pro b/src/plugins/streamers/maly/lay_plugin/lay_plugin.pro new file mode 100644 index 000000000..0677a2552 --- /dev/null +++ b/src/plugins/streamers/maly/lay_plugin/lay_plugin.pro @@ -0,0 +1,22 @@ + +TARGET = maly_ui +DESTDIR = $$OUT_PWD/../../../../lay_plugins + +include($$PWD/../../../lay_plugin.pri) + +INCLUDEPATH += $$PWD/../db_plugin +DEPENDPATH += $$PWD/../db_plugin +LIBS += -L$$DESTDIR/../db_plugins -lmaly + +!isEmpty(RPATH) { + QMAKE_RPATHDIR += $$RPATH/db_plugins +} + +HEADERS = \ + layMALYReaderPlugin.h \ + +SOURCES = \ + layMALYReaderPlugin.cc \ + +FORMS = \ + MALYReaderOptionPage.ui \ diff --git a/src/plugins/streamers/maly/maly.pro b/src/plugins/streamers/maly/maly.pro new file mode 100644 index 000000000..0a2501ae9 --- /dev/null +++ b/src/plugins/streamers/maly/maly.pro @@ -0,0 +1,10 @@ + +TEMPLATE = subdirs + +SUBDIRS = db_plugin unit_tests +unit_tests.depends += db_plugin + +!equals(HAVE_QT, "0") { + SUBDIRS += lay_plugin + lay_plugin.depends += db_plugin +} diff --git a/src/plugins/streamers/maly/unit_tests/dbMALYReader.cc b/src/plugins/streamers/maly/unit_tests/dbMALYReader.cc new file mode 100644 index 000000000..d43dbe0cc --- /dev/null +++ b/src/plugins/streamers/maly/unit_tests/dbMALYReader.cc @@ -0,0 +1,108 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "dbMALYReader.h" +#include "dbLayoutDiff.h" +#include "dbWriter.h" +#include "tlUnitTest.h" + +#include + +static void run_test (tl::TestBase *_this, const std::string &base, const char *file, const char *file_au, const char *map = 0, double lambda = 0.1, double dbu = 0.001, const std::vector *lib_paths = 0) +{ + db::MALYReaderOptions *opt = new db::MALYReaderOptions(); + opt->dbu = dbu; + + db::LayerMap lm; + if (map) { + unsigned int ln = 0; + tl::Extractor ex (map); + while (! ex.at_end ()) { + std::string n; + int l; + ex.read_word_or_quoted (n); + ex.test (":"); + ex.read (l); + ex.test (","); + lm.map (n, ln++, db::LayerProperties (l, 0)); + } + opt->layer_map = lm; + opt->create_other_layers = true; + } + + db::LoadLayoutOptions options; + options.set_options (opt); + + db::Manager m (false); + db::Layout layout (&m), layout2 (&m), layout2_mag (&m), layout_au (&m); + + { + std::string fn (base); + fn += "/maly/"; + fn += file; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (layout, options); + } + + std::string tc_name = layout.cell_name (*layout.begin_top_down ()); + + // normalize the layout by writing to OASIS and reading from .. + + std::string tmp_oas_file = _this->tmp_file (tl::sprintf ("%s.oas", tc_name)); + std::string tmp_maly_file = _this->tmp_file (tl::sprintf ("%s.mag", tc_name)); + + { + tl::OutputStream stream (tmp_oas_file); + db::SaveLayoutOptions options; + options.set_format ("OASIS"); + db::Writer writer (options); + writer.write (layout, stream); + } + + { + tl::InputStream stream (tmp_oas_file); + db::Reader reader (stream); + reader.read (layout2); + } + + { + std::string fn (base); + fn += "/maly/"; + fn += file_au; + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (layout_au); + } + + bool equal = db::compare_layouts (layout2, layout_au, db::layout_diff::f_boxes_as_polygons | db::layout_diff::f_verbose | db::layout_diff::f_flatten_array_insts, 1); + if (! equal) { + _this->raise (tl::sprintf ("Compare failed after reading - see %s vs %s\n", tmp_oas_file, file_au)); + } +} + +TEST(1) +{ + run_test (_this, tl::testdata (), "MALY_TEST.maly", "mag_test_au.oas"); +} + diff --git a/src/plugins/streamers/maly/unit_tests/unit_tests.pro b/src/plugins/streamers/maly/unit_tests/unit_tests.pro new file mode 100644 index 000000000..7a6319ee6 --- /dev/null +++ b/src/plugins/streamers/maly/unit_tests/unit_tests.pro @@ -0,0 +1,19 @@ + +DESTDIR_UT = $$OUT_PWD/../../../.. + +TARGET = maly_tests + +include($$PWD/../../../../lib_ut.pri) + +SOURCES = \ + dbMALYReader.cc \ + +INCLUDEPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common +DEPENDPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common + +LIBS += -L$$DESTDIR_UT -lklayout_db -lklayout_tl -lklayout_gsi + +PLUGINPATH = $$OUT_PWD/../../../../db_plugins +QMAKE_RPATHDIR += $$PLUGINPATH + +LIBS += -L$$PLUGINPATH -lmaly From c55a0757c17444792243ba5d84f185e4c9ea7777 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 27 Apr 2025 14:27:04 +0200 Subject: [PATCH 171/392] Preparations for 0.30.1 --- Changelog | 17 +++++++++++++++++ Changelog.Debian | 7 +++++++ version.sh | 4 ++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/Changelog b/Changelog index c4450b33d..387183e50 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,20 @@ +0.30.1 (2025-04-27): +* Bugfix: %GITHUB%/issues/2011 Some DRC bugs fixed +* Bug: %GITHUB%/issues/2014 Bug fixes in LEF/DEF reader +* Enhancement: %GITHUB%/issues/2016 Lazy evaluation of PCell also when changing guiding shape properties +* Enhancement: %GITHUB%/issues/2019 Support for Qt 6.9 +* Bugfix: %GITHUB%/issues/2020 String weak ordering issue fixed in edge processor +* Enhancement: %GITHUB%/issues/2024 Option to configure grid density +* Bugfix: %GITHUB%/issues/2025 Brackets get added in List type PCell parameter edit field +* Bugfix: %GITHUB%/issues/2026 Display is dead after opening 2.5d view +* Bugfix/Enhancement: some updates of "strmxor" tool + - strmxor was giving wrong results if cell variants are + present where one variant is covered entirely by a large shape + - parallelization now happens on a per-layer basis (same as for + XOR tool in KLayout) + - Shape count was not consistent in deep mode + - All buddy tools print total runtime with -d11 + 0.30.0 (2025-03-25): * Bug: %GITHUB%/issues/1996 More robust triangulation * Bug: %GITHUB%/issues/2002 Path to polygon conversion issue diff --git a/Changelog.Debian b/Changelog.Debian index 1d2c2b3c3..a69eb136e 100644 --- a/Changelog.Debian +++ b/Changelog.Debian @@ -1,3 +1,10 @@ +klayout (0.30.1-1) unstable; urgency=low + + * New features and bugfixes + - See changelog + + -- Matthias Köfferlein Sun, 27 Apr 2025 14:26:50 +0200 + klayout (0.30.0-1) unstable; urgency=low * New features and bugfixes diff --git a/version.sh b/version.sh index 737a9f907..3b9bf8033 100644 --- a/version.sh +++ b/version.sh @@ -2,10 +2,10 @@ # This script is sourced to define the main version parameters # The main version -KLAYOUT_VERSION="0.30.0" +KLAYOUT_VERSION="0.30.1" # The version used for PyPI (don't use variables here!) -KLAYOUT_PYPI_VERSION="0.30.0" +KLAYOUT_PYPI_VERSION="0.30.1" # The build date KLAYOUT_VERSION_DATE=$(date "+%Y-%m-%d") From 9aa8d79bfc56334ca8e3bed648a23bc25d2aa06c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 27 Apr 2025 14:43:53 +0200 Subject: [PATCH 172/392] Enhanced unit test for better debugging --- src/tl/unit_tests/tlFileUtilsTests.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tl/unit_tests/tlFileUtilsTests.cc b/src/tl/unit_tests/tlFileUtilsTests.cc index 2eafe8bc3..2c5e210cc 100644 --- a/src/tl/unit_tests/tlFileUtilsTests.cc +++ b/src/tl/unit_tests/tlFileUtilsTests.cc @@ -1053,7 +1053,7 @@ TEST (25) std::sort (res.begin (), res.end ()); std::sort (au.begin (), au.end ()); - EXPECT_EQ (res == au, true); + EXPECT_EQ (tl::join (res, "\n"), tl::join (au, "\n")); res = tl::glob_expand (tl::combine_path (tl::combine_path (p, "**"), "*.txt")); au.clear (); @@ -1066,7 +1066,7 @@ TEST (25) std::sort (res.begin (), res.end ()); std::sort (au.begin (), au.end ()); - EXPECT_EQ (res == au, true); + EXPECT_EQ (tl::join (res, "\n"), tl::join (au, "\n")); res = tl::glob_expand (tl::combine_path (tl::combine_path (p, "**"), "*2.txt")); au.clear (); @@ -1075,7 +1075,7 @@ TEST (25) std::sort (res.begin (), res.end ()); std::sort (au.begin (), au.end ()); - EXPECT_EQ (res == au, true); + EXPECT_EQ (tl::join (res, "\n"), tl::join (au, "\n")); res = tl::glob_expand (tl::combine_path (tl::combine_path (tl::combine_path (p, "**"), "a"), "*2.txt")); au.clear (); @@ -1083,6 +1083,6 @@ TEST (25) std::sort (res.begin (), res.end ()); std::sort (au.begin (), au.end ()); - EXPECT_EQ (res == au, true); + EXPECT_EQ (tl::join (res, "\n"), tl::join (au, "\n")); } From 43310e7f49d3100599055f6cfd2c5b65f3c2670c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 27 Apr 2025 16:07:01 +0200 Subject: [PATCH 173/392] Updating Python stubs --- src/pymod/distutils_src/klayout/dbcore.pyi | 81 +++++++++++---------- src/pymod/distutils_src/klayout/rdbcore.pyi | 8 ++ src/pymod/distutils_src/klayout/tlcore.pyi | 4 +- 3 files changed, 54 insertions(+), 39 deletions(-) diff --git a/src/pymod/distutils_src/klayout/dbcore.pyi b/src/pymod/distutils_src/klayout/dbcore.pyi index a9d4ed50c..65fc5acd7 100644 --- a/src/pymod/distutils_src/klayout/dbcore.pyi +++ b/src/pymod/distutils_src/klayout/dbcore.pyi @@ -11620,7 +11620,7 @@ class DEdgePair: ... ... -class DEdgePairWithProperties(EdgePair): +class DEdgePairWithProperties(DEdgePair): r""" @brief A DEdgePair object with properties attached. This class represents a combination of a DEdgePair object an user properties. User properties are stored in form of a properties ID. Convenience methods are provided to manipulate or retrieve user properties directly. @@ -11685,7 +11685,7 @@ class DEdgePairWithProperties(EdgePair): @brief Returns a string representing the polygon """ ... - def _assign(self, other: EdgePair) -> None: + def _assign(self, other: DEdgePair) -> None: r""" @brief Assigns another object to self """ @@ -15609,8 +15609,7 @@ class DText: Setter: @brief Sets the horizontal alignment - This property specifies how the text is aligned relative to the anchor point. - This property has been introduced in version 0.22 and extended to enums in 0.28. + This is the version accepting integer values. It's provided for backward compatibility. """ size: float r""" @@ -15646,8 +15645,7 @@ class DText: Setter: @brief Sets the vertical alignment - This property specifies how the text is aligned relative to the anchor point. - This property has been introduced in version 0.22 and extended to enums in 0.28. + This is the version accepting integer values. It's provided for backward compatibility. """ x: float r""" @@ -25332,13 +25330,15 @@ class EdgePairs(ShapeCollection): @brief Creates a copy of self """ ... - def __getitem__(self, n: int) -> EdgePair: + def __getitem__(self, n: int) -> Any: r""" @brief Returns the nth edge pair 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. The \each iterator is the more general approach to access the edge pairs. + + Since version 0.30.1, this method returns a \EdgePairWithProperties object. """ ... def __iadd__(self, other: EdgePairs) -> EdgePairs: @@ -29095,7 +29095,7 @@ class Edges(ShapeCollection): @brief Creates a copy of self """ ... - def __getitem__(self, n: int) -> Edge: + def __getitem__(self, n: int) -> Any: r""" @brief Returns the nth edge of the collection @@ -29103,6 +29103,8 @@ class Edges(ShapeCollection): This method returns the raw edge (not merged edges, even if merged semantics is enabled). The \each iterator is the more general approach to access the edges. + + Since version 0.30.1, this method returns an \EdgeWithProperties object. """ ... def __iadd__(self, other: Edges) -> Edges: @@ -34907,11 +34909,11 @@ class Instance: Starting with version 0.25 the displacement is of vector type. Setter: - @brief Sets the displacement vector for the 'a' axis in micrometer units + @brief Sets the displacement vector for the 'a' axis - Like \a= with an integer displacement, this method will set the displacement vector but it accepts a vector in micrometer units that is of \DVector type. The vector will be translated to database units internally. + If the instance was not an array instance before it is made one. - This method has been introduced in version 0.25. + This method has been introduced in version 0.23. Starting with version 0.25 the displacement is of vector type. """ b: Vector r""" @@ -34956,10 +34958,10 @@ class Instance: Getter: @brief Gets the basic \CellInstArray object associated with this instance reference. Setter: - @brief Changes the \CellInstArray object to the given one. - This method replaces the instance by the given CellInstArray object. + @brief Returns the basic cell instance array object by giving a micrometer unit object. + This method replaces the instance by the given CellInstArray object and it internally transformed into database units. - This method has been introduced in version 0.22 + This method has been introduced in version 0.25 """ cplx_trans: ICplxTrans r""" @@ -35455,7 +35457,7 @@ class Instance: r""" @brief Gets the layout this instance is contained in - This method has been introduced in version 0.22. + This const version of the method has been introduced in version 0.25. """ ... @overload @@ -35463,7 +35465,7 @@ class Instance: r""" @brief Gets the layout this instance is contained in - This const version of the method has been introduced in version 0.25. + This method has been introduced in version 0.22. """ ... def pcell_declaration(self) -> PCellDeclaration_Native: @@ -47706,17 +47708,17 @@ class Netlist: @overload def circuit_by_cell_index(self, cell_index: int) -> Circuit: r""" - @brief Gets the circuit object for a given cell index (const version). + @brief Gets the circuit object for a given cell index. If the cell index is not valid or no circuit is registered with this index, nil is returned. - - This constness variant has been introduced in version 0.26.8. """ ... @overload def circuit_by_cell_index(self, cell_index: int) -> Circuit: r""" - @brief Gets the circuit object for a given cell index. + @brief Gets the circuit object for a given cell index (const version). If the cell index is not valid or no circuit is registered with this index, nil is returned. + + This constness variant has been introduced in version 0.26.8. """ ... @overload @@ -47738,20 +47740,20 @@ class Netlist: @overload def circuits_by_name(self, name_pattern: str) -> List[Circuit]: r""" - @brief Gets the circuit objects for a given name filter (const version). + @brief Gets the circuit objects for a given name filter. The name filter is a glob pattern. This method will return all \Circuit objects matching the glob pattern. - - This constness variant has been introduced in version 0.26.8. + This method has been introduced in version 0.26.4. """ ... @overload def circuits_by_name(self, name_pattern: str) -> List[Circuit]: r""" - @brief Gets the circuit objects for a given name filter. + @brief Gets the circuit objects for a given name filter (const version). The name filter is a glob pattern. This method will return all \Circuit objects matching the glob pattern. - This method has been introduced in version 0.26.4. + + This constness variant has been introduced in version 0.26.8. """ ... def combine_devices(self) -> None: @@ -47996,7 +47998,7 @@ class Netlist: @overload def top_circuit(self) -> Circuit: r""" - @brief Gets the top circuit. + @brief Gets the top circuit (const version). This method will return nil, if there is no top circuit. It will raise an error, if there is more than a single top circuit. This convenience method has been added in version 0.29.5. @@ -48005,7 +48007,7 @@ class Netlist: @overload def top_circuit(self) -> Circuit: r""" - @brief Gets the top circuit (const version). + @brief Gets the top circuit. This method will return nil, if there is no top circuit. It will raise an error, if there is more than a single top circuit. This convenience method has been added in version 0.29.5. @@ -58384,7 +58386,7 @@ class Region(ShapeCollection): @brief Creates a copy of self """ ... - def __getitem__(self, n: int) -> Polygon: + def __getitem__(self, n: int) -> Any: r""" @brief Returns the nth polygon of the region @@ -58392,6 +58394,8 @@ class Region(ShapeCollection): This method returns the raw polygon (not merged polygons, even if merged semantics is enabled). The \each iterator is the more general approach to access the polygons. + + Since version 0.30.1, this method returns a \PolygonWithProperties object. """ ... def __iadd__(self, other: Region) -> Region: @@ -63169,11 +63173,10 @@ class Shape: Starting with version 0.23, this method returns nil, if the shape does not represent an edge. Setter: - @brief Replaces the shape by the given edge - This method replaces the shape by the given edge. This method can only be called for editable layouts. It does not change the user properties of the shape. - Calling this method will invalidate any iterators. It should not be called inside a loop iterating over shapes. + @brief Replaces the shape by the given edge (in micrometer units) + This method replaces the shape by the given edge, like \edge= with a \Edge argument does. This version translates the edge from micrometer units to database units internally. - This method has been introduced in version 0.22. + This method has been introduced in version 0.25. """ edge_pair: Any r""" @@ -63377,10 +63380,11 @@ class Shape: Starting with version 0.23, this method returns nil, if the shape does not represent a geometrical primitive that can be converted to a simple polygon. Setter: - @brief Replaces the shape by the given simple polygon (in micrometer units) - This method replaces the shape by the given text, like \simple_polygon= with a \SimplePolygon argument does. This version translates the polygon from micrometer units to database units internally. + @brief Replaces the shape by the given simple polygon object + This method replaces the shape by the given simple polygon object. This method can only be called for editable layouts. It does not change the user properties of the shape. + Calling this method will invalidate any iterators. It should not be called inside a loop iterating over shapes. - This method has been introduced in version 0.25. + This method has been introduced in version 0.22. """ text: Any r""" @@ -68247,8 +68251,7 @@ class Text: Setter: @brief Sets the horizontal alignment - This property specifies how the text is aligned relative to the anchor point. - This property has been introduced in version 0.22 and extended to enums in 0.28. + This is the version accepting integer values. It's provided for backward compatibility. """ size: int r""" @@ -70054,13 +70057,15 @@ class Texts(ShapeCollection): @brief Creates a copy of self """ ... - def __getitem__(self, n: int) -> Text: + def __getitem__(self, n: int) -> Any: r""" @brief Returns the nth text This method returns nil if the index is out of range. It is available for flat texts only - i.e. those for which \has_valid_texts? is true. Use \flatten to explicitly flatten an text collection. The \each iterator is the more general approach to access the texts. + + Since version 0.30.1, this method returns a \TextWithProperties object. """ ... def __iadd__(self, other: Texts) -> Texts: diff --git a/src/pymod/distutils_src/klayout/rdbcore.pyi b/src/pymod/distutils_src/klayout/rdbcore.pyi index 237cf2d15..73e6d9688 100644 --- a/src/pymod/distutils_src/klayout/rdbcore.pyi +++ b/src/pymod/distutils_src/klayout/rdbcore.pyi @@ -655,6 +655,14 @@ class RdbItem: """ ... @overload + def add_value(self, value: db.DText) -> None: + r""" + @brief Adds a text object to the values of this item + @param value The text to add. + This method has been introduced in version 0.30.1 to support text objects with properties. + """ + ... + @overload def add_value(self, value: float) -> None: r""" @brief Adds a numeric value to the values of this item diff --git a/src/pymod/distutils_src/klayout/tlcore.pyi b/src/pymod/distutils_src/klayout/tlcore.pyi index 09483e06c..fd53c5008 100644 --- a/src/pymod/distutils_src/klayout/tlcore.pyi +++ b/src/pymod/distutils_src/klayout/tlcore.pyi @@ -2908,7 +2908,9 @@ class Timer: r""" @brief Gets the current memory usage of the process in Bytes - This method has been introduced in version 0.27. + The returned value is the resident memory size on Linux and MacOS and the working set size on Windows. + + This method has been introduced in version 0.27. The value has been changed to be resident size (instead of virtual size) on Linux in version 0.30. """ ... @classmethod From f1b35d08261b3bbc9c954d8a099be8ebe55bc462 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 27 Apr 2025 16:56:46 +0200 Subject: [PATCH 174/392] WIP (MALY reader) --- src/plugins/streamers/maly/db_plugin/dbMALY.h | 201 ++++++++++++++++++ .../streamers/maly/db_plugin/gsiDeclDbMALY.cc | 27 ++- 2 files changed, 221 insertions(+), 7 deletions(-) diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.h b/src/plugins/streamers/maly/db_plugin/dbMALY.h index acd5f1777..c4f81e8f4 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALY.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.h @@ -25,6 +25,8 @@ #define HDR_dbMALY #include "dbPoint.h" +#include "dbTrans.h" +#include "dbBox.h" #include "tlException.h" #include "tlInternational.h" @@ -56,6 +58,205 @@ public: virtual void warn (const std::string &txt, int warn_level) = 0; }; +/** + * @brief A class representing a title field on a mask + */ +class MALYTitle +{ +public: + /** + * @brief Default constructor + */ + MALYTitle () + : width (0.0), height (0.0), pitch (0.0), type (String), font (Standard) + { } + + /** + * @brief The type of the title + */ + enum Type + { + String = 0, // A user-defined string + Date = 1, // The date + Serial = 2 // A serial number + }; + + /** + * @brief The font to be used + */ + enum Font + { + Standard = 0, // Standard font + Native = 1 // Native tool font + }; + + /** + * @brief The string for "String" type + */ + std::string string; + + /** + * @brief The transformation of the title + * + * The origin of the title is supposed to be in the center of + * the title field. + */ + db::DTrans transformation; + + /** + * @brief Optional font parameters: character width + */ + double width; + + /** + * @brief Optional font parameters: character height + */ + double height; + + /** + * @brief Optional font parameters: character pitch + */ + double pitch; + + /** + * @brief The type of the title + */ + Type type; + + /** + * @brief The font to be used + */ + Font font; +}; + +/** + * @brief A class representing a structure (pattern) on a mask + */ +class MALYStructure +{ + /** + * @brief Default constructor + */ + MALYStructure () + : nx (1), ny (1), dx (0.0), dy (0.0), layer (-1) + { } + + /** + * @brief The (expanded) path of the pattern file + */ + std::string path; + + /** + * @brief The name of the top cell + * If empty, the topcell is determined automatically + */ + std::string topcell; + + /** + * @brief The pattern window in the original file + */ + db::DBox size; + + /** + * @brief The transformation needed to place the original file + */ + db::DCplxTrans transformation; + + /** + * @brief The number of placements in x direction + */ + int nx; + + /** + * @brief The number of placements in y direction + */ + int ny; + + /** + * @brief The placement pitch in x direction (if nx > 1) + */ + double dx; + + /** + * @brief The placement pitch in y direction (if ny > 1) + */ + double dy; + + /** + * @brief The design name + */ + std::string dname; + + /** + * @brief The name for the mask process + */ + std::string mname; + + /** + * @brief The name for the mask tool + */ + std::string ename; + + /** + * @brief The layer used from the OASIS file + * + * A value of -1 means "all". + */ + int layer; +}; + +/** + * @brief A class representing one mask + */ +class MALYMask +{ + /** + * @brief Default constructor + */ + MALYMask () + : size_um (0.0) + { } + + /** + * @brief Size of the mask in micrometers + */ + double size_um; + + /** + * @brief Name of the mask + * + * This is also the name of the layer generated + */ + std::string name; + + /** + * @brief The list of structures + */ + std::list structures; + + /** + * @brief The list of titles + */ + std::list titles; +}; + +/** + * @brief A class representing the MALY file + */ +class MALYData +{ + /** + * @brief Default constructor + */ + MALYData () + { } + + /** + * @brief The masks defined by the file + */ + std::list masks; +}; + } #endif diff --git a/src/plugins/streamers/maly/db_plugin/gsiDeclDbMALY.cc b/src/plugins/streamers/maly/db_plugin/gsiDeclDbMALY.cc index c9ebcd37e..fa2a9cdcb 100644 --- a/src/plugins/streamers/maly/db_plugin/gsiDeclDbMALY.cc +++ b/src/plugins/streamers/maly/db_plugin/gsiDeclDbMALY.cc @@ -83,14 +83,18 @@ gsi::ClassExt maly_reader_options ( "@param map The layer map to set.\n" "@param create_other_layers The flag indicating whether other layers will be created as well. Set to false to read only the layers in the layer map.\n" "\n" - "This method has been added in version 0.26.2." + "Layer maps can also be used to map the named MALY mask layers to GDS layer/datatypes.\n" + "\n" + "This method has been added in version 0.30.2." ) + gsi::method_ext ("maly_layer_map=", &set_layer_map1, gsi::arg ("map"), "@brief Sets the layer map\n" "This sets a layer mapping for the reader. Unlike \\maly_set_layer_map, the 'create_other_layers' flag is not changed.\n" "@param map The layer map to set.\n" "\n" - "This method has been added in version 0.26.2." + "Layer maps can also be used to map the named MALY mask layers to GDS layer/datatypes.\n" + "\n" + "This method has been added in version 0.30.2." ) + gsi::method_ext ("maly_select_all_layers", &select_all_layers, "@brief Selects all layers and disables the layer map\n" @@ -98,13 +102,13 @@ gsi::ClassExt maly_reader_options ( "This disables any layer map and enables reading of all layers.\n" "New layers will be created when required.\n" "\n" - "This method has been added in version 0.26.2." + "This method has been added in version 0.30.2." ) + gsi::method_ext ("maly_layer_map", &get_layer_map, "@brief Gets the layer map\n" "@return A reference to the layer map\n" "\n" - "This method has been added in version 0.26.2." + "This method has been added in version 0.30.2." ) + gsi::method_ext ("maly_create_other_layers?", &create_other_layers, "@brief Gets a value indicating whether other layers shall be created\n" @@ -112,19 +116,28 @@ gsi::ClassExt maly_reader_options ( "This attribute acts together with a layer map (see \\maly_layer_map=). Layers not listed in this map are created as well when " "\\maly_create_other_layers? is true. Otherwise they are ignored.\n" "\n" - "This method has been added in version 0.26.2." + "This method has been added in version 0.30.2." ) + gsi::method_ext ("maly_create_other_layers=", &set_create_other_layers, gsi::arg ("create"), "@brief Specifies whether other layers shall be created\n" "@param create True, if other layers will be created.\n" "See \\maly_create_other_layers? for a description of this attribute.\n" "\n" - "This method has been added in version 0.26.2." + "This method has been added in version 0.30.2." + ) + + gsi::method_ext ("maly_dbu=", &set_maly_dbu, gsi::arg ("dbu"), + "@brief Specifies the database unit which the reader uses and produces\n" + "The database unit is the final resolution of the produced layout. This physical resolution is usually " + "defined by the layout system - GDS for example typically uses 1nm (maly_dbu=0.001).\n" + "All geometry in the MALY pattern files is brought to the database unit by scaling.\n" + "\n" + "This method has been added in version 0.30.2." ) + gsi::method_ext ("maly_dbu", &get_maly_dbu, "@brief Specifies the database unit which the reader uses and produces\n" "See \\maly_dbu= method for a description of this property.\n" - "\nThis property has been added in version 0.26.2.\n" + "\n" + "This method has been added in version 0.30.2." ), "" ); From 6e7eff95e7380f5d9544504d0fcb68ae109c7d72 Mon Sep 17 00:00:00 2001 From: klayoutmatthias Date: Sun, 27 Apr 2025 19:16:37 +0200 Subject: [PATCH 175/392] Fixed glob feature for Windows. --- src/tl/tl/tlFileUtils.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/tl/tl/tlFileUtils.cc b/src/tl/tl/tlFileUtils.cc index a3b755db0..1735d853f 100644 --- a/src/tl/tl/tlFileUtils.cc +++ b/src/tl/tl/tlFileUtils.cc @@ -458,6 +458,16 @@ static void glob_partial (const std::string &where, std::vector::co ++pfrom; } +#if defined(_WIN32) + if (where.empty ()) { + // On Windows, we cannot iterate the drives + std::string root = *pfrom; + ++pfrom; + glob_partial (root, pfrom, pto, res); + return; + } +#endif + tl::GlobPattern glob (tl::trimmed_part (*pfrom)); ++pfrom; auto entries = dir_entries (where, true, true, true); From e76be5b071256239ef89bcfcb1fd6c9df1711b98 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 27 Apr 2025 20:55:11 +0200 Subject: [PATCH 176/392] WIP --- .../streamers/maly/db_plugin/dbMALY.cc | 28 +--- src/plugins/streamers/maly/db_plugin/dbMALY.h | 3 + .../streamers/maly/db_plugin/dbMALYReader.cc | 139 ++++++++++++++++++ .../streamers/maly/db_plugin/dbMALYReader.h | 12 +- .../{dbMALYReader.cc => dbMALYReaderTests.cc} | 6 +- .../streamers/maly/unit_tests/unit_tests.pro | 2 +- testdata/maly/MALY_TEST.maly | 15 ++ 7 files changed, 176 insertions(+), 29 deletions(-) rename src/plugins/streamers/maly/unit_tests/{dbMALYReader.cc => dbMALYReaderTests.cc} (92%) create mode 100644 testdata/maly/MALY_TEST.maly diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.cc b/src/plugins/streamers/maly/db_plugin/dbMALY.cc index 57d67e416..b53b4b449 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALY.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.cc @@ -57,7 +57,8 @@ public: virtual bool detect (tl::InputStream &s) const { - return false; // @@@ + db::MALYReader reader (s); + return reader.test (); } virtual ReaderBase *create_reader (tl::InputStream &s) const @@ -68,7 +69,6 @@ public: virtual WriterBase *create_writer () const { return 0; - // @@@ return new db::MALYWriter (); } virtual bool can_read () const @@ -79,36 +79,16 @@ public: virtual bool can_write () const { return false; - // @@@ return true; } virtual tl::XMLElementBase *xml_reader_options_element () const { return new db::ReaderOptionsXMLElement ("mag", - tl::make_member (&db::MALYReaderOptions::dbu, "dbu") - /* @@@ - tl::make_member (&db::MALYReaderOptions::lambda, "lambda") + + tl::make_member (&db::MALYReaderOptions::dbu, "dbu") + tl::make_member (&db::MALYReaderOptions::layer_map, "layer-map") + - tl::make_member (&db::MALYReaderOptions::create_other_layers, "create-other-layers") + - tl::make_member (&db::MALYReaderOptions::keep_layer_names, "keep-layer-names") + - tl::make_member (&db::MALYReaderOptions::merge, "merge") + - tl::make_element, db::MALYReaderOptions> (&db::MALYReaderOptions::lib_paths, "lib-paths", - tl::make_member::const_iterator, std::vector > (&std::vector::begin, &std::vector::end, &std::vector::push_back, "lib-path") - ) - */ + tl::make_member (&db::MALYReaderOptions::create_other_layers, "create-other-layers") ); } - - /* @@@ - virtual tl::XMLElementBase *xml_writer_options_element () const - { - return new db::WriterOptionsXMLElement ("mag", - tl::make_member (&db::MALYWriterOptions::lambda, "lambda") + - tl::make_member (&db::MALYWriterOptions::tech, "tech") + - tl::make_member (&db::MALYWriterOptions::write_timestamp, "write-timestamp") - ); - } - @@@ */ }; // NOTE: Because MALY has such a high degree of syntactic freedom, the detection is somewhat diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.h b/src/plugins/streamers/maly/db_plugin/dbMALY.h index c4f81e8f4..ec904e969 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALY.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.h @@ -134,6 +134,7 @@ public: */ class MALYStructure { +public: /** * @brief Default constructor */ @@ -210,6 +211,7 @@ class MALYStructure */ class MALYMask { +public: /** * @brief Default constructor */ @@ -245,6 +247,7 @@ class MALYMask */ class MALYData { +public: /** * @brief Default constructor */ diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index 1d550ca9e..2352fe8ed 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -61,6 +61,22 @@ MALYReader::~MALYReader () // .. nothing yet .. } +bool +MALYReader::test () +{ + return true; // @@@ + try { + + std::string rec = read_record (); + + tl::Extractor ex (rec.c_str ()); + return ex.test ("BEGIN") && ex.test ("MALY"); + + } catch (...) { + return false; + } +} + const LayerMap & MALYReader::read (db::Layout &layout) { @@ -74,12 +90,135 @@ MALYReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) prepare_layers (layout); + const db::MALYReaderOptions &specific_options = options.get_options (); + m_dbu = specific_options.dbu; + + set_layer_map (specific_options.layer_map); + set_create_layers (specific_options.create_other_layers); + // @@@ set_keep_layer_names (specific_options.keep_layer_names); + set_keep_layer_names (true); + + MALYData data = read_maly_file (); + // @@@ finish_layers (layout); return layer_map_out (); } +std::string +MALYReader::read_record () +{ + while (! m_stream.at_end ()) { + std::string r = read_record_internal (); + tl::Extractor ex (r.c_str ()); + if (ex.test ("+")) { + error (tl::to_string (tr ("'+' character past first column - did you mean to continue a line?"))); + } else if (! ex.at_end ()) { + return r; + } + } + + return std::string (); +} + +std::string +MALYReader::read_record_internal () +{ + std::string rec; + + while (! m_stream.at_end ()) { + + char c = m_stream.get_char (); + + // skip comments + if (c == '/') { + char cc = m_stream.peek_char (); + if (cc == '/') { + while (! m_stream.at_end () && (c = m_stream.get_char ()) != '\n') + ; + if (m_stream.at_end ()) { + break; + } + } else if (cc == '*') { + m_stream.get_char (); // eat leading "*" + while (! m_stream.at_end () && (m_stream.get_char () != '*' || m_stream.peek_char () != '/')) + ; + if (m_stream.at_end ()) { + error (tl::to_string (tr ("/*...*/ comment not closed"))); + } + m_stream.get_char (); // eat trailing "/" + if (m_stream.at_end ()) { + break; + } + c = m_stream.get_char (); + } + } + + if (c == '\n') { + if (m_stream.peek_char () == '+') { + // continuation line + m_stream.get_char (); // eat "+" + if (m_stream.at_end ()) { + break; + } + c = m_stream.get_char (); + } else { + break; + } + } + + if (c == '"' || c == '\'') { + + rec += c; + + // skip quoted string + char quote = c; + while (! m_stream.at_end ()) { + c = m_stream.get_char (); + rec += c; + if (c == quote) { + quote = 0; + break; + } else if (c == '\\') { + if (m_stream.at_end ()) { + error (tl::to_string (tr ("Unexpected end of file inside quotee string"))); + } + c = m_stream.get_char (); + rec += c; + } else if (c == '\n') { + error (tl::to_string (tr ("Line break inside quoted string"))); + } + } + + if (quote) { + error (tl::to_string (tr ("Unexpected end of file inside quotee string"))); + } + + } else { + rec += c; + } + + } + + return rec; +} + +MALYData +MALYReader::read_maly_file () +{ + // @@@ + std::cout << "@@@ BEGIN_MALY" << std::endl; + std::string rec; + while (! (rec = read_record ()).empty ()) { + std::cout << rec << std::endl; + } + std::cout << "@@@ END_MALY" << std::endl; + // @@@ + + return MALYData (); // @@@ +} + void MALYReader::error (const std::string &msg) { diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h index f60f7ebb8..981d3419a 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h @@ -76,11 +76,18 @@ public: */ MALYReader (tl::InputStream &s); - /** + /** * @brief Destructor */ ~MALYReader (); + /** + * @brief Tests, if the stream is a valid MALY file + * + * This method can be used for the format detection + */ + bool test (); + /** * @brief The basic read method * @@ -140,6 +147,9 @@ private: void do_read (db::Layout &layout, db::cell_index_type to_cell, tl::TextInputStream &stream); std::string resolve_path(const std::string &path); + MALYData read_maly_file (); + std::string read_record (); + std::string read_record_internal (); }; } diff --git a/src/plugins/streamers/maly/unit_tests/dbMALYReader.cc b/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc similarity index 92% rename from src/plugins/streamers/maly/unit_tests/dbMALYReader.cc rename to src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc index d43dbe0cc..cb3f749c3 100644 --- a/src/plugins/streamers/maly/unit_tests/dbMALYReader.cc +++ b/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc @@ -28,7 +28,7 @@ #include -static void run_test (tl::TestBase *_this, const std::string &base, const char *file, const char *file_au, const char *map = 0, double lambda = 0.1, double dbu = 0.001, const std::vector *lib_paths = 0) +static void run_test (tl::TestBase *_this, const std::string &base, const char *file, const char *file_au, const char *map = 0, double dbu = 0.001) { db::MALYReaderOptions *opt = new db::MALYReaderOptions(); opt->dbu = dbu; @@ -54,7 +54,7 @@ static void run_test (tl::TestBase *_this, const std::string &base, const char * options.set_options (opt); db::Manager m (false); - db::Layout layout (&m), layout2 (&m), layout2_mag (&m), layout_au (&m); + db::Layout layout (&m), layout2 (&m), layout_au (&m); { std::string fn (base); @@ -65,12 +65,12 @@ static void run_test (tl::TestBase *_this, const std::string &base, const char * reader.read (layout, options); } + tl_assert (layout.begin_top_down () != layout.end_top_down ()); std::string tc_name = layout.cell_name (*layout.begin_top_down ()); // normalize the layout by writing to OASIS and reading from .. std::string tmp_oas_file = _this->tmp_file (tl::sprintf ("%s.oas", tc_name)); - std::string tmp_maly_file = _this->tmp_file (tl::sprintf ("%s.mag", tc_name)); { tl::OutputStream stream (tmp_oas_file); diff --git a/src/plugins/streamers/maly/unit_tests/unit_tests.pro b/src/plugins/streamers/maly/unit_tests/unit_tests.pro index 7a6319ee6..a9d89c591 100644 --- a/src/plugins/streamers/maly/unit_tests/unit_tests.pro +++ b/src/plugins/streamers/maly/unit_tests/unit_tests.pro @@ -6,7 +6,7 @@ TARGET = maly_tests include($$PWD/../../../../lib_ut.pri) SOURCES = \ - dbMALYReader.cc \ + dbMALYReaderTests.cc INCLUDEPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common DEPENDPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../db_plugin $$PWD/../../../common diff --git a/testdata/maly/MALY_TEST.maly b/testdata/maly/MALY_TEST.maly new file mode 100644 index 000000000..0fd08fc48 --- /dev/null +++ b/testdata/maly/MALY_TEST.maly @@ -0,0 +1,15 @@ + +// a comment + +BEGIN /* a multiline comment + + +*/ MALY + +// a comment + +SREF " \"// /*hello*/ " SIZE ++ 10,10 + + + From cd468d4d67501dcdc57121a1292f03dc5a3d12ec Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 27 Apr 2025 23:00:46 +0200 Subject: [PATCH 177/392] WIP --- .../streamers/maly/db_plugin/dbMALYReader.cc | 464 +++++++++++++++++- .../streamers/maly/db_plugin/dbMALYReader.h | 19 +- testdata/maly/MALY_TEST.maly | 52 +- 3 files changed, 502 insertions(+), 33 deletions(-) diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index 2352fe8ed..aa5e19a20 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -43,8 +43,82 @@ namespace db { // --------------------------------------------------------------- -// MALYReader +// Some helper structures to collect data +struct MALYReaderTitleSpec +{ + MALYReaderTitleSpec () + : enabled (false), width (1.0), height (1.0), pitch (1.0) + { } + + bool enabled; + db::DTrans trans; + double width, height, pitch; +}; + +struct MALYReaderTitleData +{ + MALYReaderTitleData () + { } + + MALYReaderTitleSpec date_spec; + MALYReaderTitleSpec serial_spec; + std::list > string_titles; +}; + +struct MALYReaderParametersData +{ + MALYReaderParametersData () + : base (Center), array_base (Center), masksize (0.0), maskmirror (false), font (MALYTitle::Standard) + { } + + enum Base + { + Origin, + Center, + LowerLeft + }; + + Base base; + Base array_base; + double masksize; + bool maskmirror; + MALYTitle::Font font; + std::list > roots; +}; + +struct MALYReaderStrRefData +{ + MALYReaderStrRefData () + : layer (-1), scale (1.0), nx (1), ny (1), dx (0.0), dy (0.0) + { } + + std::string file; + std::string name; + int layer; + db::DVector org; + db::DBox size; + double scale; + int nx, ny; + double dx, dy; +}; + +struct MALYReaderStrGroupData +{ + std::string name; + std::list refs; +}; + +struct MALYReaderMaskData +{ + MALYReaderParametersData parameters; + MALYReaderTitleData title; + std::list strgroups; +}; + + +// --------------------------------------------------------------- +// MALYReader MALYReader::MALYReader (tl::InputStream &s) : m_stream (s), @@ -64,12 +138,9 @@ MALYReader::~MALYReader () bool MALYReader::test () { - return true; // @@@ try { - std::string rec = read_record (); - - tl::Extractor ex (rec.c_str ()); + tl::Extractor ex = read_record (); return ex.test ("BEGIN") && ex.test ("MALY"); } catch (...) { @@ -106,20 +177,31 @@ MALYReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) return layer_map_out (); } -std::string +void +MALYReader::unget_record () +{ + m_record_returned = m_record; +} + +tl::Extractor MALYReader::read_record () { + if (! m_record_returned.empty ()) { + m_record = m_record_returned; + return tl::Extractor (m_record.c_str ()); + } + while (! m_stream.at_end ()) { - std::string r = read_record_internal (); - tl::Extractor ex (r.c_str ()); + m_record = read_record_internal (); + tl::Extractor ex (m_record.c_str ()); if (ex.test ("+")) { error (tl::to_string (tr ("'+' character past first column - did you mean to continue a line?"))); } else if (! ex.at_end ()) { - return r; + return ex; } } - return std::string (); + return tl::Extractor (); } std::string @@ -207,16 +289,362 @@ MALYReader::read_record_internal () MALYData MALYReader::read_maly_file () { - // @@@ - std::cout << "@@@ BEGIN_MALY" << std::endl; - std::string rec; - while (! (rec = read_record ()).empty ()) { - std::cout << rec << std::endl; + MALYData data; + try { + do_read_maly_file (data); + } catch (tl::Exception &ex) { + error (ex.msg ()); } - std::cout << "@@@ END_MALY" << std::endl; - // @@@ + return data; +} - return MALYData (); // @@@ +void +MALYReader::extract_title_trans (tl::Extractor &ex, MALYReaderTitleSpec &spec) +{ + double x = 0.0, y = 0.0; + bool ymirror = false; + int rot = 0; + + ex.read (x); + ex.read (y); + + if (ex.test ("SIZE")) { + ex.read (spec.width); + ex.read (spec.height); + ex.read (spec.pitch); + } + + if (ex.test ("MIRROR")) { + if (ex.test ("Y")) { + ymirror = true; + } else if (ex.test ("OFF")) { // @@@ + ymirror = false; + } else { + error (tl::to_string (tr ("Expected 'Y' or 'OFF' for MIRROR spec"))); + } + } + + if (ex.test ("ROTATE")) { + unsigned int a = 0; + ex.read (a); + rot = (a / 90) % 4; + } + + spec.trans = db::DTrans (rot, ymirror, db::DVector (x, y)); +} + +static +MALYReaderParametersData::Base string_to_base (const std::string &string) +{ + if (string == "ORIGIN") { + return MALYReaderParametersData::Origin; + } else if (string == "LOWERLEFT") { // @@@? + return MALYReaderParametersData::LowerLeft; + } else if (string == "CENTER") { + return MALYReaderParametersData::Center; + } else { + // @@@ error + return MALYReaderParametersData::Center; + } +} + +void +MALYReader::read_parameter (MALYReaderParametersData &data) +{ + while (true) { + + tl::Extractor ex = read_record (); + if (ex.test ("MASKMIRROR")) { + + if (ex.test ("NONE")) { + data.maskmirror = false; + } else if (ex.test ("Y")) { + data.maskmirror = true; + } else { + error (tl::to_string (tr ("Expected value Y or NONE for MASKMIRROR"))); + } + + } else if (ex.test ("MASKSIZE")) { + + data.masksize = 0.0; + ex.read (data.masksize); + + } else if (ex.test ("FONT")) { + + if (ex.test ("STANDARD")) { + data.font = MALYTitle::Standard; + } else if (ex.test ("NATIVE")) { + data.font = MALYTitle::Native; + } else { + error (tl::to_string (tr ("Expected value STANDARD or NATIVE for FONT"))); + } + + } else if (ex.test ("BASE")) { + + std::string base; + ex.read_word (base); + data.base = string_to_base (base); + + } else if (ex.test ("ARYBASE")) { + + std::string base; + ex.read_word (base); + data.array_base = string_to_base (base); + + } else if (ex.test ("REFERENCE")) { + + ex.expect ("TOOL"); + + std::string para; + ex.read_word_or_quoted (para); + // @@@ TODO: what to do with "para" + + ex.expect_end (); + + } else if (ex.test ("ROOT")) { + + std::string format, path; + ex.read_word_or_quoted (format); + ex.read_word_or_quoted (path, ".\\/+-"); + ex.expect_end (); + + data.roots.push_back (std::make_pair (format, path)); + + } else if (ex.test ("END")) { + + ex.expect ("PARAMETER"); + return; + + } else { + error (tl::to_string (tr ("Parameter spec expected or END PARAMETER"))); + } + + } +} + +void +MALYReader::read_title (MALYReaderTitleData &data) +{ + while (true) { + + tl::Extractor ex = read_record (); + if (ex.test ("DATE")) { + + if (ex.test ("OFF")) { + data.date_spec.enabled = false; + } else { + data.date_spec.enabled = true; + extract_title_trans (ex, data.date_spec); + ex.expect_end (); + } + + } else if (ex.test ("SERIAL")) { + + if (ex.test ("OFF")) { + data.serial_spec.enabled = false; + } else { + data.serial_spec.enabled = true; + extract_title_trans (ex, data.serial_spec); + ex.expect_end (); + } + + } else if (ex.test ("STRING")) { + + std::string text; + ex.read_word_or_quoted (text); + + data.string_titles.push_back (std::make_pair (text, MALYReaderTitleSpec ())); + data.string_titles.back ().second.enabled = true; + extract_title_trans (ex, data.string_titles.back ().second); + + ex.expect_end (); + + } else if (ex.test ("END")) { + + ex.expect ("TITLE"); + return; + + } else { + error (tl::to_string (tr ("Title spec expected or END TITLE"))); + } + + } +} + +void +MALYReader::read_strgroup (MALYReaderStrGroupData &data) +{ + while (true) { + + bool is_sref = false; + + tl::Extractor ex = read_record (); + if ((is_sref = ex.test ("SREF")) || ex.test ("AREF")) { + + data.refs.push_back (MALYReaderStrRefData ()); + MALYReaderStrRefData &ref = data.refs.back (); + + ex.read_word_or_quoted (ref.file); + ex.read_word_or_quoted (ref.name); + ex.read (ref.layer); + + if (ex.test ("ORG")) { + double x = 0.0, y = 0.0; + ex.read (x); + ex.read (y); + ref.org = db::DVector (x, y); + } + + if (ex.test ("SIZE")) { + double l = 0.0, b = 0.0, r = 0.0, t = 0.0; + ex.read (l); + ex.read (b); + ex.read (r); + ex.read (t); + ref.size = db::DBox (l, b, r, t); + } + + if (ex.test ("SCALE")) { + ex.read (ref.scale); + } + + if (! is_sref && ex.test ("ITERATION")) { + ex.read (ref.nx); + ex.read (ref.ny); + ex.read (ref.dx); + ex.read (ref.dy); + } + + ex.expect_end (); + + } else if (ex.test ("END")) { + + ex.expect ("STRGROUP"); + return; + + } else { + error (tl::to_string (tr ("SREF or AREF spec expected or END STRGROUP"))); + } + + } +} + +void +MALYReader::read_mask (MALYReaderMaskData &mask, bool cmask) +{ + while (true) { + + tl::Extractor ex = read_record (); + if (ex.test ("BEGIN")) { + + if (ex.test ("PARAMETER")) { + + ex.expect_end (); + read_parameter (mask.parameters); + + } else if (ex.test ("TITLE")) { + + ex.expect_end (); + read_title (mask.title); + + } else if (ex.test ("STRGROUP")) { + + mask.strgroups.push_back (MALYReaderStrGroupData ()); + + ex.read_word_or_quoted (mask.strgroups.back ().name); + ex.expect_end (); + + read_strgroup (mask.strgroups.back ()); + + } else { + error (tl::to_string (tr ("Mask component expected (PARAMETER, TITLE, STRGROUP)"))); + } + + } else if (ex.test ("END")) { + + ex.expect (cmask ? "CMASK" : "MASK"); + break; + + } else { + error (tl::to_string (tr ("Mask component expected (PARAMETER, TITLE, STRGROUP)"))); + } + + } +} + +bool +MALYReader::read_maskset (MALYData &data) +{ + tl::Extractor ex = read_record (); + if (! ex.test ("BEGIN") || ! ex.test ("MASKSET")) { + unget_record (); + return false; + } + + MALYReaderMaskData cmask; + std::list masks; + + while (true) { + + ex = read_record (); + + if (ex.test ("END")) { + + ex.expect ("MASKSET"); + ex.expect_end (); + // @@@ create_masks (cmask, masks, data); + return true; + + } else if (ex.test ("BEGIN")) { + + MALYReaderMaskData *mm = 0; + bool cm = false; + if (ex.test ("MASK")) { + masks.push_back (MALYReaderMaskData ()); + mm = &masks.back (); + } else if (ex.test ("CMASK")) { + mm = &cmask; + cm = true; + } else { + error (tl::to_string (tr ("'BEGIN MASK' or 'BEGIN CMASK' record expected"))); + } + + ex.expect_end (); + read_mask (*mm, cm); + + } else { + error (tl::to_string (tr ("'BEGIN MASK' or 'BEGIN CMASK' record expected"))); + } + + } +} + +void +MALYReader::do_read_maly_file (MALYData &data) +{ + tl::Extractor ex = read_record (); + if (! ex.test ("BEGIN") || ! ex.test ("MALY")) { + error (tl::to_string (tr ("Header expected ('BEGIN MALY')"))); + } + + std::string version; + ex.read (version, "."); + // @@@ TODO: what to do with version string? + + ex.expect_end (); + + while (read_maskset (data)) + ; + + ex = read_record (); + if (! ex.test ("END") || ! ex.test ("MALY")) { + error (tl::to_string (tr ("Terminator expected ('END MALY')"))); + } + + ex = read_record (); + if (! ex.at_end ()) { + error (tl::to_string (tr ("Records found past end of file"))); + } } void diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h index 981d3419a..0272b6886 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h @@ -45,7 +45,11 @@ namespace db { -class Technology; +class MALYReaderMaskData; +class MALYReaderTitleData; +class MALYReaderParametersData; +class MALYReaderStrGroupData; +class MALYReaderTitleSpec; /** * @brief Generic base class of MALY reader exceptions @@ -144,12 +148,23 @@ private: tl::TextInputStream m_stream; tl::AbsoluteProgress m_progress; double m_dbu; + std::string m_record; + std::string m_record_returned; void do_read (db::Layout &layout, db::cell_index_type to_cell, tl::TextInputStream &stream); std::string resolve_path(const std::string &path); MALYData read_maly_file (); - std::string read_record (); + tl::Extractor read_record (); + void unget_record (); std::string read_record_internal (); + void do_read_maly_file (MALYData &data); + bool read_maskset (MALYData &data); + void read_mask (MALYReaderMaskData &mask, bool cmask); + void read_title (MALYReaderTitleData &mask); + void read_parameter (MALYReaderParametersData &mask); + void read_strgroup (MALYReaderStrGroupData &mask); + db::DTrans extract_title_trans (tl::Extractor &ex); + void extract_title_trans (tl::Extractor &ex, MALYReaderTitleSpec &spec); }; } diff --git a/testdata/maly/MALY_TEST.maly b/testdata/maly/MALY_TEST.maly index 0fd08fc48..ca7b71601 100644 --- a/testdata/maly/MALY_TEST.maly +++ b/testdata/maly/MALY_TEST.maly @@ -1,15 +1,41 @@ -// a comment +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING TEST 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY -BEGIN /* a multiline comment - - -*/ MALY - -// a comment - -SREF " \"// /*hello*/ " SIZE -+ 10,10 - - - From 7820733bd59881ed5ca6e356c839bc52a575bc47 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 29 Apr 2025 00:28:46 +0200 Subject: [PATCH 178/392] WIP --- .../streamers/maly/db_plugin/dbMALYReader.cc | 177 +++++++++++------- .../streamers/maly/db_plugin/dbMALYReader.h | 6 +- 2 files changed, 111 insertions(+), 72 deletions(-) diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index aa5e19a20..ebf2d7b0b 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -348,13 +348,71 @@ MALYReaderParametersData::Base string_to_base (const std::string &string) } } +bool +MALYReader::begin_section (tl::Extractor &ex, const std::string &name) +{ + tl::Extractor ex_saved = ex; + + if (ex.test ("BEGIN")) { + if (name.empty ()) { + m_sections.push_back (std::string ()); + ex.read_word (m_sections.back ()); + return true; + } else if (ex.test (name.c_str ())) { + m_sections.push_back (name); + return true; + } + } + + ex = ex_saved; + return false; +} + +bool +MALYReader::end_section (tl::Extractor &ex) +{ + tl_assert (! m_sections.empty ()); + if (ex.at_end ()) { + + error (tl::to_string (tr ("Unexpected end of file during section"))); + return false; + + } else if (ex.test ("END")) { + + ex.expect (m_sections.back ().c_str ()); + m_sections.pop_back (); + return true; + + } else { + + return false; + + } +} + +void +MALYReader::skip_section () +{ + while (true) { + tl::Extractor ex = read_record (); + if (begin_section (ex)) { + skip_section (); + } else if (end_section (ex)) { + break; + } + } +} + void MALYReader::read_parameter (MALYReaderParametersData &data) { while (true) { tl::Extractor ex = read_record (); - if (ex.test ("MASKMIRROR")) { + + if (end_section (ex)) { + break; + } else if (ex.test ("MASKMIRROR")) { if (ex.test ("NONE")) { data.maskmirror = false; @@ -410,13 +468,8 @@ MALYReader::read_parameter (MALYReaderParametersData &data) data.roots.push_back (std::make_pair (format, path)); - } else if (ex.test ("END")) { - - ex.expect ("PARAMETER"); - return; - } else { - error (tl::to_string (tr ("Parameter spec expected or END PARAMETER"))); + warn (tl::to_string (tr ("Unknown record ignored"))); } } @@ -428,7 +481,10 @@ MALYReader::read_title (MALYReaderTitleData &data) while (true) { tl::Extractor ex = read_record (); - if (ex.test ("DATE")) { + + if (end_section (ex)) { + break; + } else if (ex.test ("DATE")) { if (ex.test ("OFF")) { data.date_spec.enabled = false; @@ -459,13 +515,8 @@ MALYReader::read_title (MALYReaderTitleData &data) ex.expect_end (); - } else if (ex.test ("END")) { - - ex.expect ("TITLE"); - return; - } else { - error (tl::to_string (tr ("Title spec expected or END TITLE"))); + warn (tl::to_string (tr ("Unknown record ignored"))); } } @@ -479,7 +530,9 @@ MALYReader::read_strgroup (MALYReaderStrGroupData &data) bool is_sref = false; tl::Extractor ex = read_record (); - if ((is_sref = ex.test ("SREF")) || ex.test ("AREF")) { + if (end_section (ex)) { + break; + } else if ((is_sref = ex.test ("SREF")) || ex.test ("AREF")) { data.refs.push_back (MALYReaderStrRefData ()); MALYReaderStrRefData &ref = data.refs.back (); @@ -517,56 +570,44 @@ MALYReader::read_strgroup (MALYReaderStrGroupData &data) ex.expect_end (); - } else if (ex.test ("END")) { - - ex.expect ("STRGROUP"); - return; - } else { - error (tl::to_string (tr ("SREF or AREF spec expected or END STRGROUP"))); + warn (tl::to_string (tr ("Unknown record ignored"))); } } } void -MALYReader::read_mask (MALYReaderMaskData &mask, bool cmask) +MALYReader::read_mask (MALYReaderMaskData &mask) { while (true) { tl::Extractor ex = read_record (); - if (ex.test ("BEGIN")) { - - if (ex.test ("PARAMETER")) { - - ex.expect_end (); - read_parameter (mask.parameters); - - } else if (ex.test ("TITLE")) { - - ex.expect_end (); - read_title (mask.title); - - } else if (ex.test ("STRGROUP")) { - - mask.strgroups.push_back (MALYReaderStrGroupData ()); - - ex.read_word_or_quoted (mask.strgroups.back ().name); - ex.expect_end (); - - read_strgroup (mask.strgroups.back ()); - - } else { - error (tl::to_string (tr ("Mask component expected (PARAMETER, TITLE, STRGROUP)"))); - } - - } else if (ex.test ("END")) { - - ex.expect (cmask ? "CMASK" : "MASK"); + if (end_section (ex)) { break; + } else if (begin_section (ex, "PARAMETER")) { + ex.expect_end (); + read_parameter (mask.parameters); + + } else if (begin_section (ex, "TITLE")) { + + ex.expect_end (); + read_title (mask.title); + + } else if (begin_section (ex, "STRGROUP")) { + + mask.strgroups.push_back (MALYReaderStrGroupData ()); + + ex.read_word_or_quoted (mask.strgroups.back ().name); + ex.expect_end (); + + read_strgroup (mask.strgroups.back ()); + + } else if (begin_section (ex)) { + skip_section (); } else { - error (tl::to_string (tr ("Mask component expected (PARAMETER, TITLE, STRGROUP)"))); + warn (tl::to_string (tr ("Unknown record ignored"))); } } @@ -576,7 +617,8 @@ bool MALYReader::read_maskset (MALYData &data) { tl::Extractor ex = read_record (); - if (! ex.test ("BEGIN") || ! ex.test ("MASKSET")) { + + if (! begin_section (ex, "MASKSET")) { unget_record (); return false; } @@ -588,32 +630,25 @@ MALYReader::read_maskset (MALYData &data) ex = read_record (); - if (ex.test ("END")) { + if (end_section (ex)) { - ex.expect ("MASKSET"); ex.expect_end (); // @@@ create_masks (cmask, masks, data); return true; - } else if (ex.test ("BEGIN")) { - - MALYReaderMaskData *mm = 0; - bool cm = false; - if (ex.test ("MASK")) { - masks.push_back (MALYReaderMaskData ()); - mm = &masks.back (); - } else if (ex.test ("CMASK")) { - mm = &cmask; - cm = true; - } else { - error (tl::to_string (tr ("'BEGIN MASK' or 'BEGIN CMASK' record expected"))); - } + } else if (begin_section (ex, "MASK")) { ex.expect_end (); - read_mask (*mm, cm); + masks.push_back (MALYReaderMaskData ()); + read_mask (masks.back ()); + + } else if (begin_section (ex, "CMASK")) { + + ex.expect_end (); + read_mask (cmask); } else { - error (tl::to_string (tr ("'BEGIN MASK' or 'BEGIN CMASK' record expected"))); + warn (tl::to_string (tr ("Unknown record ignored"))); } } @@ -623,7 +658,7 @@ void MALYReader::do_read_maly_file (MALYData &data) { tl::Extractor ex = read_record (); - if (! ex.test ("BEGIN") || ! ex.test ("MALY")) { + if (! begin_section (ex, "MALY")) { error (tl::to_string (tr ("Header expected ('BEGIN MALY')"))); } @@ -637,7 +672,7 @@ MALYReader::do_read_maly_file (MALYData &data) ; ex = read_record (); - if (! ex.test ("END") || ! ex.test ("MALY")) { + if (! end_section (ex)) { error (tl::to_string (tr ("Terminator expected ('END MALY')"))); } diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h index 0272b6886..e8544782a 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h @@ -150,6 +150,7 @@ private: double m_dbu; std::string m_record; std::string m_record_returned; + std::list m_sections; void do_read (db::Layout &layout, db::cell_index_type to_cell, tl::TextInputStream &stream); std::string resolve_path(const std::string &path); @@ -159,12 +160,15 @@ private: std::string read_record_internal (); void do_read_maly_file (MALYData &data); bool read_maskset (MALYData &data); - void read_mask (MALYReaderMaskData &mask, bool cmask); + void read_mask (MALYReaderMaskData &mask); void read_title (MALYReaderTitleData &mask); void read_parameter (MALYReaderParametersData &mask); void read_strgroup (MALYReaderStrGroupData &mask); db::DTrans extract_title_trans (tl::Extractor &ex); void extract_title_trans (tl::Extractor &ex, MALYReaderTitleSpec &spec); + bool begin_section (tl::Extractor &ex, const std::string &name = std::string ()); + bool end_section (tl::Extractor &ex); + void skip_section (); }; } From fa30fd0f7b764a7215b0ccce4113efc343afb2e5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 29 Apr 2025 22:19:59 +0200 Subject: [PATCH 179/392] WIP, MALY reader --- .../streamers/maly/db_plugin/dbMALY.cc | 80 +++++ src/plugins/streamers/maly/db_plugin/dbMALY.h | 31 +- .../streamers/maly/db_plugin/dbMALYReader.cc | 321 ++++++++++++------ .../streamers/maly/db_plugin/dbMALYReader.h | 87 ++++- 4 files changed, 405 insertions(+), 114 deletions(-) diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.cc b/src/plugins/streamers/maly/db_plugin/dbMALY.cc index b53b4b449..954514f79 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALY.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.cc @@ -38,6 +38,86 @@ MALYDiagnostics::~MALYDiagnostics () // .. nothing yet .. } +// --------------------------------------------------------------- +// MALYData implementation + +std::string +MALYTitle::to_string () const +{ + std::string res; + res += "\"" + string + "\" " + transformation.to_string (); + res += tl::sprintf (" %g,%g,%g", width, height, pitch); + if (font == Standard) { + res += " [Standard]"; + } else if (font == Native) { + res += " [Native]"; + } + return res; +} + +std::string +MALYStructure::to_string () const +{ + std::string res; + res += path + "{" + topcell + "}"; + if (layer < 0) { + res += "(*)"; + } else { + res += tl::sprintf ("(%d)", layer); + } + + if (! mname.empty ()) { + res += " mname(" + mname + ")"; + } + if (! ename.empty ()) { + res += " ename(" + ename + ")"; + } + if (! dname.empty ()) { + res += " dname(" + dname + ")"; + } + + res += " "; + res += size.to_string (); + + res += " "; + res += transformation.to_string (); + + if (nx > 1 || ny > 1) { + res += tl::sprintf (" [%.12gx%d,%.12gx%d]", dx, nx, dy, ny); + } + + return res; +} + +std::string +MALYMask::to_string () const +{ + std::string res; + res += "Mask " + name + "\n"; + res += " Size " + tl::to_string (size_um); + + for (auto t = titles.begin (); t != titles.end (); ++t) { + res += "\n Title " + t->to_string (); + } + for (auto s = structures.begin (); s != structures.end (); ++s) { + res += "\n Ref " + s->to_string (); + } + return res; +} + +std::string +MALYData::to_string () const +{ + std::string res; + for (auto m = masks.begin (); m != masks.end (); ++m) { + if (m != masks.begin ()) { + res += "\n"; + } + res += m->to_string (); + } + return res; +} + // --------------------------------------------------------------- // MALY format declaration diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.h b/src/plugins/streamers/maly/db_plugin/dbMALY.h index ec904e969..bdcbaae7b 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALY.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.h @@ -76,9 +76,9 @@ public: */ enum Type { - String = 0, // A user-defined string - Date = 1, // The date - Serial = 2 // A serial number + String = 0, // A user-defined string + Date = 1, // The date + Serial = 2 // A serial number }; /** @@ -86,8 +86,9 @@ public: */ enum Font { - Standard = 0, // Standard font - Native = 1 // Native tool font + FontNotSet = 0, // Undef + Standard = 1, // Standard font + Native = 2 // Native tool font }; /** @@ -127,6 +128,11 @@ public: * @brief The font to be used */ Font font; + + /** + * @brief Returns a string representing the structure + */ + std::string to_string () const; }; /** @@ -204,6 +210,11 @@ public: * A value of -1 means "all". */ int layer; + + /** + * @brief Returns a string representing the structure + */ + std::string to_string () const; }; /** @@ -240,6 +251,11 @@ public: * @brief The list of titles */ std::list titles; + + /** + * @brief Returns a string representing the mask + */ + std::string to_string () const; }; /** @@ -258,6 +274,11 @@ public: * @brief The masks defined by the file */ std::list masks; + + /** + * @brief Returns a string representing the data set + */ + std::string to_string () const; }; } diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index ebf2d7b0b..5d9086c48 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -42,88 +42,14 @@ namespace db { -// --------------------------------------------------------------- -// Some helper structures to collect data - -struct MALYReaderTitleSpec -{ - MALYReaderTitleSpec () - : enabled (false), width (1.0), height (1.0), pitch (1.0) - { } - - bool enabled; - db::DTrans trans; - double width, height, pitch; -}; - -struct MALYReaderTitleData -{ - MALYReaderTitleData () - { } - - MALYReaderTitleSpec date_spec; - MALYReaderTitleSpec serial_spec; - std::list > string_titles; -}; - -struct MALYReaderParametersData -{ - MALYReaderParametersData () - : base (Center), array_base (Center), masksize (0.0), maskmirror (false), font (MALYTitle::Standard) - { } - - enum Base - { - Origin, - Center, - LowerLeft - }; - - Base base; - Base array_base; - double masksize; - bool maskmirror; - MALYTitle::Font font; - std::list > roots; -}; - -struct MALYReaderStrRefData -{ - MALYReaderStrRefData () - : layer (-1), scale (1.0), nx (1), ny (1), dx (0.0), dy (0.0) - { } - - std::string file; - std::string name; - int layer; - db::DVector org; - db::DBox size; - double scale; - int nx, ny; - double dx, dy; -}; - -struct MALYReaderStrGroupData -{ - std::string name; - std::list refs; -}; - -struct MALYReaderMaskData -{ - MALYReaderParametersData parameters; - MALYReaderTitleData title; - std::list strgroups; -}; - - // --------------------------------------------------------------- // MALYReader MALYReader::MALYReader (tl::InputStream &s) : m_stream (s), m_progress (tl::to_string (tr ("Reading MALY file")), 1000), - m_dbu (0.001) + m_dbu (0.001), + m_last_record_line (0) { m_progress.set_format (tl::to_string (tr ("%.0fk lines"))); m_progress.set_format_unit (1000.0); @@ -171,6 +97,8 @@ MALYReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) MALYData data = read_maly_file (); + // @@@ + std::cout << data.to_string () << std::endl; // @@@ finish_layers (layout); @@ -187,18 +115,26 @@ tl::Extractor MALYReader::read_record () { if (! m_record_returned.empty ()) { + m_record = m_record_returned; + m_record_returned.clear (); + return tl::Extractor (m_record.c_str ()); + } while (! m_stream.at_end ()) { + + m_last_record_line = m_stream.line_number (); m_record = read_record_internal (); + tl::Extractor ex (m_record.c_str ()); if (ex.test ("+")) { error (tl::to_string (tr ("'+' character past first column - did you mean to continue a line?"))); } else if (! ex.at_end ()) { return ex; } + } return tl::Extractor (); @@ -333,12 +269,12 @@ MALYReader::extract_title_trans (tl::Extractor &ex, MALYReaderTitleSpec &spec) spec.trans = db::DTrans (rot, ymirror, db::DVector (x, y)); } -static -MALYReaderParametersData::Base string_to_base (const std::string &string) +MALYReader::MALYReaderParametersData::Base +MALYReader::string_to_base (const std::string &string) { if (string == "ORIGIN") { return MALYReaderParametersData::Origin; - } else if (string == "LOWERLEFT") { // @@@? + } else if (string == "LOWERLEFT") { return MALYReaderParametersData::LowerLeft; } else if (string == "CENTER") { return MALYReaderParametersData::Center; @@ -531,7 +467,27 @@ MALYReader::read_strgroup (MALYReaderStrGroupData &data) tl::Extractor ex = read_record (); if (end_section (ex)) { + break; + + } else if (ex.test ("PROPERTY")) { + + if (data.refs.empty ()) { + error (tl::to_string (tr ("PROPERTY entry without a preceeding SREF or AREF"))); + } + + while (! ex.at_end ()) { + if (ex.test ("DNAME")) { + ex.read_word_or_quoted (data.refs.back ().dname); + } else if (ex.test ("ENAME")) { + ex.read_word_or_quoted (data.refs.back ().ename); + } else if (ex.test ("MNAME")) { + ex.read_word_or_quoted (data.refs.back ().mname); + } else { + error (tl::to_string (tr ("Unknown PROPERTY item"))); + } + } + } else if ((is_sref = ex.test ("SREF")) || ex.test ("AREF")) { data.refs.push_back (MALYReaderStrRefData ()); @@ -633,13 +589,15 @@ MALYReader::read_maskset (MALYData &data) if (end_section (ex)) { ex.expect_end (); - // @@@ create_masks (cmask, masks, data); + create_masks (cmask, masks, data); return true; } else if (begin_section (ex, "MASK")) { - ex.expect_end (); masks.push_back (MALYReaderMaskData ()); + ex.read (masks.back ().name); + + ex.expect_end (); read_mask (masks.back ()); } else if (begin_section (ex, "CMASK")) { @@ -654,6 +612,183 @@ MALYReader::read_maskset (MALYData &data) } } +void +MALYReader::create_masks (const MALYReaderMaskData &cmask, const std::list &masks, MALYData &data) +{ + for (auto i = masks.begin (); i != masks.end (); ++i) { + + data.masks.push_back (MALYMask ()); + MALYMask &m = data.masks.back (); + + m.name = i->name; + + m.size_um = i->parameters.masksize * 25400.0; + if (m.size_um < db::epsilon) { + m.size_um = cmask.parameters.masksize * 25400.0; + } + if (m.size_um < db::epsilon) { + m.size_um = 7.0 * 25400.0; // @@@? + } + + MALYTitle::Font font = i->parameters.font; + if (font == MALYTitle::FontNotSet) { + font = cmask.parameters.font; + } + if (font == MALYTitle::FontNotSet) { + font = MALYTitle::Standard; // @@@? + } + + const MALYReaderTitleSpec *date_spec = 0; + if (i->title.date_spec.enabled) { + date_spec = &i->title.date_spec; + } else if (cmask.title.date_spec.enabled) { + date_spec = &cmask.title.date_spec; + } + if (date_spec) { + m.titles.push_back (create_title (MALYTitle::Date, *date_spec, font, std::string (""))); + } + + const MALYReaderTitleSpec *serial_spec = 0; + if (i->title.serial_spec.enabled) { + serial_spec = &i->title.serial_spec; + } else if (cmask.title.serial_spec.enabled) { + serial_spec = &cmask.title.serial_spec; + } + if (date_spec) { + m.titles.push_back (create_title (MALYTitle::Serial, *serial_spec, font, std::string (""))); + } + + for (auto t = i->title.string_titles.begin (); t != i->title.string_titles.end (); ++t) { + m.titles.push_back (create_title (MALYTitle::String, t->second, font, t->first)); + } + for (auto t = cmask.title.string_titles.begin (); t != cmask.title.string_titles.end (); ++t) { + m.titles.push_back (create_title (MALYTitle::String, t->second, font, t->first)); + } + + MALYReaderParametersData::Base base = i->parameters.base; + if (base == MALYReaderParametersData::BaseNotSet) { + base = cmask.parameters.base; + } + if (base == MALYReaderParametersData::BaseNotSet) { + base = MALYReaderParametersData::Center; // @@@? + } + + MALYReaderParametersData::Base array_base = MALYReaderParametersData::BaseNotSet; + if (array_base == MALYReaderParametersData::BaseNotSet) { + array_base = cmask.parameters.base; + } + if (array_base == MALYReaderParametersData::BaseNotSet) { + array_base = MALYReaderParametersData::Center; // @@@? + } + + for (auto sg = cmask.strgroups.begin (); sg != cmask.strgroups.end (); ++sg) { + for (auto s = sg->refs.begin (); s != sg->refs.end (); ++s) { + m.structures.push_back (create_structure (i->parameters, cmask.parameters, *s, sg->name, base, array_base)); + } + } + for (auto sg = i->strgroups.begin (); sg != i->strgroups.end (); ++sg) { + for (auto s = sg->refs.begin (); s != sg->refs.end (); ++s) { + m.structures.push_back (create_structure (i->parameters, cmask.parameters, *s, sg->name, base, array_base)); + } + } + + } +} + +MALYTitle +MALYReader::create_title (MALYTitle::Type type, const MALYReaderTitleSpec &data, MALYTitle::Font font, const std::string &string) +{ + MALYTitle title; + + title.transformation = data.trans; + title.width = data.width; + title.height = data.height; + title.pitch = data.pitch; + title.type = type; + title.font = font; + title.string = string; + + return title; +} + +MALYStructure +MALYReader::create_structure (const MALYReaderParametersData &mparam, const MALYReaderParametersData &cparam, const MALYReaderStrRefData &data, const std::string & /*strgroup_name*/, MALYReaderParametersData::Base base, MALYReaderParametersData::Base array_base) +{ + MALYStructure str; + + str.size = data.size; + str.dname = data.dname; + str.ename = data.ename; + str.mname = data.mname; + str.topcell = data.name; + str.nx = std::max (1, data.nx); + str.ny = std::max (1, data.ny); + str.dx = data.dx; + str.dy = data.dy; + str.layer = data.layer; + + str.path = resolve_path (mparam, data.file); + if (str.path.empty ()) { + str.path = resolve_path (cparam, data.file); + } + if (str.path.empty ()) { + // try any fail later ... + str.path = data.file; + } + + MALYReaderParametersData::Base eff_base = (data.nx > 1 || data.ny > 1) ? array_base : base; + + db::DPoint rp; + switch (eff_base) { + case MALYReaderParametersData::LowerLeft: + rp = data.size.p1 (); + break; + case MALYReaderParametersData::Center: + default: + // NOTE: the center implies the whole array's center in case of an AREF + rp = (data.size + data.size.moved (db::DVector (str.dx * (str.nx - 1), str.dy * (str.ny - 1)))).center (); + break; + case MALYReaderParametersData::Origin: + break; + } + + db::DCplxTrans mirr (mparam.maskmirror != cparam.maskmirror ? db::DFTrans::m90 : db::DFTrans::r0); + str.transformation = mirr * db::DCplxTrans (data.scale, 0.0, false, data.org + (db::DPoint () - rp)); + + return str; +} + +std::string +MALYReader::resolve_path (const MALYReaderParametersData ¶m, const std::string &path) +{ + if (tl::is_absolute (path)) { + + return path; + + } else { + + // NOTE: we don't differentiate by file type here. Each root is used in the + // same way to find the actual file. + // Relative paths are always resolved relative to the MALY file. + + for (auto r = param.roots.begin (); r != param.roots.end (); ++r) { + + std::string p = tl::combine_path (r->second, path); + if (! tl::is_absolute (p)) { + p = tl::combine_path (tl::dirname (m_stream.source ()), p); + } + + if (tl::file_exists (p)) { + return p; + } + + } + + } + + return std::string (); +} + void MALYReader::do_read_maly_file (MALYData &data) { @@ -663,7 +798,7 @@ MALYReader::do_read_maly_file (MALYData &data) } std::string version; - ex.read (version, "."); + ex.read_word (version, "."); // @@@ TODO: what to do with version string? ex.expect_end (); @@ -685,7 +820,7 @@ MALYReader::do_read_maly_file (MALYData &data) void MALYReader::error (const std::string &msg) { - throw MALYReaderException (msg, m_stream.line_number (), m_stream.source ()); + throw MALYReaderException (msg, m_last_record_line, m_stream.source ()); } void @@ -702,7 +837,7 @@ MALYReader::warn (const std::string &msg, int wl) int ws = compress_warning (msg); if (ws < 0) { tl::warn << msg - << tl::to_string (tr (" (line=")) << m_stream.line_number () + << tl::to_string (tr (" (line=")) << m_last_record_line << tl::to_string (tr (", file=")) << m_stream.source () << ")"; } else if (ws == 0) { @@ -710,24 +845,6 @@ MALYReader::warn (const std::string &msg, int wl) } } -std::string -MALYReader::resolve_path (const std::string &path) -{ - tl::URI path_uri (path); - - if (tl::is_absolute (path_uri.path ())) { - - return path_uri.to_string (); - - } else { - - tl::URI source_uri (m_stream.source ()); - source_uri.set_path (tl::dirname (source_uri.path ())); - return source_uri.resolved (tl::URI (path)).to_string (); - - } -} - void MALYReader::do_read (db::Layout &layout, db::cell_index_type cell_index, tl::TextInputStream &stream) { diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h index e8544782a..8face4692 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h @@ -45,12 +45,6 @@ namespace db { -class MALYReaderMaskData; -class MALYReaderTitleData; -class MALYReaderParametersData; -class MALYReaderStrGroupData; -class MALYReaderTitleSpec; - /** * @brief Generic base class of MALY reader exceptions */ @@ -145,15 +139,89 @@ public: virtual void warn (const std::string &txt, int wl = 1); private: + struct MALYReaderTitleSpec + { + MALYReaderTitleSpec () + : enabled (false), width (1.0), height (1.0), pitch (1.0) + { } + + bool enabled; + db::DTrans trans; + double width, height, pitch; + }; + + struct MALYReaderTitleData + { + MALYReaderTitleData () + { } + + MALYReaderTitleSpec date_spec; + MALYReaderTitleSpec serial_spec; + std::list > string_titles; + }; + + struct MALYReaderParametersData + { + MALYReaderParametersData () + : base (BaseNotSet), array_base (BaseNotSet), masksize (0.0), maskmirror (false), font (MALYTitle::FontNotSet) + { } + + enum Base + { + BaseNotSet, + Origin, + Center, + LowerLeft + }; + + Base base; + Base array_base; + double masksize; + bool maskmirror; + MALYTitle::Font font; + std::list > roots; + }; + + struct MALYReaderStrRefData + { + MALYReaderStrRefData () + : layer (-1), scale (1.0), nx (1), ny (1), dx (0.0), dy (0.0) + { } + + std::string file; + std::string name; + std::string dname, ename, mname; + int layer; + db::DVector org; + db::DBox size; + double scale; + int nx, ny; + double dx, dy; + }; + + struct MALYReaderStrGroupData + { + std::string name; + std::list refs; + }; + + struct MALYReaderMaskData + { + std::string name; + MALYReaderParametersData parameters; + MALYReaderTitleData title; + std::list strgroups; + }; + tl::TextInputStream m_stream; tl::AbsoluteProgress m_progress; double m_dbu; + unsigned int m_last_record_line; std::string m_record; std::string m_record_returned; std::list m_sections; void do_read (db::Layout &layout, db::cell_index_type to_cell, tl::TextInputStream &stream); - std::string resolve_path(const std::string &path); MALYData read_maly_file (); tl::Extractor read_record (); void unget_record (); @@ -169,6 +237,11 @@ private: bool begin_section (tl::Extractor &ex, const std::string &name = std::string ()); bool end_section (tl::Extractor &ex); void skip_section (); + MALYTitle create_title (MALYTitle::Type type, const MALYReaderTitleSpec &data, MALYTitle::Font font, const std::string &string); + void create_masks (const MALYReaderMaskData &cmask, const std::list &masks, MALYData &data); + MALYStructure create_structure (const MALYReaderParametersData &mparam, const MALYReaderParametersData &cparam, const MALYReaderStrRefData &data, const std::string &strgroup_name, MALYReaderParametersData::Base base, MALYReaderParametersData::Base array_base); + std::string resolve_path (const MALYReaderParametersData ¶m, const std::string &path); + static MALYReaderParametersData::Base string_to_base (const std::string &string); }; } From 03873d9b6c33b347d58e140254a7611ab8b47fe6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 29 Apr 2025 22:56:53 +0200 Subject: [PATCH 180/392] WIP --- .../streamers/maly/db_plugin/dbMALYReader.cc | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index 5d9086c48..189551635 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -253,10 +253,10 @@ MALYReader::extract_title_trans (tl::Extractor &ex, MALYReaderTitleSpec &spec) if (ex.test ("MIRROR")) { if (ex.test ("Y")) { ymirror = true; - } else if (ex.test ("OFF")) { // @@@ + } else if (ex.test ("NONE")) { ymirror = false; } else { - error (tl::to_string (tr ("Expected 'Y' or 'OFF' for MIRROR spec"))); + error (tl::to_string (tr ("Expected 'Y' or 'NONE' for MIRROR spec"))); } } @@ -279,8 +279,7 @@ MALYReader::string_to_base (const std::string &string) } else if (string == "CENTER") { return MALYReaderParametersData::Center; } else { - // @@@ error - return MALYReaderParametersData::Center; + error (tl::to_string (tr ("Unknown base specification: ")) + string); } } @@ -391,7 +390,7 @@ MALYReader::read_parameter (MALYReaderParametersData &data) std::string para; ex.read_word_or_quoted (para); - // @@@ TODO: what to do with "para" + // TODO: what to do with "para" ex.expect_end (); @@ -627,7 +626,8 @@ MALYReader::create_masks (const MALYReaderMaskData &cmask, const std::listparameters.font; @@ -635,7 +635,7 @@ MALYReader::create_masks (const MALYReaderMaskData &cmask, const std::list Date: Tue, 29 Apr 2025 22:59:17 +0200 Subject: [PATCH 181/392] WIP --- src/plugins/streamers/maly/db_plugin/dbMALYReader.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index 189551635..29b56c005 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -279,7 +279,7 @@ MALYReader::string_to_base (const std::string &string) } else if (string == "CENTER") { return MALYReaderParametersData::Center; } else { - error (tl::to_string (tr ("Unknown base specification: ")) + string); + throw tl::Exception (tl::to_string (tr ("Unknown base specification: ")) + string); } } @@ -627,7 +627,7 @@ MALYReader::create_masks (const MALYReaderMaskData &cmask, const std::listparameters.font; @@ -671,7 +671,7 @@ MALYReader::create_masks (const MALYReaderMaskData &cmask, const std::list Date: Wed, 30 Apr 2025 06:39:46 +0900 Subject: [PATCH 182/392] To use Ruby 3.3.8 from MacPorts and fix some Python syntax warnings --- macbuild/build4mac_env.py | 2 +- macbuild/makeDMG4mac.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/macbuild/build4mac_env.py b/macbuild/build4mac_env.py index 3be1c22de..e3527edf6 100755 --- a/macbuild/build4mac_env.py +++ b/macbuild/build4mac_env.py @@ -192,7 +192,7 @@ RubySequoia = { 'exe': '/System/Library/Frameworks/Ruby.framework/Versions # install with 'sudo port install ruby33' # [Key Type Name] = 'MP33' Ruby33MacPorts = { 'exe': '/opt/local/bin/ruby3.3', - 'inc': '/opt/local/include/ruby-3.3.7', + 'inc': '/opt/local/include/ruby-3.3.8', 'lib': '/opt/local/lib/libruby.3.3.dylib' } diff --git a/macbuild/makeDMG4mac.py b/macbuild/makeDMG4mac.py index 972a36374..b7e7aa9da 100755 --- a/macbuild/makeDMG4mac.py +++ b/macbuild/makeDMG4mac.py @@ -424,7 +424,7 @@ def CheckPkgDirectory(): #------------------------------------------------------ # [5] Check the occupied disk space #------------------------------------------------------ - command = "\du -sm %s" % DefaultBundleName + command = r"\du -sm %s" % DefaultBundleName sizeApp = int( os.popen(command).read().strip("\n").split("\t")[0] ) #------------------------------------------------------ @@ -671,14 +671,14 @@ def MakeTargetDMGFile(msg=""): imageDest = "%s/.background" % MountDir if not os.path.isdir(imageDest): os.mkdir(imageDest) - command = "\cp -p %s %s/%s" % (imageSrc, imageDest, BackgroundPNG) + command = r"\cp -p %s %s/%s" % (imageSrc, imageDest, BackgroundPNG) os.system(command) #-------------------------------------------------------- # (6) Create a symbolic link to /Applications #-------------------------------------------------------- print( ">>> (6) Creating a symbolic link to /Applications..." ) - command = "\ln -s %s %s/%s" % (RootApplications, MountDir, RootApplications) + command = r"\ln -s %s %s/%s" % (RootApplications, MountDir, RootApplications) os.system(command) #-------------------------------------------------------- @@ -702,7 +702,7 @@ def MakeTargetDMGFile(msg=""): print( ">>> (8) Copying the volume icon..." ) iconsSrc = "macbuild/Resources/%s" % VolumeIcons iconsDest = "%s/.VolumeIcon.icns" % MountDir - command1 = "\cp -p %s %s" % (iconsSrc, iconsDest) + command1 = r"\cp -p %s %s" % (iconsSrc, iconsDest) command2 = "SetFile -c icnC %s" % iconsDest os.system(command1) sleep(2) @@ -713,7 +713,7 @@ def MakeTargetDMGFile(msg=""): # (9) Change the permission #-------------------------------------------------------- print( ">>> (9) Changing permission to 755..." ) - command = "\chmod -Rf 755 %s &> /dev/null" % MountDir + command = r"\chmod -Rf 755 %s &> /dev/null" % MountDir os.system(command) #-------------------------------------------------------- From 55e2b27bf67500d0186be29655081fc703ab9235 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 1 May 2025 23:05:01 +0200 Subject: [PATCH 183/392] WIP, MALY reader --- .../streamers/maly/db_plugin/dbMALYReader.cc | 138 +++++++++++++++--- .../streamers/maly/db_plugin/dbMALYReader.h | 2 +- 2 files changed, 121 insertions(+), 19 deletions(-) diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index 29b56c005..4c4e38c5b 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -29,6 +29,8 @@ #include "dbStatic.h" #include "dbShapeProcessor.h" #include "dbTechnology.h" +#include "dbCellMapping.h" +#include "dbLayerMapping.h" #include "tlException.h" #include "tlString.h" @@ -92,19 +94,131 @@ MALYReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) set_layer_map (specific_options.layer_map); set_create_layers (specific_options.create_other_layers); - // @@@ set_keep_layer_names (specific_options.keep_layer_names); set_keep_layer_names (true); MALYData data = read_maly_file (); - - // @@@ - std::cout << data.to_string () << std::endl; - // @@@ + import_data (layout, data); finish_layers (layout); return layer_map_out (); } +void +MALYReader::import_data (db::Layout &layout, const MALYData &data) +{ + db::LayoutLocker locker (&layout); + + // create a new top cell + db::Cell &top_cell = layout.cell (layout.add_cell ("MALY_JOBDECK")); + + // count the number of files to read + size_t n = 0; + for (auto m = data.masks.begin (); m != data.masks.end (); ++m) { + n += m->structures.size (); + } + + tl::RelativeProgress progress (tl::to_string (tr ("Reading layouts")), n, size_t (1)); + + for (auto m = data.masks.begin (); m != data.masks.end (); ++m, ++progress) { + + db::Cell &mask_cell = layout.cell (layout.add_cell (("MASK_" + m->name).c_str ())); + top_cell.insert (db::CellInstArray (mask_cell.cell_index (), db::Trans ())); + + auto lp = open_layer (layout, m->name); + if (! lp.first) { + continue; + } + unsigned int target_layer = lp.second; + + for (auto s = m->structures.begin (); s != m->structures.end (); ++s) { + + db::LoadLayoutOptions options; + + tl::InputStream is (s->path); + db::Layout temp_layout; + db::Reader reader (is); + reader.read (temp_layout, options); + + // configure MEBES reader for compatibility with OASIS.Mask + try { + options.set_option_by_name ("mebes_produce_boundary", false); + options.set_option_by_name ("mebes_data_layer", s->layer); + options.set_option_by_name ("mebes_data_datatype", int (0)); + } catch (...) { + // ignore if there is no MEBES support + } + + db::cell_index_type source_cell; + + if (s->topcell.empty ()) { + + auto t = temp_layout.begin_top_down (); + if (t == temp_layout.end_top_down ()) { + throw tl::Exception (tl::to_string (tr ("Mask pattern file '%s' does not have a top cell")), s->path); + } + + source_cell = *t; + ++t; + if (t != temp_layout.end_top_down ()) { + throw tl::Exception (tl::to_string (tr ("Mask pattern file '%s' does not have a single top cell")), s->path); + } + + } else { + + auto cbm = layout.cell_by_name (s->topcell.c_str ()); + if (! cbm.first) { + throw tl::Exception (tl::to_string (tr ("Mask pattern file '%s' does not have a cell named '%s' as required by mask '%s'")), s->path, s->topcell, m->name); + } + + source_cell = cbm.second; + + } + + int source_layer = layout.get_layer_maybe (db::LayerProperties (s->layer, 0)); + if (source_layer >= 0) { + + // create a host cell for the pattern + + std::string cn = m->name; + if (s->mname.empty ()) { + cn += ".PATTERN"; + } else { + cn += "." + s->mname; + } + db::cell_index_type target_cell = layout.add_cell (cn.c_str ()); + + // create the pattern instance + + db::ICplxTrans trans = db::CplxTrans (layout.dbu ()).inverted () * s->transformation * db::CplxTrans (temp_layout.dbu ()); + db::CellInstArray array; + if (s->nx > 1 || s->ny > 1) { + db::Coord idx = db::coord_traits::rounded (s->dx / layout.dbu ()); + db::Coord idy = db::coord_traits::rounded (s->dy / layout.dbu ()); + array = db::CellInstArray (target_cell, trans, db::Vector (idx, 0), db::Vector (0, idy), s->nx, s->ny); + } else { + array = db::CellInstArray (target_cell, trans); + } + mask_cell.insert (array); + + // move over the shapes from the pattern layout to the target layout + + db::CellMapping cm; + cm.create_single_mapping_full (layout, target_cell, temp_layout, source_cell); + + db::LayerMapping lm; + lm.map (source_layer, target_layer); + + layout.cell (target_cell).move_tree_shapes (temp_layout.cell (source_cell), cm, lm); + + } + + } + + } + + // @@@ TODO: generate titles +} + void MALYReader::unget_record () { @@ -755,7 +869,7 @@ MALYReader::create_structure (const MALYReaderParametersData &mparam, const MALY } db::DCplxTrans mirr (mparam.maskmirror != cparam.maskmirror ? db::DFTrans::m90 : db::DFTrans::r0); - str.transformation = mirr * db::DCplxTrans (data.scale, 0.0, false, data.org + (db::DPoint () - rp)); + str.transformation = mirr * db::DCplxTrans (data.scale, 0.0, false, data.org) * db::DCplxTrans (db::DPoint () - rp); return str; } @@ -847,17 +961,5 @@ MALYReader::warn (const std::string &msg, int wl) } } -void -MALYReader::do_read (db::Layout &layout, db::cell_index_type cell_index, tl::TextInputStream &stream) -{ - try { - - // @@@ - - } catch (tl::Exception &ex) { - error (ex.msg ()); - } -} - } diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h index 8face4692..acbcd8346 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h @@ -221,7 +221,7 @@ private: std::string m_record_returned; std::list m_sections; - void do_read (db::Layout &layout, db::cell_index_type to_cell, tl::TextInputStream &stream); + void import_data (db::Layout &layout, const MALYData &data); MALYData read_maly_file (); tl::Extractor read_record (); void unget_record (); From 92d79d46c13b74188dac25ed1feb3d2a0c2490cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 May 2025 21:31:41 +0000 Subject: [PATCH 184/392] Bump pypa/cibuildwheel from 2.23.2 to 2.23.3 Bumps [pypa/cibuildwheel](https://github.com/pypa/cibuildwheel) from 2.23.2 to 2.23.3. - [Release notes](https://github.com/pypa/cibuildwheel/releases) - [Changelog](https://github.com/pypa/cibuildwheel/blob/main/docs/changelog.md) - [Commits](https://github.com/pypa/cibuildwheel/compare/v2.23.2...v2.23.3) --- updated-dependencies: - dependency-name: pypa/cibuildwheel dependency-version: 2.23.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 02b0beaba..d9f6a3b9b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -60,7 +60,7 @@ jobs: HOST_CCACHE_DIR="$(ccache -k cache_dir)" mkdir -p $HOST_CCACHE_DIR - name: Build wheels # check https://cibuildwheel.readthedocs.io/en/stable/setup/#github-actions - uses: pypa/cibuildwheel@v2.23.2 + uses: pypa/cibuildwheel@v2.23.3 # to supply options, put them in 'env', like: # env: # CIBW_SOME_OPTION: value From 226ba429f1bc0d3b5f85da73c4cef56ac8717a56 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 2 May 2025 13:27:59 +0200 Subject: [PATCH 185/392] WIP: MALY reader, debugging, tests --- src/plugins/streamers/maly/db_plugin/dbMALY.h | 8 +- .../streamers/maly/db_plugin/dbMALYReader.cc | 55 +++++++--- .../streamers/maly/db_plugin/dbMALYReader.h | 8 +- .../maly/unit_tests/dbMALYReaderTests.cc | 103 ++++++++++++------ testdata/maly/MALY_test1.maly | 60 ++++++++++ testdata/maly/MALY_test10.maly | 62 +++++++++++ .../maly/{MALY_TEST.maly => MALY_test2a.maly} | 2 +- testdata/maly/MALY_test2b.maly | 42 +++++++ testdata/maly/MALY_test2c.maly | 41 +++++++ testdata/maly/MALY_test2d.maly | 41 +++++++ testdata/maly/MALY_test2e.maly | 41 +++++++ testdata/maly/MALY_test2f.maly | 41 +++++++ testdata/maly/MALY_test2g.maly | 41 +++++++ testdata/maly/MALY_test2h.maly | 41 +++++++ testdata/maly/MALY_test2i.maly | 41 +++++++ testdata/maly/MALY_test2j.maly | 41 +++++++ testdata/maly/MALY_test2k.maly | 41 +++++++ testdata/maly/MALY_test2l.maly | 41 +++++++ testdata/maly/MALY_test2m.maly | 41 +++++++ testdata/maly/MALY_test2n.maly | 41 +++++++ testdata/maly/maly_test10_au.oas | Bin 0 -> 1705 bytes testdata/maly/test10_oas/pat.oas | Bin 0 -> 519 bytes 22 files changed, 776 insertions(+), 56 deletions(-) create mode 100644 testdata/maly/MALY_test1.maly create mode 100644 testdata/maly/MALY_test10.maly rename testdata/maly/{MALY_TEST.maly => MALY_test2a.maly} (93%) create mode 100644 testdata/maly/MALY_test2b.maly create mode 100644 testdata/maly/MALY_test2c.maly create mode 100644 testdata/maly/MALY_test2d.maly create mode 100644 testdata/maly/MALY_test2e.maly create mode 100644 testdata/maly/MALY_test2f.maly create mode 100644 testdata/maly/MALY_test2g.maly create mode 100644 testdata/maly/MALY_test2h.maly create mode 100644 testdata/maly/MALY_test2i.maly create mode 100644 testdata/maly/MALY_test2j.maly create mode 100644 testdata/maly/MALY_test2k.maly create mode 100644 testdata/maly/MALY_test2l.maly create mode 100644 testdata/maly/MALY_test2m.maly create mode 100644 testdata/maly/MALY_test2n.maly create mode 100644 testdata/maly/maly_test10_au.oas create mode 100644 testdata/maly/test10_oas/pat.oas diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.h b/src/plugins/streamers/maly/db_plugin/dbMALY.h index bdcbaae7b..84f2b3347 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALY.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.h @@ -61,7 +61,7 @@ public: /** * @brief A class representing a title field on a mask */ -class MALYTitle +class DB_PUBLIC MALYTitle { public: /** @@ -138,7 +138,7 @@ public: /** * @brief A class representing a structure (pattern) on a mask */ -class MALYStructure +class DB_PUBLIC MALYStructure { public: /** @@ -220,7 +220,7 @@ public: /** * @brief A class representing one mask */ -class MALYMask +class DB_PUBLIC MALYMask { public: /** @@ -261,7 +261,7 @@ public: /** * @brief A class representing the MALY file */ -class MALYData +class DB_PUBLIC MALYData { public: /** diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index 4c4e38c5b..562cefba2 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -165,7 +165,7 @@ MALYReader::import_data (db::Layout &layout, const MALYData &data) } else { - auto cbm = layout.cell_by_name (s->topcell.c_str ()); + auto cbm = temp_layout.cell_by_name (s->topcell.c_str ()); if (! cbm.first) { throw tl::Exception (tl::to_string (tr ("Mask pattern file '%s' does not have a cell named '%s' as required by mask '%s'")), s->path, s->topcell, m->name); } @@ -174,27 +174,31 @@ MALYReader::import_data (db::Layout &layout, const MALYData &data) } - int source_layer = layout.get_layer_maybe (db::LayerProperties (s->layer, 0)); + int source_layer = temp_layout.get_layer_maybe (db::LayerProperties (s->layer, 0)); if (source_layer >= 0) { // create a host cell for the pattern std::string cn = m->name; - if (s->mname.empty ()) { - cn += ".PATTERN"; + if (s->topcell.empty ()) { + if (s->mname.empty ()) { + cn += ".PATTERN"; + } else { + cn += "." + s->mname; + } } else { - cn += "." + s->mname; + cn += "." + s->topcell; } db::cell_index_type target_cell = layout.add_cell (cn.c_str ()); // create the pattern instance - db::ICplxTrans trans = db::CplxTrans (layout.dbu ()).inverted () * s->transformation * db::CplxTrans (temp_layout.dbu ()); + db::ICplxTrans trans = db::CplxTrans (layout.dbu ()).inverted () * s->transformation * db::CplxTrans (layout.dbu ()); db::CellInstArray array; if (s->nx > 1 || s->ny > 1) { db::Coord idx = db::coord_traits::rounded (s->dx / layout.dbu ()); db::Coord idy = db::coord_traits::rounded (s->dy / layout.dbu ()); - array = db::CellInstArray (target_cell, trans, db::Vector (idx, 0), db::Vector (0, idy), s->nx, s->ny); + array = db::CellInstArray (target_cell, trans, trans.fp_trans () * db::Vector (idx, 0), trans.fp_trans () * db::Vector (0, idy), s->nx, s->ny); } else { array = db::CellInstArray (target_cell, trans); } @@ -277,6 +281,7 @@ MALYReader::read_record_internal () while (! m_stream.at_end () && (m_stream.get_char () != '*' || m_stream.peek_char () != '/')) ; if (m_stream.at_end ()) { + m_last_record_line = m_stream.line_number (); error (tl::to_string (tr ("/*...*/ comment not closed"))); } m_stream.get_char (); // eat trailing "/" @@ -288,19 +293,25 @@ MALYReader::read_record_internal () } if (c == '\n') { + if (m_stream.peek_char () == '+') { + + if (tl::Extractor (rec.c_str ()).at_end ()) { + m_last_record_line = m_stream.line_number (); + error (tl::to_string (tr ("'+' character at beginning of new record - did you mean to continue a line?"))); + } + // continuation line m_stream.get_char (); // eat "+" if (m_stream.at_end ()) { break; } - c = m_stream.get_char (); + } else { break; } - } - if (c == '"' || c == '\'') { + } else if (c == '"' || c == '\'') { rec += c; @@ -314,16 +325,19 @@ MALYReader::read_record_internal () break; } else if (c == '\\') { if (m_stream.at_end ()) { - error (tl::to_string (tr ("Unexpected end of file inside quotee string"))); + m_last_record_line = m_stream.line_number (); + error (tl::to_string (tr ("Unexpected end of file inside quoted string"))); } c = m_stream.get_char (); rec += c; } else if (c == '\n') { + m_last_record_line = m_stream.line_number (); error (tl::to_string (tr ("Line break inside quoted string"))); } } if (quote) { + m_last_record_line = m_stream.line_number (); error (tl::to_string (tr ("Unexpected end of file inside quotee string"))); } @@ -512,11 +526,14 @@ MALYReader::read_parameter (MALYReaderParametersData &data) std::string format, path; ex.read_word_or_quoted (format); - ex.read_word_or_quoted (path, ".\\/+-"); + ex.read_word_or_quoted (path, ".\\/+-_"); ex.expect_end (); data.roots.push_back (std::make_pair (format, path)); + } else if (begin_section (ex)) { + warn (tl::to_string (tr ("Unknown section ignored"))); + skip_section (); } else { warn (tl::to_string (tr ("Unknown record ignored"))); } @@ -564,6 +581,9 @@ MALYReader::read_title (MALYReaderTitleData &data) ex.expect_end (); + } else if (begin_section (ex)) { + warn (tl::to_string (tr ("Unknown section ignored"))); + skip_section (); } else { warn (tl::to_string (tr ("Unknown record ignored"))); } @@ -639,6 +659,9 @@ MALYReader::read_strgroup (MALYReaderStrGroupData &data) ex.expect_end (); + } else if (begin_section (ex)) { + warn (tl::to_string (tr ("Unknown section ignored"))); + skip_section (); } else { warn (tl::to_string (tr ("Unknown record ignored"))); } @@ -674,6 +697,7 @@ MALYReader::read_mask (MALYReaderMaskData &mask) read_strgroup (mask.strgroups.back ()); } else if (begin_section (ex)) { + warn (tl::to_string (tr ("Unknown section ignored"))); skip_section (); } else { warn (tl::to_string (tr ("Unknown record ignored"))); @@ -718,6 +742,9 @@ MALYReader::read_maskset (MALYData &data) ex.expect_end (); read_mask (cmask); + } else if (begin_section (ex)) { + warn (tl::to_string (tr ("Unknown section ignored"))); + skip_section (); } else { warn (tl::to_string (tr ("Unknown record ignored"))); } @@ -788,9 +815,9 @@ MALYReader::create_masks (const MALYReaderMaskData &cmask, const std::listparameters.array_base; if (array_base == MALYReaderParametersData::BaseNotSet) { - array_base = cmask.parameters.base; + array_base = cmask.parameters.array_base; } if (array_base == MALYReaderParametersData::BaseNotSet) { array_base = MALYReaderParametersData::Center; diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h index acbcd8346..1cf4787c7 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h @@ -138,6 +138,13 @@ public: */ virtual void warn (const std::string &txt, int wl = 1); + /** + * @brief Reads the MALY file into a MALYData structure + * + * This method is provided for test purposes mainly. + */ + MALYData read_maly_file (); + private: struct MALYReaderTitleSpec { @@ -222,7 +229,6 @@ private: std::list m_sections; void import_data (db::Layout &layout, const MALYData &data); - MALYData read_maly_file (); tl::Extractor read_record (); void unget_record (); std::string read_record_internal (); diff --git a/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc b/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc index cb3f749c3..fdb94953a 100644 --- a/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc +++ b/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc @@ -24,6 +24,7 @@ #include "dbMALYReader.h" #include "dbLayoutDiff.h" #include "dbWriter.h" +#include "dbTestSupport.h" #include "tlUnitTest.h" #include @@ -54,7 +55,7 @@ static void run_test (tl::TestBase *_this, const std::string &base, const char * options.set_options (opt); db::Manager m (false); - db::Layout layout (&m), layout2 (&m), layout_au (&m); + db::Layout layout (&m); { std::string fn (base); @@ -65,44 +66,74 @@ static void run_test (tl::TestBase *_this, const std::string &base, const char * reader.read (layout, options); } - tl_assert (layout.begin_top_down () != layout.end_top_down ()); - std::string tc_name = layout.cell_name (*layout.begin_top_down ()); + std::string fn_au (base); + fn_au += "/maly/"; + fn_au += file_au; - // normalize the layout by writing to OASIS and reading from .. - - std::string tmp_oas_file = _this->tmp_file (tl::sprintf ("%s.oas", tc_name)); - - { - tl::OutputStream stream (tmp_oas_file); - db::SaveLayoutOptions options; - options.set_format ("OASIS"); - db::Writer writer (options); - writer.write (layout, stream); - } - - { - tl::InputStream stream (tmp_oas_file); - db::Reader reader (stream); - reader.read (layout2); - } - - { - std::string fn (base); - fn += "/maly/"; - fn += file_au; - tl::InputStream stream (fn); - db::Reader reader (stream); - reader.read (layout_au); - } - - bool equal = db::compare_layouts (layout2, layout_au, db::layout_diff::f_boxes_as_polygons | db::layout_diff::f_verbose | db::layout_diff::f_flatten_array_insts, 1); - if (! equal) { - _this->raise (tl::sprintf ("Compare failed after reading - see %s vs %s\n", tmp_oas_file, file_au)); - } + db::compare_layouts (_this, layout, fn_au, db::WriteOAS); } -TEST(1) +TEST(1_Basic) { - run_test (_this, tl::testdata (), "MALY_TEST.maly", "mag_test_au.oas"); + std::string fn (tl::testdata ()); + fn += "/maly/MALY_test1.maly"; + + tl::InputStream stream (fn); + db::MALYReader reader (stream); + + db::MALYData data = reader.read_maly_file (); + + EXPECT_EQ (data.to_string (), + "Mask A\n" + " Size 127000\n" + " Title \"\" m90 50,-50 1,1,1 [Standard]\n" + " Title \"\" r0 0,-50 1,1,1 [Standard]\n" + " Title \"MaskA1\" r0 -50,50 1,1,1 [Standard]\n" + " Title \"WITH \"QUOTES\"\" m45 50,0 1,1,1 [Standard]\n" + " Ref A1.oas{CHIP_A}(1) (0,0;10,10) m90 *1 20,0\n" + " Ref A2.oas{CHIP_A}(2) ename(e001) dname(d001) (0,0;50,50) m90 *0.8 20,0 [2x5,1x2]\n" + " Ref B3.oas{CHIP_A}(2) (0,0;12,12) m90 *1 20,0" + ) +} + +static std::string run_test_with_error (tl::TestBase * /*_this*/, const std::string &file) +{ + std::string fn (tl::testdata ()); + fn += "/maly/"; + fn += file; + + tl::InputStream stream (fn); + db::MALYReader reader (stream); + + try { + reader.read_maly_file (); + tl_assert (false); + } catch (tl::Exception &ex) { + tl::error << ex.msg (); + return ex.msg (); + } +} + +TEST(2_Errors) +{ + EXPECT_EQ (run_test_with_error (_this, "MALY_test2a.maly").find ("Line break inside quoted string (line=17,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2b.maly").find ("/*...*/ comment not closed (line=43,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2c.maly").find ("Expected value STANDARD or NATIVE for FONT (line=7,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2d.maly").find ("Unknown base specification: NOVALIDBASE (line=8,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2e.maly").find ("Expected end of text here: NOVALIDKEY .. (line=15,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2f.maly").find ("Expected 'Y' or 'NONE' for MIRROR spec (line=15,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2g.maly").find ("Expected end of text here: UNEXPECTED (line=20,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2h.maly").find ("Expected value Y or NONE for MASKMIRROR (line=23,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2i.maly").find ("Expected end of text here: UNEXPECTED (line=29,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2j.maly").find ("Expected end of text here: NOVALIDKEY .. (line=30,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2k.maly").find ("Expected a real number here: SCALE 0.80 .. (line=31,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2l.maly").find ("Expected 'PARAMETER' here: CMASK (line=19,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2m.maly").find ("Expected 'CMASK' here: TITLE (line=18,"), size_t (0)); + EXPECT_EQ (run_test_with_error (_this, "MALY_test2n.maly").find ("Header expected ('BEGIN MALY') (line=2, "), size_t (0)); +} + +TEST(10_BasicLayout) +{ + run_test (_this, tl::testdata (), "MALY_test10.maly", "maly_test10_au.oas"); } diff --git a/testdata/maly/MALY_test1.maly b/testdata/maly/MALY_test1.maly new file mode 100644 index 000000000..92512ae14 --- /dev/null +++ b/testdata/maly/MALY_test1.maly @@ -0,0 +1,60 @@ + +// A comment + +/* +A multi-line comment +BEGIN MALY 1.1 -- not seend +*/ + +BEGIN MALY 1.1 // ignored + BEGIN MASKSET + BEGIN CMASK + AN UNKNOWN MASK RECORD + BEGIN NONSENSE + SOMETHING INSIDE A NONSENSE RECORD + BEGIN MORE + SOMETHING INSIDE ANOTHER NONSENSE RECORD + END MORE + END NONSENSE + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE "/home/NATIVE" + REFERENCE TOOL a.para + AN UNKNOWN PARAMETER + END PARAMETER + BEGIN TITLE + AN UNKNOWN TITLE RECORD + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "WITH \"QUOTES\"" 50.0 0 ++SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 ++// with a continuation line + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + +// A comment at the end diff --git a/testdata/maly/MALY_test10.maly b/testdata/maly/MALY_test10.maly new file mode 100644 index 000000000..0e70b1ee2 --- /dev/null +++ b/testdata/maly/MALY_test10.maly @@ -0,0 +1,62 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 7 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK test10_oas + MASKMIRROR NONE + END PARAMETER + END CMASK + BEGIN MASK A + BEGIN STRGROUP G1 + SREF pat.oas TOP 1 ORG -2000.0 0 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 0.8 + AREF pat.oas TOP 2 ORG 1000 0 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 1.0 ITERATION 2 5 1500 2000 + END STRGROUP + BEGIN STRGROUP G2 + SREF pat.oas TOP 3 ORG -3000.0 2000 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 2.0 + END STRGROUP + END MASK + BEGIN MASK B + BEGIN PARAMETER + MASKMIRROR Y + END PARAMETER + BEGIN STRGROUP G1 + SREF pat.oas TOP 1 ORG -2000.0 0 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 0.8 + AREF pat.oas TOP 2 ORG 1000 0 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 1.0 ITERATION 2 5 1500 2000 + END STRGROUP + BEGIN STRGROUP G2 + SREF pat.oas TOP 3 ORG -3000.0 2000 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 2.0 + END STRGROUP + END MASK + BEGIN MASK C + BEGIN PARAMETER + ARYBASE LOWERLEFT + BASE LOWERLEFT + END PARAMETER + BEGIN STRGROUP G1 + SREF pat.oas TOP 1 ORG -2000.0 0 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 0.8 + AREF pat.oas TOP 2 ORG 1000 0 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 1.0 ITERATION 2 5 1500 2000 + END STRGROUP + BEGIN STRGROUP G2 + SREF pat.oas TOP 3 ORG -3000.0 2000 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 2.0 + END STRGROUP + END MASK + BEGIN MASK D + BEGIN PARAMETER + ARYBASE CENTER + BASE CENTER + END PARAMETER + BEGIN STRGROUP G1 + SREF pat.oas TOP 1 ORG -2000.0 0 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 0.8 + AREF pat.oas TOP 2 ORG 1000 0 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 1.0 ITERATION 2 5 1500 2000 + END STRGROUP + BEGIN STRGROUP G2 + SREF pat.oas TOP 3 ORG -3000.0 2000 SIZE -250.0 -250.0 1000.0 1000.0 SCALE 2.0 + END STRGROUP + END MASK + END MASKSET +END MALY diff --git a/testdata/maly/MALY_TEST.maly b/testdata/maly/MALY_test2a.maly similarity index 93% rename from testdata/maly/MALY_TEST.maly rename to testdata/maly/MALY_test2a.maly index ca7b71601..53f459c17 100644 --- a/testdata/maly/MALY_TEST.maly +++ b/testdata/maly/MALY_test2a.maly @@ -14,7 +14,7 @@ BEGIN MALY 1.1 BEGIN TITLE DATE 50.0 -50.0 MIRROR Y ROTATE 180 SERIAL 0 -50.0 - STRING TEST 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + STRING "TEST\" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 END TITLE END CMASK BEGIN MASK A diff --git a/testdata/maly/MALY_test2b.maly b/testdata/maly/MALY_test2b.maly new file mode 100644 index 000000000..19d98c940 --- /dev/null +++ b/testdata/maly/MALY_test2b.maly @@ -0,0 +1,42 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + /* not terminated + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2c.maly b/testdata/maly/MALY_test2c.maly new file mode 100644 index 000000000..671061bef --- /dev/null +++ b/testdata/maly/MALY_test2c.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT NOVALIDFONT // wrong keyword + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2d.maly b/testdata/maly/MALY_test2d.maly new file mode 100644 index 000000000..ef775d98a --- /dev/null +++ b/testdata/maly/MALY_test2d.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE NOVALIDBASE // not a valid keyword + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2e.maly b/testdata/maly/MALY_test2e.maly new file mode 100644 index 000000000..212ed0556 --- /dev/null +++ b/testdata/maly/MALY_test2e.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 NOVALIDKEYWORD + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2f.maly b/testdata/maly/MALY_test2f.maly new file mode 100644 index 000000000..1fc9256b5 --- /dev/null +++ b/testdata/maly/MALY_test2f.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR NOVALIDSPEC ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2g.maly b/testdata/maly/MALY_test2g.maly new file mode 100644 index 000000000..041baf271 --- /dev/null +++ b/testdata/maly/MALY_test2g.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A UNEXPECTED + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2h.maly b/testdata/maly/MALY_test2h.maly new file mode 100644 index 000000000..24b8c96a9 --- /dev/null +++ b/testdata/maly/MALY_test2h.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR NOVALIDKEYWORD + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2i.maly b/testdata/maly/MALY_test2i.maly new file mode 100644 index 000000000..5c02ca50e --- /dev/null +++ b/testdata/maly/MALY_test2i.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 UNEXPECTED + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2j.maly b/testdata/maly/MALY_test2j.maly new file mode 100644 index 000000000..7d561956e --- /dev/null +++ b/testdata/maly/MALY_test2j.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 NOVALIDKEYWORD + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2k.maly b/testdata/maly/MALY_test2k.maly new file mode 100644 index 000000000..170e50cab --- /dev/null +++ b/testdata/maly/MALY_test2k.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 // missing argument ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2l.maly b/testdata/maly/MALY_test2l.maly new file mode 100644 index 000000000..1b3e40065 --- /dev/null +++ b/testdata/maly/MALY_test2l.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + // missing closing section END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2m.maly b/testdata/maly/MALY_test2m.maly new file mode 100644 index 000000000..5a9530e3d --- /dev/null +++ b/testdata/maly/MALY_test2m.maly @@ -0,0 +1,41 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + // missing opening BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/MALY_test2n.maly b/testdata/maly/MALY_test2n.maly new file mode 100644 index 000000000..b80eafdad --- /dev/null +++ b/testdata/maly/MALY_test2n.maly @@ -0,0 +1,41 @@ + +BEGIN WRONG 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 5 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK /home/MASK + ROOT NATIVE /home/NATIVE + REFERENCE TOOL a.para + END PARAMETER + BEGIN TITLE + DATE 50.0 -50.0 MIRROR Y ROTATE 180 + SERIAL 0 -50.0 + STRING "TEST" 50.0 0 SIZE 1.0 1.0 1.0 MIRROR Y ROTATE 90 + END TITLE + END CMASK + BEGIN MASK A + BEGIN PARAMETER + ROOT OASIS.MASK /home/mask1 + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + DATE OFF + STRING MaskA1 -50.0 50.00 + END TITLE + BEGIN STRGROUP G1 + SREF A1.oas CHIP_A 1 ORG -20.0 0 SIZE 0.0 0.0 10.0 10.0 + AREF A2.oas CHIP_A 2 ORG -20.0 0 SIZE 0.0 0.0 50.0 50.0 ++ SCALE 0.800 ITERATION 5 2 2.0 1.0 + PROPERTY DNAME d001 ENAME e001 + END STRGROUP + BEGIN STRGROUP G2 + SREF B3.oas CHIP_A 2 ORG -20.0 0.0 SIZE 0.0 0.0 12.0 12.0 + END STRGROUP + END MASK + END MASKSET +END MALY + diff --git a/testdata/maly/maly_test10_au.oas b/testdata/maly/maly_test10_au.oas new file mode 100644 index 0000000000000000000000000000000000000000..89f1533a83498c4122fbf50e6441587ad693bace GIT binary patch literal 1705 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKC?n3q!6L)YEF;ds&!EJR>XUoMnybM;fb~F; z;|1o9>4KX(8~>b$4qRRSKbqrK6n{_8gJqftG@5`%@xYguLQ5Cs4_D`M3`5Qmrqog z2_eG6%f-tss?3NGVdZ7!l@|OVb8r?95HT{d0F7#R#3^#0g<(N2+rmR~K*Y_kVTPLo zucY7)DZZ62$${8FMlT!C0A6t=1}_)kKZ=C~V*CPz zMw?}39KHOZBlya!*2anN4?HqE#{4`~?w?-X4K2nL9Sarq6dfB7@gt8dg?Xu*j(Ai- z6`OK^NCyM2h~N)V3@0&TxLio^hcIDfM7Uf4>hcP7m(L{C<-CGF_%NKrgyC{-B?fmV z;Xen93&c1OA@#&g_;6F)w$}D14IQ%+5A&FF26DUgvG4B|U-L2lj@HHxyCnmX4k}s- zNS}C-%KVCrftM5F@Pno34(}w@;cS9G*fAW=h~aP+!5^%Il@Sr-Oi-8Cpu2n`sV)~& zVsNzrCOgYQ#s9~?W$N_soZ;_tH7^O{teu>ekd)rg)Xwj3q;Fx{9Qb0k#qTvU6+T>& z)2saOkY`!*^H2s}exQC!;a_Tp4jehhz{cRZLNqs;udIr$>* z+)$_cEH~26vNTTrR`9jAJm1(XXDR~&FNe@~UIr$HEsPDb-!U~z`obvqm%FjEih+ZH zx#i6c?xqO~`GuZyvd@|c0x#`B3W17v8I>694TOIkI|jD3ck_KiQ=ealG=4NMUAVcc zabn}ogBvpxykAHPLmj+OVub($FN5F#F~I};f(JMR4**U0!*r3+b;52w#y}C**yE-u zRlPyF_xg+Eq>_z$#*tvoeZ&zwmj-_NYGHraU0b5h#6=j9a=D!&@GD>Uy<0w~{K+hm1Zo^9E>$~10#PUq))3aZ+Ba0#Y)n4Ig>VaJ=_fKTkXFmF}KjCP4@kdrB X#!H;Ql1!Pgn6Z(Ok)dH^0R{#DaEYlE literal 0 HcmV?d00001 diff --git a/testdata/maly/test10_oas/pat.oas b/testdata/maly/test10_oas/pat.oas new file mode 100644 index 0000000000000000000000000000000000000000..2818a3a183fc24e5bc0de03ad8d6abb978ab10d1 GIT binary patch literal 519 zcmY!lcJ=kt^>+;R4CduxWH!_@U@&5o5oh9Hk>O{S5oh6NP+~~+$vtDu)nFjNdZ5Vh z0&~Z7!OfkGe@;XPt}g!{&2cM=zo+NHGR=)u{H8MxJUDPI`*Pm)GYN6}N^Oh!HqKys zYqNNB?6mWJpH?$xY~A~lnUguhKY*7(RGFE9k!gb81V3IzC5DP@;XelrF6J+iS2jM) zvY)w3>HADct1IDhX)H>4ZI Date: Fri, 2 May 2025 14:25:34 +0200 Subject: [PATCH 186/392] Added title support, tests, debugging --- .../streamers/maly/db_plugin/dbMALYReader.cc | 60 ++++++++++++++---- .../streamers/maly/db_plugin/dbMALYReader.h | 5 +- .../maly/unit_tests/dbMALYReaderTests.cc | 12 ++-- testdata/maly/MALY_test11.maly | 38 +++++++++++ testdata/maly/maly_test11_au.oas | Bin 0 -> 3437 bytes 5 files changed, 95 insertions(+), 20 deletions(-) create mode 100644 testdata/maly/MALY_test11.maly create mode 100644 testdata/maly/maly_test11_au.oas diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index 562cefba2..5f4d88fb2 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -31,6 +31,7 @@ #include "dbTechnology.h" #include "dbCellMapping.h" #include "dbLayerMapping.h" +#include "dbGlyphs.h" #include "tlException.h" #include "tlString.h" @@ -218,9 +219,30 @@ MALYReader::import_data (db::Layout &layout, const MALYData &data) } + // produce the titles + + for (auto t = m->titles.begin (); t != m->titles.end (); ++t) { + + const double one_mm = 1000.0; + + auto gen = db::TextGenerator::default_generator (); + double scale = std::min (t->width * one_mm / (gen->width () * gen->dbu ()), t->height * one_mm / (gen->height () * gen->dbu ())); + + auto &s = t->string; + int len = int (s.size ()); + db::DVector shift (-t->width * one_mm * len * 0.5, -t->height * one_mm * 0.5); + double char_spacing = t->width * one_mm - gen->width () * gen->dbu () * scale; + + db::Region text = gen->text_as_region (s, layout.dbu (), scale, false, 0.0, char_spacing, 0.0); + text.transform (db::Trans (db::CplxTrans (layout.dbu ()).inverted () * shift)); + text.transform (db::CplxTrans (layout.dbu ()).inverted () * db::DCplxTrans (t->transformation) * db::CplxTrans (layout.dbu ())); + + text.insert_into (&layout, mask_cell.cell_index (), target_layer); + + } + } - // @@@ TODO: generate titles } void @@ -376,6 +398,10 @@ MALYReader::extract_title_trans (tl::Extractor &ex, MALYReaderTitleSpec &spec) ex.read (spec.width); ex.read (spec.height); ex.read (spec.pitch); + } else { + spec.width = 1.0; + spec.height = 1.0; + spec.pitch = 1.0; } if (ex.test ("MIRROR")) { @@ -394,7 +420,7 @@ MALYReader::extract_title_trans (tl::Extractor &ex, MALYReaderTitleSpec &spec) rot = (a / 90) % 4; } - spec.trans = db::DTrans (rot, ymirror, db::DVector (x, y)); + spec.trans = db::DTrans (rot, false, db::DVector (x, y)) * db::DTrans (ymirror ? db::DFTrans::m90 : db::DFTrans::r0); } MALYReader::MALYReaderParametersData::Base @@ -552,6 +578,8 @@ MALYReader::read_title (MALYReaderTitleData &data) break; } else if (ex.test ("DATE")) { + data.date_spec.given = true; + if (ex.test ("OFF")) { data.date_spec.enabled = false; } else { @@ -562,6 +590,8 @@ MALYReader::read_title (MALYReaderTitleData &data) } else if (ex.test ("SERIAL")) { + data.serial_spec.given = true; + if (ex.test ("OFF")) { data.serial_spec.enabled = false; } else { @@ -779,31 +809,33 @@ MALYReader::create_masks (const MALYReaderMaskData &cmask, const std::listparameters.maskmirror != cmask.parameters.maskmirror); + const MALYReaderTitleSpec *date_spec = 0; - if (i->title.date_spec.enabled) { + if (i->title.date_spec.given) { date_spec = &i->title.date_spec; - } else if (cmask.title.date_spec.enabled) { + } else if (cmask.title.date_spec.given) { date_spec = &cmask.title.date_spec; } - if (date_spec) { - m.titles.push_back (create_title (MALYTitle::Date, *date_spec, font, std::string (""))); + if (date_spec && date_spec->enabled) { + m.titles.push_back (create_title (MALYTitle::Date, *date_spec, font, maskmirror, std::string (""))); } const MALYReaderTitleSpec *serial_spec = 0; - if (i->title.serial_spec.enabled) { + if (i->title.serial_spec.given) { serial_spec = &i->title.serial_spec; - } else if (cmask.title.serial_spec.enabled) { + } else if (cmask.title.serial_spec.given) { serial_spec = &cmask.title.serial_spec; } - if (date_spec) { - m.titles.push_back (create_title (MALYTitle::Serial, *serial_spec, font, std::string (""))); + if (serial_spec && serial_spec->enabled) { + m.titles.push_back (create_title (MALYTitle::Serial, *serial_spec, font, maskmirror, std::string (""))); } for (auto t = i->title.string_titles.begin (); t != i->title.string_titles.end (); ++t) { - m.titles.push_back (create_title (MALYTitle::String, t->second, font, t->first)); + m.titles.push_back (create_title (MALYTitle::String, t->second, font, maskmirror, t->first)); } for (auto t = cmask.title.string_titles.begin (); t != cmask.title.string_titles.end (); ++t) { - m.titles.push_back (create_title (MALYTitle::String, t->second, font, t->first)); + m.titles.push_back (create_title (MALYTitle::String, t->second, font, maskmirror, t->first)); } MALYReaderParametersData::Base base = i->parameters.base; @@ -839,11 +871,11 @@ MALYReader::create_masks (const MALYReaderMaskData &cmask, const std::list &masks, MALYData &data); MALYStructure create_structure (const MALYReaderParametersData &mparam, const MALYReaderParametersData &cparam, const MALYReaderStrRefData &data, const std::string &strgroup_name, MALYReaderParametersData::Base base, MALYReaderParametersData::Base array_base); std::string resolve_path (const MALYReaderParametersData ¶m, const std::string &path); diff --git a/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc b/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc index fdb94953a..438613561 100644 --- a/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc +++ b/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc @@ -86,10 +86,9 @@ TEST(1_Basic) EXPECT_EQ (data.to_string (), "Mask A\n" " Size 127000\n" - " Title \"\" m90 50,-50 1,1,1 [Standard]\n" - " Title \"\" r0 0,-50 1,1,1 [Standard]\n" - " Title \"MaskA1\" r0 -50,50 1,1,1 [Standard]\n" - " Title \"WITH \"QUOTES\"\" m45 50,0 1,1,1 [Standard]\n" + " Title \"\" m90 0,-50 1,1,1 [Standard]\n" + " Title \"MaskA1\" m90 50,50 1,1,1 [Standard]\n" + " Title \"WITH \"QUOTES\"\" r270 -50,0 1,1,1 [Standard]\n" " Ref A1.oas{CHIP_A}(1) (0,0;10,10) m90 *1 20,0\n" " Ref A2.oas{CHIP_A}(2) ename(e001) dname(d001) (0,0;50,50) m90 *0.8 20,0 [2x5,1x2]\n" " Ref B3.oas{CHIP_A}(2) (0,0;12,12) m90 *1 20,0" @@ -137,3 +136,8 @@ TEST(10_BasicLayout) run_test (_this, tl::testdata (), "MALY_test10.maly", "maly_test10_au.oas"); } +TEST(11_Titles) +{ + run_test (_this, tl::testdata (), "MALY_test11.maly", "maly_test11_au.oas"); +} + diff --git a/testdata/maly/MALY_test11.maly b/testdata/maly/MALY_test11.maly new file mode 100644 index 000000000..4bfc631f1 --- /dev/null +++ b/testdata/maly/MALY_test11.maly @@ -0,0 +1,38 @@ + +BEGIN MALY 1.1 + BEGIN MASKSET + BEGIN CMASK + BEGIN PARAMETER + MASKSIZE 7 + FONT STANDARD + BASE ORIGIN + ARYBASE ORIGIN + ROOT OASIS.MASK test10_oas + MASKMIRROR NONE + END PARAMETER + BEGIN TITLE + DATE -50000 -5000 MIRROR Y ROTATE 90 + SERIAL -50000 -10000 + STRING "A STRING TITLE UPSIDE DOWN" 0 -50000 MIRROR Y ROTATE 180 + STRING "A STRING TITLE" 0 -51500 + END TITLE + END CMASK + BEGIN MASK A + END MASK + BEGIN MASK B + BEGIN PARAMETER + MASKMIRROR Y + END PARAMETER + BEGIN TITLE + SERIAL OFF + STRING "A STRING TITLE FOR MASK B" 5000 -53000 + END TITLE + END MASK + BEGIN MASK C + BEGIN TITLE + DATE OFF + STRING "A STRING TITLE FOR MASK C" 0 -55000 SIZE 1.5 2.0 3.0 + END TITLE + END MASK + END MASKSET +END MALY diff --git a/testdata/maly/maly_test11_au.oas b/testdata/maly/maly_test11_au.oas new file mode 100644 index 0000000000000000000000000000000000000000..cb76b91c4d0e6a08db546844e97ade3454571b88 GIT binary patch literal 3437 zcmd_pha(h>!@%*oBlBcu@4b#AT)AX(IO~ifC-X?MXYz1l9Vf}os9a?46>)ZCSC^SR zdQLgxF-p9@|Kt6B{)Vp<$`FZAveZQ(P%KP98Yn~suuBgV$jLDtKIIO1eY5j0c!w5#|nmgV%wbU~()Hf3c$O~>@Lp`yc*j1d> zQo6;sxu|gdIyMxGr8^*T0a$3n0o@!+3g9zovY^O3O;X_KmdfQv($v0+18K+nQM37oVb=E>>s-oyh@i^bZb3PR7&=Syw}( zV?<7uBlhp?lsHffS_WFAK_gv9lZvz%(dQ^47wnvLK;u2yPU-ns`p3QIJpA(a$nN`q zuKe1wli31#Bkocj*cLPMb9e?fjkj-`@@epY?{c8g-7@vJmr0HzSibGEKE-E-zlUlv z>Y3_0GD&G9&1jD;NB!Yk9MQ^C1F0-r8F!L#StNkO&Y9V$rXgp)sEr&F7jRK38<(;lT?$e?tao- zrQmFLYRe7ub=GYoD!YpnQmVEKbG0=Pxq_>YFl~HmUWKYKQ0%p^4ulU3P>`18YM0RQ z2PWn$0Nu12YG1>*?-Kcq7iRA{bB{Rb;(gX0JXX`L_7y|<3Ynfd1C zOLP979|@h9uF8GscuM^M^TxDao}%41Xkq5EIi|@rHQUb3KLhi_dL~|SYtQIKJI@WnPtV}fdx#7ED|$!_nLdrTrGbO z&tdAa*EK!uFlMCzs8urc=d%D2$H?POfk(m>ZMCv%31#Y8=~B!^u0iX(-C&KO1V;(T zSAjZt&+000R=!Z?4aobdgka@=F(5~>iHU%$uP(9=BE`*XD_L5M_26Y}$>x~D=gVfe zsVMx`W20?lfVKI7I}es!s1J!!yKho@CXWB%3A2htYCfx5BtP&mChz)+h`u$`tOZmu z;rf&KzmI9t{#M@4euWT!sD=wspXWpkC3N=q2G{KxGUY*{<@6hjfGrOh0HN#j3nP*8>U*( z+ghXf9*PdO2JD5_y6lYX23&WhPKCpoTV93r>}#z=t?Wd7--#gzAN)Dip!7ArQfqrh z@Xq>So>k3dv1dM8T&kM8H|Qf>Fd^9}>we#?lxb=zeXuOyF;@k7)4Nvf)y#u4LJ#wZGnAd9jDr@&TNcElfx z9W8ro#?GM-IsmN;8#9R|Gv$!a+t!osL=xcR4T1Ei>4Su`yN;- z9bj!6)s8Jfp`jk{{cF0EwLltP?Ch{y3D9`ruBXH0BprH;L;Ru6ZHB}c_CckZ6O)l$bO z(ocCjulC>l4|(HT;WnA|@@raaJC8Nu151M-m1x@*heQO&E189Srs*~i>s`b)%LrM> zVGO=2g&6%$DOeKjvMup=XpX-42o1pRfA$t{#KbJq)4^228%7OJ=@gG;6@ zcl?=})!sXVna43OgnCP94XA@9e0R4pudjUKu<)v33{jK4ig~Rd3DDnkPrn!9L!WkTm!sW5RrM<|PlSh*m{gGpQzVM=Z zc=G1x#Qu!>R&Kt#WJru=zd+b|o|Mlfhy8_dsr#r#&@_dp_-Ca3h;FtixQ{~gOV;H3 z<hHhRybLeXAArYdU3XO;(zXp} zV(A0-A)$at?U^U3u}KJL0M3z*7AC9UFx{xczEO;dW3-(=)*Y@rHfqJ#0M3twfE<0E zUGOpZ2fKfL%0<2MxdPa1NrT$a1({$)%Yqy=_foDVu=G~a&8MteMIUacjxsq|G?IhnyQ!k(D{Pu8WC7Vy*akH zk=*1oT&gO4nTNb}G=UVIxR~FYt`cbDd=vcQ;S45m#fnP2d>k0HWg4$F3N~mPoTw7p znJ&JehVmfeC5HGgLc|C56$lHB?@I*rP7UW@pDaJ$P%BO!P3X*m@69~Pw#wW85v&)b zlNY?lTySH^Oe%-JkU2!&nuo!)Kawqn36fiQ`ZZZYER2Igz3ufw@HwZzN)1_@Q|SB1 zV$c&|>j+~Luh>?cPM{Z9B3|b(n=K+PW~&b8cGNT3!DB zp|FNLqka(DU&Vj34HjNzMsX+3FfbIOhD4*5Wq`}d$R;F;4Q0gQD98R;fA&!1U8TWH zi=^1FtcevflfZ;i>;1ljm0LtB1el6@7>%! z{^Fzy#Q{7QOuWVbU!JO931hzH!u&$koK4?24m$XN)XZOV2L18Dk?B1(r<-YN43h$oKKEwJ?o~MUJI^FgV*(tuk cdmkA9AmDpmAOI)<3<2VRKtSC8uK Date: Fri, 2 May 2025 14:49:13 +0200 Subject: [PATCH 187/392] Providing meta info (boundary per mask) from MALY reader --- .../streamers/gds2/db_plugin/dbGDS2ReaderBase.cc | 2 -- src/plugins/streamers/maly/db_plugin/dbMALY.cc | 2 +- .../streamers/maly/db_plugin/dbMALYReader.cc | 15 +++++++++++++++ .../streamers/maly/db_plugin/dbMALYReader.h | 1 + 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc index 413db5084..dad91a563 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc @@ -33,8 +33,6 @@ namespace db { -// --------------------------------------------------------------- - // --------------------------------------------------------------- // GDS2ReaderBase diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.cc b/src/plugins/streamers/maly/db_plugin/dbMALY.cc index 954514f79..385e217fa 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALY.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.cc @@ -133,7 +133,7 @@ public: virtual std::string format_name () const { return "MALY"; } virtual std::string format_desc () const { return "MALY jobdeck"; } virtual std::string format_title () const { return "MALY (MALY jobdeck format)"; } - virtual std::string file_format () const { return "MALY jobdeck files (*.maly *.MALY)"; } + virtual std::string file_format () const { return "MALY jobdeck files (*.maly *.MALY *.mly *.MLY)"; } virtual bool detect (tl::InputStream &s) const { diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index 5f4d88fb2..d57efcd59 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -99,11 +99,26 @@ MALYReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) MALYData data = read_maly_file (); import_data (layout, data); + create_metadata (layout, data); finish_layers (layout); return layer_map_out (); } +void +MALYReader::create_metadata (db::Layout &layout, const MALYData &data) +{ + tl::Variant boundary_per_mask = tl::Variant::empty_array (); + + for (auto m = data.masks.begin (); m != data.masks.end (); ++m) { + double ms = m->size_um; + db::DBox box (-0.5 * ms, -0.5 * ms, 0.5 * ms, 0.5 * ms); + boundary_per_mask.insert (m->name, box); + } + + layout.add_meta_info ("boundary_per_mask", MetaInfo (tl::to_string (tr ("Physical mask boundary per mask name")), boundary_per_mask)); +} + void MALYReader::import_data (db::Layout &layout, const MALYData &data) { diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h index bf6ed2989..ea4feabbb 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.h @@ -230,6 +230,7 @@ private: std::list m_sections; void import_data (db::Layout &layout, const MALYData &data); + void create_metadata (db::Layout &layout, const MALYData &data); tl::Extractor read_record (); void unget_record (); std::string read_record_internal (); From b77b4d7d3c71a24308fdea91dbb60b1919dd6492 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 2 May 2025 15:02:18 +0200 Subject: [PATCH 188/392] Bug fixes --- src/plugins/streamers/maly/db_plugin/dbMALYReader.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc index d57efcd59..a474874a7 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALYReader.cc @@ -88,8 +88,6 @@ MALYReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) { init (options); - prepare_layers (layout); - const db::MALYReaderOptions &specific_options = options.get_options (); m_dbu = specific_options.dbu; @@ -97,6 +95,8 @@ MALYReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) set_create_layers (specific_options.create_other_layers); set_keep_layer_names (true); + prepare_layers (layout); + MALYData data = read_maly_file (); import_data (layout, data); create_metadata (layout, data); From 1932532416a6fedfd0e2567e0c76974e2092c620 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 2 May 2025 15:07:28 +0200 Subject: [PATCH 189/392] Bug fixes, tests --- src/plugins/streamers/maly/db_plugin/dbMALY.cc | 2 +- .../maly/unit_tests/dbMALYReaderTests.cc | 1 + testdata/maly/maly_test10_lm_au.oas | Bin 0 -> 1705 bytes 3 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 testdata/maly/maly_test10_lm_au.oas diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.cc b/src/plugins/streamers/maly/db_plugin/dbMALY.cc index 385e217fa..372b91f2d 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALY.cc +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.cc @@ -163,7 +163,7 @@ public: virtual tl::XMLElementBase *xml_reader_options_element () const { - return new db::ReaderOptionsXMLElement ("mag", + return new db::ReaderOptionsXMLElement ("maly", tl::make_member (&db::MALYReaderOptions::dbu, "dbu") + tl::make_member (&db::MALYReaderOptions::layer_map, "layer-map") + tl::make_member (&db::MALYReaderOptions::create_other_layers, "create-other-layers") diff --git a/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc b/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc index 438613561..95a6ff447 100644 --- a/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc +++ b/src/plugins/streamers/maly/unit_tests/dbMALYReaderTests.cc @@ -134,6 +134,7 @@ TEST(2_Errors) TEST(10_BasicLayout) { run_test (_this, tl::testdata (), "MALY_test10.maly", "maly_test10_au.oas"); + run_test (_this, tl::testdata (), "MALY_test10.maly", "maly_test10_lm_au.oas", "A: 10, B: 11, C: 12, D: 13"); } TEST(11_Titles) diff --git a/testdata/maly/maly_test10_lm_au.oas b/testdata/maly/maly_test10_lm_au.oas new file mode 100644 index 0000000000000000000000000000000000000000..5d495f5b1ab34757a7892a6949e138bc9fae5b11 GIT binary patch literal 1705 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKC?n3q!6L)YEF;ds&!EJR>XUoMnybM;fb~F; z;|1o9>4KX(8~>b$4qRRSKbqrK6n{_8gJqftG@5`%@xvym(W8jq$Rpw=2WI8a52Z(s3cqMp6MU{CFBEr0aynLd{ z+z1gKUM^mCQDrWK2rDl$ue9I~nS--mBTkX~ybKF^*%ls>10rsQ4Kv&% zcqIjYND)>hYQf0y;CJzYHU5oTB^G`+1L|LJNDjmXGJ4s72Jnh2F?hKM|4}R~5aSmx zG}dm|{=g%%W6aM(<^JjA-Oyr8(XmirPtmaf5kK4--a zRIw=sh;%UUiU|G?#c&c2hRcNne+Uy+Muf`+pf0aKcX>OhF6R~e!H3}_ZVZ=mD>1k` z3I91*Tp-4Q2&pG_=7*c&wzalDY3P`pc$mkWGmzV@k9~i)_?nOTceFNs*ew~5bWqV! zK>EasROVM~47{8WhaW6Oclc~l9nL2BgB`=+To?{#5&XeQSQ!yP&IEON4Z6#_Np-oH z5`(K1FxgoaD*iwAEmNn5=L~F zoL=RJhdj%gpNBH=@&ol-3jb0&bl}K223GdvxhYl*N1E(g#OBO9xZ}YD8D-|r&B+&e z=Y~4nXStDnmZfp}w}P*|<@v^DIa3)JcsYc=^D;0oY+-De{f?<&(icX-zub+TRSX;q z%q?$ra5qg@$S?GqlYQ1q5O`@1QV3MU%c#U)Zy@~Z*fFrJy_@eFn)>`Yr17J9>B7xj zjT0Mx9^9Cr;Qc~U80z4K5-S84co_r_hzTCx7d*frcmQa^AEt|pt`m0iF$Rjb#vV6S zsp<{Vz1LqPCzWg@#HD4glGW*=Z|C6Rt++^#v$I2EiqgLWciXKmueE)BCzNNY&1PGP zZ8fJeZ{?}Udge?L`F>`dwaLa~o|DqnJuk12Q2EucU7>k@8uyIb7J>iU*B=luOwW;c z|LFeK4d*x~O?x*_Wu4c7*;V Date: Sat, 3 May 2025 22:36:39 +0200 Subject: [PATCH 190/392] WIP: network R extractor, needs testing --- src/pex/pex/pex.pro | 6 + src/pex/pex/pexRExtractor.cc | 282 ------------- src/pex/pex/pexRExtractor.h | 340 ---------------- src/pex/pex/pexRExtractorTech.cc | 29 ++ src/pex/pex/pexRExtractorTech.h | 167 ++++++++ src/pex/pex/pexRNetExtractor.cc | 465 ++++++++++++++++++++++ src/pex/pex/pexRNetExtractor.h | 97 +++++ src/pex/pex/pexRNetwork.cc | 309 ++++++++++++++ src/pex/pex/pexRNetwork.h | 377 ++++++++++++++++++ src/pex/pex/pexSquareCountingRExtractor.h | 1 + src/pex/pex/pexTriangulationRExtractor.h | 1 + src/pex/unit_tests/pexRExtractorTests.cc | 1 + 12 files changed, 1453 insertions(+), 622 deletions(-) create mode 100644 src/pex/pex/pexRExtractorTech.cc create mode 100644 src/pex/pex/pexRExtractorTech.h create mode 100644 src/pex/pex/pexRNetExtractor.cc create mode 100644 src/pex/pex/pexRNetExtractor.h create mode 100644 src/pex/pex/pexRNetwork.cc create mode 100644 src/pex/pex/pexRNetwork.h diff --git a/src/pex/pex/pex.pro b/src/pex/pex/pex.pro index 145a1866e..6d69e018c 100644 --- a/src/pex/pex/pex.pro +++ b/src/pex/pex/pex.pro @@ -10,12 +10,18 @@ SOURCES = \ pexForceLink.cc \ pexRExtractor.cc \ gsiDeclRExtractor.cc \ + pexRExtractorTech.cc \ + pexRNetExtractor.cc \ + pexRNetwork.cc \ pexSquareCountingRExtractor.cc \ pexTriangulationRExtractor.cc HEADERS = \ pexForceLink.h \ pexRExtractor.h \ + pexRExtractorTech.h \ + pexRNetExtractor.h \ + pexRNetwork.h \ pexSquareCountingRExtractor.h \ pexTriangulationRExtractor.h diff --git a/src/pex/pex/pexRExtractor.cc b/src/pex/pex/pexRExtractor.cc index 11c36c771..10afe62cc 100644 --- a/src/pex/pex/pexRExtractor.cc +++ b/src/pex/pex/pexRExtractor.cc @@ -22,292 +22,10 @@ #include "pexRExtractor.h" -#include "tlEquivalenceClusters.h" namespace pex { -// ----------------------------------------------------------------------------- - -std::string -RNode::to_string () const -{ - std::string res; - switch (type) { - default: - res += "$" + tl::to_string (port_index); - break; - case VertexPort: - res += "V" + tl::to_string (port_index); - break; - case PolygonPort: - res += "P" + tl::to_string (port_index); - break; - } - return res; -} - -// ----------------------------------------------------------------------------- - -std::string -RElement::to_string () const -{ - std::string na; - if (a ()) { - na = a ()->to_string (); - } else { - na = "(nil)"; - } - - std::string nb; - if (b ()) { - nb = b ()->to_string (); - } else { - nb = "(nil)"; - } - - if (nb < na) { - std::swap (na, nb); - } - - std::string res = "R " + na + " " + nb + " "; - res += tl::sprintf ("%.6g", resistance ()); - return res; -} - -// ----------------------------------------------------------------------------- - -RNetwork::RNetwork () -{ - // .. nothing yet .. -} - -RNetwork::~RNetwork () -{ - clear (); -} - -std::string -RNetwork::to_string () const -{ - std::string res; - for (auto e = m_elements.begin (); e != m_elements.end (); ++e) { - if (! res.empty ()) { - res += "\n"; - } - res += e->to_string (); - } - return res; -} - -void -RNetwork::clear () -{ - m_elements.clear (); // must happen before m_nodes - m_nodes.clear (); - m_elements_by_nodes.clear (); - m_nodes_by_type.clear (); -} - -RNode * -RNetwork::create_node (RNode::node_type type, unsigned int port_index) -{ - if (type != RNode::Internal) { - - auto i = m_nodes_by_type.find (std::make_pair (type, port_index)); - if (i != m_nodes_by_type.end ()) { - - return i->second; - - } else { - - RNode *new_node = new RNode (this, type, db::DBox (), port_index); - m_nodes.push_back (new_node); - m_nodes_by_type.insert (std::make_pair (std::make_pair (type, port_index), new_node)); - - return new_node; - } - - } else { - - RNode *new_node = new RNode (this, type, db::DBox (), port_index); - m_nodes.push_back (new_node); - return new_node; - - } -} - -RElement * -RNetwork::create_element (double conductance, RNode *a, RNode *b) -{ - std::pair key (a, b); - if (size_t (b) < size_t (a)) { - std::swap (key.first, key.second); - } - - auto i = m_elements_by_nodes.find (key); - if (i != m_elements_by_nodes.end ()) { - - if (conductance == pex::RElement::short_value () || i->second->conductance == pex::RElement::short_value ()) { - i->second->conductance = pex::RElement::short_value (); - } else { - i->second->conductance += conductance; - } - - return i->second; - - } else { - - RElement *element = new RElement (this, conductance, a, b); - m_elements.push_back (element); - m_elements_by_nodes.insert (std::make_pair (key, element)); - - a->m_elements.push_back (element); - element->m_ia = --a->m_elements.end (); - b->m_elements.push_back (element); - element->m_ib = --b->m_elements.end (); - - return element; - - } -} - -void -RNetwork::remove_node (RNode *node) -{ - tl_assert (node->type == RNode::Internal); - while (! node->m_elements.empty ()) { - delete const_cast (node->m_elements.front ()); - } - delete node; -} - -void -RNetwork::remove_element (RElement *element) -{ - RNode *a = const_cast (element->a ()); - RNode *b = const_cast (element->b ()); - - delete element; - - if (a && a->type == RNode::Internal && a->m_elements.empty ()) { - delete a; - } - if (b && b->type == RNode::Internal && b->m_elements.empty ()) { - delete b; - } -} - -void -RNetwork::join_nodes (RNode *a, RNode *b) -{ - for (auto e = b->elements ().begin (); e != b->elements ().end (); ++e) { - RNode *on = const_cast ((*e)->other (b)); - if (on != a) { - create_element ((*e)->conductance, on, a); - } - } - - a->location += b->location; - remove_node (b); -} - -void -RNetwork::simplify () -{ - bool any_change = true; - - while (any_change) { - - any_change = false; - - // join shorted clusters - we take care to remove internal nodes only - - tl::equivalence_clusters clusters; - for (auto e = m_elements.begin (); e != m_elements.end (); ++e) { - if (e->conductance == pex::RElement::short_value () && (e->a ()->type == pex::RNode::Internal || e->b ()->type == pex::RNode::Internal)) { - clusters.same (e->a (), e->b ()); - } - } - - for (size_t ic = 1; ic <= clusters.size (); ++ic) { - - RNode *remaining = 0; - RNode *first_node = 0; - for (auto c = clusters.begin_cluster (ic); c != clusters.end_cluster (ic); ++c) { - RNode *n = const_cast ((*c)->first); - if (! first_node) { - first_node = n; - } - if (n->type != pex::RNode::Internal) { - remaining = n; - break; - } - } - - if (! remaining) { - // Only internal nodes - remaining = first_node; - } - - for (auto c = clusters.begin_cluster (ic); c != clusters.end_cluster (ic); ++c) { - RNode *n = const_cast ((*c)->first); - if (n != remaining && n->type == pex::RNode::Internal) { - any_change = true; - join_nodes (remaining, n); - } - } - - } - - // combine serial resistors if connected through an internal node - - std::vector nodes_to_remove; - - for (auto n = m_nodes.begin (); n != m_nodes.end (); ++n) { - - size_t nres = n->elements ().size (); - - if (n->type == pex::RNode::Internal && nres <= 2) { - - any_change = true; - - if (nres == 2) { - - auto e = n->elements ().begin (); - - RNode *n1 = const_cast ((*e)->other (n.operator-> ())); - double r1 = (*e)->resistance (); - - ++e; - RNode *n2 = const_cast ((*e)->other (n.operator-> ())); - double r2 = (*e)->resistance (); - - double r = r1 + r2; - if (r == 0.0) { - create_element (pex::RElement::short_value (), n1, n2); - } else { - create_element (1.0 / r, n1, n2); - } - - } - - nodes_to_remove.push_back (n.operator-> ()); - - } - - } - - for (auto n = nodes_to_remove.begin (); n != nodes_to_remove.end (); ++n) { - remove_node (*n); - } - - } - -} - -// ----------------------------------------------------------------------------- - RExtractor::RExtractor () { // .. nothing yet .. diff --git a/src/pex/pex/pexRExtractor.h b/src/pex/pex/pexRExtractor.h index b9da173bc..e80c3f248 100644 --- a/src/pex/pex/pexRExtractor.h +++ b/src/pex/pex/pexRExtractor.h @@ -26,352 +26,12 @@ #include "pexCommon.h" #include "dbPolygon.h" -#include "dbPLC.h" -#include "tlList.h" - -#include -#include -#include namespace pex { -class RElement; -class RNode; class RNetwork; -/** - * @brief Represents a node in the R graph - * - * A node connects to multiple elements (resistors). - * Every element has two nodes. The nodes and elements form - * a graph. - * - * RNode object cannot be created directly. Use "create_node" - * from RNetwork. - */ -struct PEX_PUBLIC RNode - : public tl::list_node -{ -public: - /** - * @brief The type of the node - */ - enum node_type { - Internal, // an internal node, not related to a port - VertexPort, // a node related to a vertex port - PolygonPort // a node related to a polygon port - }; - - /** - * @brief The node type - */ - node_type type; - - /** - * @brief The location + extension of the node - */ - db::DBox location; - - /** - * @brief An index locating the node in the vertex or polygon port lists - * - * For internal nodes, the index is a unique numbers. - */ - unsigned int port_index; - - /** - * @brief Gets the R elements connected to this node - */ - const std::list &elements () const - { - return m_elements; - } - - /** - * @brief Returns a string representation of the node - */ - std::string to_string () const; - - /** - * @brief Gets the network the node lives in - */ - RNetwork *graph () const - { - return mp_network; - } - -protected: - friend class RNetwork; - friend class RElement; - friend class tl::list_impl; - - RNode (RNetwork *network, node_type _type, const db::DBox &_location, unsigned int _port_index) - : type (_type), location (_location), port_index (_port_index), mp_network (network) - { } - - ~RNode () { } - -private: - RNode (const RNode &other); - RNode &operator= (const RNode &other); - - RNetwork *mp_network; - mutable std::list m_elements; -}; - -/** - * @brief Represents an R element in the graph (an edge) - * - * An element has two nodes that form the ends of the edge and - * a conductance value (given in Siemens). - * - * The value can be RElement::short_value() indicating - * "infinite" conductance (a short). - * - * RElement objects cannot be created directly. Use "create_element" - * from RNetwork. - */ -struct PEX_PUBLIC RElement - : public tl::list_node -{ - /** - * @brief The conductance value - */ - double conductance; - - /** - * @brief The nodes the resistor connects - */ - const RNode *a () const { return mp_a; } - const RNode *b () const { return mp_b; } - - /** - * @brief Gets the other node for n - */ - const RNode *other (const RNode *n) const - { - if (mp_a == n) { - return mp_b; - } else if (mp_b == n) { - return mp_a; - } - tl_assert (false); - } - - /** - * @brief Represents the conductance value for a short - */ - static double short_value () - { - return std::numeric_limits::infinity (); - } - - /** - * @brief Gets the resistance value - * - * The resistance value is the inverse of the conducance. - */ - double resistance () const - { - return conductance == short_value () ? 0.0 : 1.0 / conductance; - } - - /** - * @brief Returns a string representation of the element - */ - std::string to_string () const; - - /** - * @brief Gets the network the node lives in - */ - RNetwork *graph () const - { - return mp_network; - } - -protected: - friend class RNetwork; - friend class tl::list_impl; - - RElement (RNetwork *network, double _conductivity, const RNode *a, const RNode *b) - : conductance (_conductivity), mp_network (network), mp_a (a), mp_b (b) - { } - - ~RElement () - { - if (mp_a) { - mp_a->m_elements.erase (m_ia); - } - if (mp_b) { - mp_b->m_elements.erase (m_ib); - } - mp_a = mp_b = 0; - } - - std::list::iterator m_ia, m_ib; - RNetwork *mp_network; - const RNode *mp_a, *mp_b; - -private: - RElement (const RElement &other); - RElement &operator= (const RElement &other); -}; - -/** - * @brief Represents a R network (a graph of RNode and RElement) - */ -class PEX_PUBLIC RNetwork - : public tl::Object -{ -public: - typedef tl::list node_list; - typedef node_list::const_iterator node_iterator; - typedef tl::list element_list; - typedef element_list::const_iterator element_iterator; - - /** - * @brief Constructor - */ - RNetwork (); - - /** - * @brief Destructor - */ - ~RNetwork (); - - /** - * @brief Creates a node with the given type and port index - * - * If the node type is Internal, a new node is created always. - * If the node type is VertexPort or PolygonPort, an existing - * node is returned if one way created with the same type - * or port index already. This avoids creating duplicates - * for the same port. - */ - RNode *create_node (RNode::node_type type, unsigned int port_index); - - /** - * @brief Creates a new element between the given nodes - * - * If an element already exists between the specified nodes, the - * given value is added to the existing element and the existing - * object is returned. - */ - RElement *create_element (double conductance, RNode *a, RNode *b); - - /** - * @brief Removes the given element - * - * Removing the element will also remove any orphan nodes - * at the ends if they are of type Internal. - */ - void remove_element (RElement *element); - - /** - * @brief Removes the node and the attached elements. - * - * Only nodes of type Internal can be removed. - */ - void remove_node (RNode *node); - - /** - * @brief Clears the network - */ - void clear (); - - /** - * @brief Simplifies the network - * - * This will: - * - Join serial resistors if connected by an internal node - * - Remove shorts and join the nodes, if one of them is - * an internal node. The non-internal node will persist. - * - Remove "dangling" resistors if the dangling node is - * an internal one - */ - void simplify (); - - /** - * @brief Iterate the nodes (begin) - */ - node_iterator begin_nodes () const - { - return m_nodes.begin (); - } - - /** - * @brief Iterate the nodes (end) - */ - node_iterator end_nodes () const - { - return m_nodes.end (); - } - - /** - * @brief Gets the number of nodes - */ - size_t num_nodes () const - { - return m_nodes.size (); - } - - /** - * @brief Gets the number of internal nodes - */ - size_t num_internal_nodes () const - { - size_t count = 0; - for (auto n = m_nodes.begin (); n != m_nodes.end (); ++n) { - if (n->type == pex::RNode::Internal) { - ++count; - } - } - return count; - } - - /** - * @brief Iterate the elements (begin) - */ - element_iterator begin_elements () const - { - return m_elements.begin (); - } - - /** - * @brief Iterate the elements (end) - */ - element_iterator end_elements () const - { - return m_elements.end (); - } - - /** - * @brief Gets the number of elements - */ - size_t num_elements () const - { - return m_elements.size (); - } - - /** - * @brief Returns a string representation of the graph - */ - std::string to_string () const; - -private: - node_list m_nodes; - element_list m_elements; - std::map, RElement *> m_elements_by_nodes; - std::map, RNode *> m_nodes_by_type; - - RNetwork (const RNetwork &); - RNetwork &operator= (const RNetwork &); - - void join_nodes (RNode *a, RNode *b); -}; - - /** * @brief A base class for an resistance extractor * diff --git a/src/pex/pex/pexRExtractorTech.cc b/src/pex/pex/pexRExtractorTech.cc new file mode 100644 index 000000000..65e1a6604 --- /dev/null +++ b/src/pex/pex/pexRExtractorTech.cc @@ -0,0 +1,29 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "pexRExtractorTech.h" + +namespace pex +{ + // .. nothing yet .. +} diff --git a/src/pex/pex/pexRExtractorTech.h b/src/pex/pex/pexRExtractorTech.h new file mode 100644 index 000000000..b2840d020 --- /dev/null +++ b/src/pex/pex/pexRExtractorTech.h @@ -0,0 +1,167 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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_pexRExtractorTech +#define HDR_pexRExtractorTech + +#include "pexCommon.h" + +#include + +namespace pex +{ + +/** + * @brief Specifies the extraction parameters for vias + * + * Note that the layers are generic IDs. These are usigned ints specifying + * a layer. + */ +class RExtractorTechVia +{ +public: + RExtractorTechVia () + : cut_layer (0), top_conductor (0), bottom_conductor (0), resistance (0.0), merge_distance (0.0) + { + // .. nothing yet .. + } + + /** + * @brief Specifies the cut layer + * This is the layer the via sits on + */ + unsigned int cut_layer; + + /** + * @brief Specifies the top conductor + * The value is the ID of the top conductor layer + */ + unsigned int top_conductor; + + /** + * @brief Specifies the bottom conductor + * The value is the ID of the bottom conductor layer + */ + unsigned int bottom_conductor; + + /** + * @brief Specifies the resistance in Ohm * sqaure micrometer + */ + double resistance; + + /** + * @brief Specifies the merge distance in micrometers + * The merge distance indicates a range under which vias are merged + * into bigger effective areas to reduce the complexity of via arrays. + */ + double merge_distance; +}; + +/** + * @brief Specifies the extraction parameters for a conductor layer + * + * Note that the layers are generic IDs. These are usigned ints specifying + * a layer. + */ +class RExtractorTechConductor +{ +public: + /** + * @brief A algorithm to use + */ + enum Algorithm + { + /** + * @brief The square counting algorithm + * This algorithm is suitable for "long and thin" wires. + */ + SquareCounting = 0, + + /** + * @brief The triangulation algorithm + * This algorithm is suitable to "large" sheets, specifically substrate. + */ + Triangulation = 1 + }; + + /** + * @brief The constructor + */ + RExtractorTechConductor () + : layer (0), resistance (0.0), algorithm (SquareCounting), triangulation_min_b (-1.0), triangulation_max_area (-1.0) + { + // .. nothing yet .. + } + + /** + * @brief Specifies the layer + * The value is the generic ID of the layer. + */ + unsigned int layer; + + /** + * @brief Specifies the sheet resistance + * The sheet resistance is given in units of Ohm / square + */ + double resistance; + + /** + * @brief The algorihm to use + */ + Algorithm algorithm; + + /** + * @brief The "min_b" parameter for the triangulation + * The "b" parameter is a ratio of shortest triangle edge to circle radius. + * If a negative value is given, the default value is taken. + */ + double triangulation_min_b; + + /** + * @brief The "max_area" parameter for the triangulation + * The "max_area" specifies the maximum area of the triangles produced in square micrometers. + * If a negative value is given, the default value is taken. + */ + double triangulation_max_area; +}; + +/** + * @brief Specifies the extraction parameters + */ +class RExtractorTech +{ +public: + /** + * @brief A list of via definitions + */ + std::list vias; + + /** + * @brief A list of conductor definitions + */ + std::list conductors; +}; + +} + +#endif + diff --git a/src/pex/pex/pexRNetExtractor.cc b/src/pex/pex/pexRNetExtractor.cc new file mode 100644 index 000000000..78f4f6a4b --- /dev/null +++ b/src/pex/pex/pexRNetExtractor.cc @@ -0,0 +1,465 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "pexCommon.h" + +#include "pexRNetExtractor.h" +#include "pexRNetwork.h" +#include "pexRExtractorTech.h" +#include "pexSquareCountingRExtractor.h" +#include "pexTriangulationRExtractor.h" + +#include "dbBoxScanner.h" +#include "dbPolygonTools.h" +#include "dbRegionProcessors.h" +#include "dbCompoundOperation.h" +#include "dbPolygonNeighborhood.h" + +namespace pex +{ + +RNetExtractor::RNetExtractor (double dbu) + : m_dbu (dbu) +{ + // .. nothing yet .. +} + +void +RNetExtractor::extract (const RExtractorTech &tech, + const std::map &geo, + const std::map > &vertex_ports, + const std::map > &polygon_ports, + RNetwork &rnetwork) +{ + rnetwork.clear (); + + std::map > via_ports; + create_via_ports (tech, geo, via_ports, rnetwork); + + for (auto g = geo.begin (); g != geo.end (); ++g) { + + // Find the conductor spec for the given layer + const RExtractorTechConductor *cond = 0; + for (auto c = tech.conductors.begin (); c != tech.conductors.end () && !cond; ++c) { + if (c->layer == g->first) { + cond = c.operator-> (); + } + } + if (! cond) { + continue; + } + + // Compute the offsets for the port indexes + // The port indexes are assigned incrementally, so the first index of a port + // for layer n is: + // size(ports for layer 0) + ... + size(ports for layer n-1) + // (size is 0 for empty port list) + unsigned int vp_offset = 0, pp_offset = 0; + for (auto p = vertex_ports.begin (); p != vertex_ports.end () && p->first < g->first; ++p) { + vp_offset += p->second.size (); + } + for (auto p = polygon_ports.begin (); p != polygon_ports.end () && p->first < g->first; ++p) { + vp_offset += p->second.size (); + } + + // fetch the port list for vertex ports + auto ivp = vertex_ports.find (g->first); + static std::vector empty_vertex_ports; + const std::vector &vp = ivp == vertex_ports.end () ? empty_vertex_ports : ivp->second; + + // fetch the port list for polygon ports + auto ipp = polygon_ports.find (g->first); + static std::vector empty_polygon_ports; + const std::vector &pp = ipp == polygon_ports.end () ? empty_polygon_ports : ipp->second; + + // fetch the port list for via ports + auto iviap = via_ports.find (g->first); + static std::vector empty_via_ports; + const std::vector &viap = iviap == via_ports.end () ? empty_via_ports : iviap->second; + + // extract the conductor polygon and integrate the results into the target network + extract_conductor (*cond, g->second, vp, vp_offset, pp, pp_offset, viap, rnetwork); + + } +} + +static double +via_conductance (const RExtractorTechVia &via_tech, + const db::Polygon &poly, + double dbu) +{ + if (via_tech.resistance < 1e-10) { + return RElement::short_value (); + } else { + return (1.0 / via_tech.resistance) * dbu * dbu * poly.area (); + } +} + +namespace +{ + +class ViaAggregationVisitor + : public db::PolygonNeighborhoodVisitor +{ +public: + ViaAggregationVisitor (const RExtractorTechVia *via_tech, std::vector > *conductances, double dbu) + : mp_via_tech (via_tech), mp_conductances (conductances), m_dbu (dbu) + { + // .. nothing yet .. + } + + virtual void neighbors (const db::Layout * /*layout*/, const db::Cell * /*cell*/, const db::PolygonWithProperties &polygon, const neighbors_type &neighbors) + { + auto i = neighbors.find ((unsigned int) 1); + if (i == neighbors.end ()) { + return; + } + + double c = 0; + for (auto vp = i->second.begin (); vp != i->second.end (); ++vp) { + double cc = via_conductance (*mp_via_tech, *vp, m_dbu); + if (cc == RElement::short_value ()) { + c = cc; + break; + } else { + c += cc; + } + } + + mp_conductances->push_back (std::make_pair (c, polygon.box ().center ())); + } + +private: + const RExtractorTechVia *mp_via_tech; + std::vector > *mp_conductances; + double m_dbu; +}; + +} + +void +RNetExtractor::create_via_ports (const RExtractorTech &tech, + const std::map &geo, + std::map > &vias, + RNetwork &rnetwork) +{ + std::vector > via_conductances; + + for (auto v = tech.vias.begin (); v != tech.vias.end (); ++v) { + + auto g = geo.find (v->cut_layer); + if (g == geo.end ()) { + continue; + } + + via_conductances.clear (); + + if (v->merge_distance > db::epsilon) { + + // with merge, follow this scheme: + // 1.) do a merge by over/undersize + // 2.) do a convex decomposition, so we get convex via shapes with the bbox center inside the polygon + // 3.) re-aggregate the original via polygons and collect the total conductance per merged shape + + db::Coord sz = db::coord_traits::rounded (0.5 * v->merge_distance / m_dbu); + + db::Region merged_vias = g->second.sized (sz).sized (-sz); + merged_vias.process (db::ConvexDecomposition (db::PO_any)); + + std::vector children; + children.push_back (new db::CompoundRegionOperationPrimaryNode ()); + children.push_back (new db::CompoundRegionOperationSecondaryNode (const_cast (&g->second))); + + ViaAggregationVisitor visitor (v.operator-> (), &via_conductances, m_dbu); + db::PolygonNeighborhoodCompoundOperationNode en_node (children, &visitor, 0); + merged_vias.cop_to_region (en_node); + + } else { + + for (auto p = g->second.begin_merged (); ! p.at_end (); ++p) { + via_conductances.push_back (std::make_pair (via_conductance (*v, *p, m_dbu), p->box ().center ())); + } + + } + + // create the via resistor elements + unsigned int port_index = 0; + for (auto vc = via_conductances.begin (); vc != via_conductances.end (); ++vc) { + + RNode *a = rnetwork.create_node (RNode::Internal, port_index++); + RNode *b = rnetwork.create_node (RNode::Internal, port_index++); + + rnetwork.create_element (vc->first, a, b); + + vias[v->bottom_conductor].push_back (ViaPort (vc->second, a)); + vias[v->top_conductor].push_back (ViaPort (vc->second, b)); + + } + + } +} + +static inline size_t make_id (size_t index, unsigned int type) +{ + return (index << 2) + type; +} + +static inline size_t index_from_id (size_t id) +{ + return id >> 2; +} + +static inline unsigned int type_from_id (size_t id) +{ + return id & 3; +} + +namespace +{ + +class ExtractingReceiver + : public db::box_scanner_receiver2 +{ +public: + ExtractingReceiver (const RExtractorTechConductor *cond, + const std::vector *vertex_ports, + unsigned int vertex_port_index_offset, + const std::vector *polygon_ports, + unsigned int polygon_port_index_offset, + const std::vector *via_ports, + double dbu, + RNetwork *rnetwork) + : mp_cond (cond), + mp_vertex_ports (vertex_ports), + mp_polygon_ports (polygon_ports), + mp_via_ports (via_ports), + m_next_internal_port_index (0), + m_vertex_port_index_offset (vertex_port_index_offset), + m_polygon_port_index_offset (polygon_port_index_offset), + m_dbu (dbu), + mp_rnetwork (rnetwork) + { + for (auto n = rnetwork->begin_nodes (); n != rnetwork->end_nodes (); ++n) { + if (n->type == RNode::Internal && n->port_index > m_next_internal_port_index) { + m_next_internal_port_index = n->port_index; + } + } + } + + void finish1 (const db::Polygon *poly, const size_t poly_id) + { + auto i = m_interacting_ports.find (poly_id); + if (i == m_interacting_ports.end ()) { + static std::set empty_ids; + extract (*poly, empty_ids); + } else { + extract (*poly, i->second); + m_interacting_ports.erase (i); + } + } + + void add (const db::Polygon *poly, const size_t poly_id, const db::Box *port, const size_t port_id) + { + if (db::interact (*poly, *port)) { + m_interacting_ports[poly_id].insert (port_id); + } + } + +private: + std::map > m_interacting_ports; + const RExtractorTechConductor *mp_cond; + const std::vector *mp_vertex_ports; + const std::vector *mp_polygon_ports; + const std::vector *mp_via_ports; + std::map m_id_to_node; + unsigned int m_next_internal_port_index; + unsigned int m_vertex_port_index_offset; + unsigned int m_polygon_port_index_offset; + double m_dbu; + RNetwork *mp_rnetwork; + + void extract (const db::Polygon &poly, const std::set &port_ids) + { + std::vector local_vertex_ports; + std::vector local_vertex_port_ids; + std::vector local_polygon_ports; + std::vector local_polygon_port_ids; + + for (auto i = port_ids.begin (); i != port_ids.end (); ++i) { + switch (type_from_id (*i)) { + case 0: // vertex port + local_vertex_port_ids.push_back (*i); + local_vertex_ports.push_back ((*mp_vertex_ports) [index_from_id (*i)]); + break; + case 1: // via port + local_vertex_port_ids.push_back (*i); + local_vertex_ports.push_back ((*mp_via_ports) [index_from_id (*i)].position); + break; + case 2: // polygon port + local_polygon_port_ids.push_back (*i); + local_polygon_ports.push_back ((*mp_polygon_ports) [index_from_id (*i)]); + break; + } + } + + pex::RNetwork local_network; + + switch (mp_cond->algorithm) { + case RExtractorTechConductor::SquareCounting: + default: + { + pex::SquareCountingRExtractor rex (m_dbu); + rex.extract (poly, local_vertex_ports, local_polygon_ports, local_network); + } + break; + case RExtractorTechConductor::Triangulation: + { + pex::TriangulationRExtractor rex (m_dbu); + rex.extract (poly, local_vertex_ports, local_polygon_ports, local_network); + } + break; + } + + integrate (local_network, local_vertex_port_ids, local_polygon_port_ids); + } + + void integrate (const RNetwork &local_network, + const std::vector &local_vertex_port_ids, + const std::vector &local_polygon_port_ids) + { + // create or find the new nodes in the target network + std::unordered_map n2n; + for (auto n = local_network.begin_nodes (); n != local_network.end_nodes (); ++n) { + + const RNode *local = n.operator-> (); + RNode *global = 0; + + if (local->type == RNode::Internal) { + + // for internal nodes always create a node in the target network + global = mp_rnetwork->create_node (local->type, ++m_next_internal_port_index); + + } else if (local->type == RNode::VertexPort) { + + // for vertex nodes reuse the via node or create a new target node, unless one + // was created already. + + size_t id = local_vertex_port_ids [local->port_index]; + + auto i2n = m_id_to_node.find (id); + if (i2n != m_id_to_node.end ()) { + global = i2n->second; + } else { + if (type_from_id (id) == 0) { // vertex port + global = mp_rnetwork->create_node (RNode::VertexPort, index_from_id (id) + m_vertex_port_index_offset); + global->location = local->location; + } else if (type_from_id (id) == 1) { // via port + global = (*mp_via_ports) [index_from_id (id)].node; + } + m_id_to_node.insert (std::make_pair (id, global)); + } + + } else if (local->type == RNode::PolygonPort) { + + // for polygon nodes create a new target node, unless one was created already. + + size_t id = local_polygon_port_ids [local->port_index]; + tl_assert (type_from_id (id) == 2); + + auto i2n = m_id_to_node.find (id); + if (i2n != m_id_to_node.end ()) { + global = i2n->second; + } else { + global = mp_rnetwork->create_node (RNode::VertexPort, index_from_id (id) + m_polygon_port_index_offset); + global->location = local->location; + m_id_to_node.insert (std::make_pair (id, global)); + } + + } + + tl_assert (global != 0); + n2n.insert (std::make_pair (local, global)); + + } + + // create the R elements in the target network + for (auto e = local_network.begin_elements (); e != local_network.begin_elements (); ++e) { + + const RElement *local = e.operator-> (); + + auto ia = n2n.find (local->a ()); + auto ib = n2n.find (local->b ()); + tl_assert (ia != n2n.end ()); + tl_assert (ia != n2n.end ()); + + mp_rnetwork->create_element (local->conductance, ia->second, ib->second); + + } + } +}; + +} + +void +RNetExtractor::extract_conductor (const RExtractorTechConductor &cond, + const db::Region ®ion, + const std::vector &vertex_ports, + unsigned int vertex_ports_index_offset, + const std::vector &polygon_ports, + unsigned int polygon_ports_index_offset, + const std::vector &via_ports, + RNetwork &rnetwork) +{ + db::box_scanner2 scanner; + + size_t poly_id = 0; + for (auto p = region.addressable_merged_polygons (); ! p.at_end (); ++p) { + scanner.insert1 (p.operator-> (), poly_id++); + } + + std::list box_heap; + + // type 0 objects (vertex ports) + for (auto i = vertex_ports.begin (); i != vertex_ports.end (); ++i) { + // @@@ could be without enlarge? + box_heap.push_back (db::Box (*i, *i).enlarged (db::Vector (1, 1))); + scanner.insert2 (&box_heap.back (), make_id (i - vertex_ports.begin (), 0)); + } + + // type 1 objects (via ports) + for (auto i = via_ports.begin (); i != via_ports.end (); ++i) { + // @@@ could be without enlarge? + box_heap.push_back (db::Box (i->position, i->position).enlarged (db::Vector (1, 1))); + scanner.insert2 (&box_heap.back (), make_id (i - via_ports.begin (), 1)); + } + + // type 2 objects (polygon ports) + for (auto i = polygon_ports.begin (); i != polygon_ports.end (); ++i) { + box_heap.push_back (i->box ()); + scanner.insert2 (&box_heap.back (), make_id (i - polygon_ports.begin (), 2)); + } + + ExtractingReceiver rec (&cond, &vertex_ports, vertex_ports_index_offset, &polygon_ports, polygon_ports_index_offset, &via_ports, m_dbu, &rnetwork); + scanner.process (rec, 0, db::box_convert (), db::box_convert ()); +} + +} diff --git a/src/pex/pex/pexRNetExtractor.h b/src/pex/pex/pexRNetExtractor.h new file mode 100644 index 000000000..a2df7d6aa --- /dev/null +++ b/src/pex/pex/pexRNetExtractor.h @@ -0,0 +1,97 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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_pexRNetExtractor +#define HDR_pexRNetExtractor + +#include "pexCommon.h" +#include "dbRegion.h" + +namespace pex +{ + +class RExtractorTech; +class RExtractorTechConductor; +class RNetwork; +class RNode; + +/** + * @brief Implementation of the R extractor for a multi-polygon/multi-layer net + */ +class RNetExtractor +{ +public: + /** + * @brief Constructor + * @param dbu The database unit to be used to convert coordinates into micrometers + */ + RNetExtractor (double dbu); + + /** + * @brief Extracts a R network from a given set of geometries and ports + * @param geo The geometries per layer + * @param vertex_ports The vertex ports - a list of layer and points to attache a port to on this layer + * @param polygon_ports The polygon ports - a list of layer and polygons to attach a port to on this layer + * @param rnetwork The network extracted (output) + * + * The network nodes will carry the information about the port, in case they + * have been generated from a port. + */ + void extract (const RExtractorTech &tech, + const std::map &geo, + const std::map > &vertex_ports, + const std::map > &polygon_ports, + RNetwork &rnetwork); + + /** + * @brief A structure describing a via port + * This structure is used internally + */ + struct ViaPort + { + ViaPort () : node (0) { } + ViaPort (const db::Point &p, RNode *n) : position (p), node (n) { } + db::Point position; + RNode *node; + }; + +private: + double m_dbu; + + void create_via_ports (const RExtractorTech &tech, + const std::map &geo, + std::map > &vias, + RNetwork &rnetwork); + + void extract_conductor (const RExtractorTechConductor &cond, + const db::Region ®ion, + const std::vector &vertex_ports, + unsigned int vertex_ports_index_offset, + const std::vector &polygon_ports, + unsigned int polygon_ports_index_offset, + const std::vector &via_ports, + RNetwork &rnetwork); +}; + +} + +#endif diff --git a/src/pex/pex/pexRNetwork.cc b/src/pex/pex/pexRNetwork.cc new file mode 100644 index 000000000..0a552e2e2 --- /dev/null +++ b/src/pex/pex/pexRNetwork.cc @@ -0,0 +1,309 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "pexRNetwork.h" +#include "tlEquivalenceClusters.h" + +namespace pex +{ + +// ----------------------------------------------------------------------------- + +std::string +RNode::to_string () const +{ + std::string res; + switch (type) { + default: + res += "$" + tl::to_string (port_index); + break; + case VertexPort: + res += "V" + tl::to_string (port_index); + break; + case PolygonPort: + res += "P" + tl::to_string (port_index); + break; + } + return res; +} + +// ----------------------------------------------------------------------------- + +std::string +RElement::to_string () const +{ + std::string na; + if (a ()) { + na = a ()->to_string (); + } else { + na = "(nil)"; + } + + std::string nb; + if (b ()) { + nb = b ()->to_string (); + } else { + nb = "(nil)"; + } + + if (nb < na) { + std::swap (na, nb); + } + + std::string res = "R " + na + " " + nb + " "; + res += tl::sprintf ("%.6g", resistance ()); + return res; +} + +// ----------------------------------------------------------------------------- + +RNetwork::RNetwork () +{ + // .. nothing yet .. +} + +RNetwork::~RNetwork () +{ + clear (); +} + +std::string +RNetwork::to_string () const +{ + std::string res; + for (auto e = m_elements.begin (); e != m_elements.end (); ++e) { + if (! res.empty ()) { + res += "\n"; + } + res += e->to_string (); + } + return res; +} + +void +RNetwork::clear () +{ + m_elements.clear (); // must happen before m_nodes + m_nodes.clear (); + m_elements_by_nodes.clear (); + m_nodes_by_type.clear (); +} + +RNode * +RNetwork::create_node (RNode::node_type type, unsigned int port_index) +{ + if (type != RNode::Internal) { + + auto i = m_nodes_by_type.find (std::make_pair (type, port_index)); + if (i != m_nodes_by_type.end ()) { + + return i->second; + + } else { + + RNode *new_node = new RNode (this, type, db::DBox (), port_index); + m_nodes.push_back (new_node); + m_nodes_by_type.insert (std::make_pair (std::make_pair (type, port_index), new_node)); + + return new_node; + } + + } else { + + RNode *new_node = new RNode (this, type, db::DBox (), port_index); + m_nodes.push_back (new_node); + return new_node; + + } +} + +RElement * +RNetwork::create_element (double conductance, RNode *a, RNode *b) +{ + std::pair key (a, b); + if (size_t (b) < size_t (a)) { + std::swap (key.first, key.second); + } + + auto i = m_elements_by_nodes.find (key); + if (i != m_elements_by_nodes.end ()) { + + if (conductance == pex::RElement::short_value () || i->second->conductance == pex::RElement::short_value ()) { + i->second->conductance = pex::RElement::short_value (); + } else { + i->second->conductance += conductance; + } + + return i->second; + + } else { + + RElement *element = new RElement (this, conductance, a, b); + m_elements.push_back (element); + m_elements_by_nodes.insert (std::make_pair (key, element)); + + a->m_elements.push_back (element); + element->m_ia = --a->m_elements.end (); + b->m_elements.push_back (element); + element->m_ib = --b->m_elements.end (); + + return element; + + } +} + +void +RNetwork::remove_node (RNode *node) +{ + tl_assert (node->type == RNode::Internal); + while (! node->m_elements.empty ()) { + delete const_cast (node->m_elements.front ()); + } + delete node; +} + +void +RNetwork::remove_element (RElement *element) +{ + RNode *a = const_cast (element->a ()); + RNode *b = const_cast (element->b ()); + + delete element; + + if (a && a->type == RNode::Internal && a->m_elements.empty ()) { + delete a; + } + if (b && b->type == RNode::Internal && b->m_elements.empty ()) { + delete b; + } +} + +void +RNetwork::join_nodes (RNode *a, RNode *b) +{ + for (auto e = b->elements ().begin (); e != b->elements ().end (); ++e) { + RNode *on = const_cast ((*e)->other (b)); + if (on != a) { + create_element ((*e)->conductance, on, a); + } + } + + a->location += b->location; + remove_node (b); +} + +void +RNetwork::simplify () +{ + bool any_change = true; + + while (any_change) { + + any_change = false; + + // join shorted clusters - we take care to remove internal nodes only + + tl::equivalence_clusters clusters; + for (auto e = m_elements.begin (); e != m_elements.end (); ++e) { + if (e->conductance == pex::RElement::short_value () && (e->a ()->type == pex::RNode::Internal || e->b ()->type == pex::RNode::Internal)) { + clusters.same (e->a (), e->b ()); + } + } + + for (size_t ic = 1; ic <= clusters.size (); ++ic) { + + RNode *remaining = 0; + RNode *first_node = 0; + for (auto c = clusters.begin_cluster (ic); c != clusters.end_cluster (ic); ++c) { + RNode *n = const_cast ((*c)->first); + if (! first_node) { + first_node = n; + } + if (n->type != pex::RNode::Internal) { + remaining = n; + break; + } + } + + if (! remaining) { + // Only internal nodes + remaining = first_node; + } + + for (auto c = clusters.begin_cluster (ic); c != clusters.end_cluster (ic); ++c) { + RNode *n = const_cast ((*c)->first); + if (n != remaining && n->type == pex::RNode::Internal) { + any_change = true; + join_nodes (remaining, n); + } + } + + } + + // combine serial resistors if connected through an internal node + + std::vector nodes_to_remove; + + for (auto n = m_nodes.begin (); n != m_nodes.end (); ++n) { + + size_t nres = n->elements ().size (); + + if (n->type == pex::RNode::Internal && nres <= 2) { + + any_change = true; + + if (nres == 2) { + + auto e = n->elements ().begin (); + + RNode *n1 = const_cast ((*e)->other (n.operator-> ())); + double r1 = (*e)->resistance (); + + ++e; + RNode *n2 = const_cast ((*e)->other (n.operator-> ())); + double r2 = (*e)->resistance (); + + double r = r1 + r2; + if (r == 0.0) { + create_element (pex::RElement::short_value (), n1, n2); + } else { + create_element (1.0 / r, n1, n2); + } + + } + + nodes_to_remove.push_back (n.operator-> ()); + + } + + } + + for (auto n = nodes_to_remove.begin (); n != nodes_to_remove.end (); ++n) { + remove_node (*n); + } + + } + +} + +} diff --git a/src/pex/pex/pexRNetwork.h b/src/pex/pex/pexRNetwork.h new file mode 100644 index 000000000..d0fe65a9a --- /dev/null +++ b/src/pex/pex/pexRNetwork.h @@ -0,0 +1,377 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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_pexRNetwork +#define HDR_pexRNetwork + +#include "pexCommon.h" + +#include "dbPolygon.h" +#include "dbPLC.h" +#include "tlList.h" + +#include +#include +#include + +namespace pex +{ + +class RElement; +class RNode; +class RNetwork; + +/** + * @brief Represents a node in the R graph + * + * A node connects to multiple elements (resistors). + * Every element has two nodes. The nodes and elements form + * a graph. + * + * RNode object cannot be created directly. Use "create_node" + * from RNetwork. + */ +struct PEX_PUBLIC RNode + : public tl::list_node +{ +public: + /** + * @brief The type of the node + */ + enum node_type { + Internal, // an internal node, not related to a port + VertexPort, // a node related to a vertex port + PolygonPort // a node related to a polygon port + }; + + /** + * @brief The node type + */ + node_type type; + + /** + * @brief The location + extension of the node + */ + db::DBox location; + + /** + * @brief An index locating the node in the vertex or polygon port lists + * + * For internal nodes, the index is a unique numbers. + */ + unsigned int port_index; + + /** + * @brief Gets the R elements connected to this node + */ + const std::list &elements () const + { + return m_elements; + } + + /** + * @brief Returns a string representation of the node + */ + std::string to_string () const; + + /** + * @brief Gets the network the node lives in + */ + RNetwork *graph () const + { + return mp_network; + } + +protected: + friend class RNetwork; + friend class RElement; + friend class tl::list_impl; + + RNode (RNetwork *network, node_type _type, const db::DBox &_location, unsigned int _port_index) + : type (_type), location (_location), port_index (_port_index), mp_network (network) + { } + + ~RNode () { } + +private: + RNode (const RNode &other); + RNode &operator= (const RNode &other); + + RNetwork *mp_network; + mutable std::list m_elements; +}; + +/** + * @brief Represents an R element in the graph (an edge) + * + * An element has two nodes that form the ends of the edge and + * a conductance value (given in Siemens). + * + * The value can be RElement::short_value() indicating + * "infinite" conductance (a short). + * + * RElement objects cannot be created directly. Use "create_element" + * from RNetwork. + */ +struct PEX_PUBLIC RElement + : public tl::list_node +{ + /** + * @brief The conductance value + */ + double conductance; + + /** + * @brief The nodes the resistor connects + */ + const RNode *a () const { return mp_a; } + const RNode *b () const { return mp_b; } + + /** + * @brief Gets the other node for n + */ + const RNode *other (const RNode *n) const + { + if (mp_a == n) { + return mp_b; + } else if (mp_b == n) { + return mp_a; + } + tl_assert (false); + } + + /** + * @brief Represents the conductance value for a short + */ + static double short_value () + { + return std::numeric_limits::infinity (); + } + + /** + * @brief Gets the resistance value + * + * The resistance value is the inverse of the conducance. + */ + double resistance () const + { + return conductance == short_value () ? 0.0 : 1.0 / conductance; + } + + /** + * @brief Returns a string representation of the element + */ + std::string to_string () const; + + /** + * @brief Gets the network the node lives in + */ + RNetwork *graph () const + { + return mp_network; + } + +protected: + friend class RNetwork; + friend class tl::list_impl; + + RElement (RNetwork *network, double _conductivity, const RNode *a, const RNode *b) + : conductance (_conductivity), mp_network (network), mp_a (a), mp_b (b) + { } + + ~RElement () + { + if (mp_a) { + mp_a->m_elements.erase (m_ia); + } + if (mp_b) { + mp_b->m_elements.erase (m_ib); + } + mp_a = mp_b = 0; + } + + std::list::iterator m_ia, m_ib; + RNetwork *mp_network; + const RNode *mp_a, *mp_b; + +private: + RElement (const RElement &other); + RElement &operator= (const RElement &other); +}; + +/** + * @brief Represents a R network (a graph of RNode and RElement) + */ +class PEX_PUBLIC RNetwork + : public tl::Object +{ +public: + typedef tl::list node_list; + typedef node_list::const_iterator node_iterator; + typedef tl::list element_list; + typedef element_list::const_iterator element_iterator; + + /** + * @brief Constructor + */ + RNetwork (); + + /** + * @brief Destructor + */ + ~RNetwork (); + + /** + * @brief Creates a node with the given type and port index + * + * If the node type is Internal, a new node is created always. + * If the node type is VertexPort or PolygonPort, an existing + * node is returned if one way created with the same type + * or port index already. This avoids creating duplicates + * for the same port. + */ + RNode *create_node (RNode::node_type type, unsigned int port_index); + + /** + * @brief Creates a new element between the given nodes + * + * If an element already exists between the specified nodes, the + * given value is added to the existing element and the existing + * object is returned. + */ + RElement *create_element (double conductance, RNode *a, RNode *b); + + /** + * @brief Removes the given element + * + * Removing the element will also remove any orphan nodes + * at the ends if they are of type Internal. + */ + void remove_element (RElement *element); + + /** + * @brief Removes the node and the attached elements. + * + * Only nodes of type Internal can be removed. + */ + void remove_node (RNode *node); + + /** + * @brief Clears the network + */ + void clear (); + + /** + * @brief Simplifies the network + * + * This will: + * - Join serial resistors if connected by an internal node + * - Remove shorts and join the nodes, if one of them is + * an internal node. The non-internal node will persist. + * - Remove "dangling" resistors if the dangling node is + * an internal one + */ + void simplify (); + + /** + * @brief Iterate the nodes (begin) + */ + node_iterator begin_nodes () const + { + return m_nodes.begin (); + } + + /** + * @brief Iterate the nodes (end) + */ + node_iterator end_nodes () const + { + return m_nodes.end (); + } + + /** + * @brief Gets the number of nodes + */ + size_t num_nodes () const + { + return m_nodes.size (); + } + + /** + * @brief Gets the number of internal nodes + */ + size_t num_internal_nodes () const + { + size_t count = 0; + for (auto n = m_nodes.begin (); n != m_nodes.end (); ++n) { + if (n->type == pex::RNode::Internal) { + ++count; + } + } + return count; + } + + /** + * @brief Iterate the elements (begin) + */ + element_iterator begin_elements () const + { + return m_elements.begin (); + } + + /** + * @brief Iterate the elements (end) + */ + element_iterator end_elements () const + { + return m_elements.end (); + } + + /** + * @brief Gets the number of elements + */ + size_t num_elements () const + { + return m_elements.size (); + } + + /** + * @brief Returns a string representation of the graph + */ + std::string to_string () const; + +private: + node_list m_nodes; + element_list m_elements; + std::map, RElement *> m_elements_by_nodes; + std::map, RNode *> m_nodes_by_type; + + RNetwork (const RNetwork &); + RNetwork &operator= (const RNetwork &); + + void join_nodes (RNode *a, RNode *b); +}; + +} + +#endif + diff --git a/src/pex/pex/pexSquareCountingRExtractor.h b/src/pex/pex/pexSquareCountingRExtractor.h index 2d95b3e26..e5228ad87 100644 --- a/src/pex/pex/pexSquareCountingRExtractor.h +++ b/src/pex/pex/pexSquareCountingRExtractor.h @@ -24,6 +24,7 @@ #define HDR_pexSquareCountingRExtractor #include "pexCommon.h" +#include "pexRNetwork.h" #include "pexRExtractor.h" #include "dbPLCConvexDecomposition.h" diff --git a/src/pex/pex/pexTriangulationRExtractor.h b/src/pex/pex/pexTriangulationRExtractor.h index 6749c7f84..634cf81eb 100644 --- a/src/pex/pex/pexTriangulationRExtractor.h +++ b/src/pex/pex/pexTriangulationRExtractor.h @@ -24,6 +24,7 @@ #define HDR_pexTriangulationRExtractor #include "pexCommon.h" +#include "pexRNetwork.h" #include "pexRExtractor.h" #include "dbPLCTriangulation.h" diff --git a/src/pex/unit_tests/pexRExtractorTests.cc b/src/pex/unit_tests/pexRExtractorTests.cc index b0ace9056..6e04a6f6a 100644 --- a/src/pex/unit_tests/pexRExtractorTests.cc +++ b/src/pex/unit_tests/pexRExtractorTests.cc @@ -22,6 +22,7 @@ #include "pexRExtractor.h" +#include "pexRNetwork.h" #include "tlUnitTest.h" TEST(network_basic) From f83cd61843037a875cb240732541c81cc42fc6d8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 4 May 2025 13:11:46 +0200 Subject: [PATCH 191/392] WIP: debugging, tests. --- src/db/db/dbPropertiesRepository.cc | 6 +- src/db/db/dbPropertiesRepository.h | 2 +- src/pex/pex/pexRNetExtractor.cc | 68 ++++++---- src/pex/pex/pexRNetExtractor.h | 17 ++- src/pex/pex/pexRNetwork.cc | 18 ++- src/pex/pex/pexRNetwork.h | 6 +- src/pex/pex/pexSquareCountingRExtractor.cc | 5 +- src/pex/unit_tests/pexRNetExtractorTests.cc | 139 ++++++++++++++++++++ src/pex/unit_tests/unit_tests.pro | 1 + testdata/pex/netex_viagen1.gds | Bin 0 -> 414 bytes testdata/pex/netex_viagen2.gds | Bin 0 -> 630 bytes 11 files changed, 220 insertions(+), 42 deletions(-) create mode 100644 src/pex/unit_tests/pexRNetExtractorTests.cc create mode 100644 testdata/pex/netex_viagen1.gds create mode 100644 testdata/pex/netex_viagen2.gds diff --git a/src/db/db/dbPropertiesRepository.cc b/src/db/db/dbPropertiesRepository.cc index 790b2a919..bba686627 100644 --- a/src/db/db/dbPropertiesRepository.cc +++ b/src/db/db/dbPropertiesRepository.cc @@ -373,7 +373,7 @@ PropertiesRepository::prop_name_id (const tl::Variant &name) } } -property_names_id_type +property_values_id_type PropertiesRepository::prop_value_id (const tl::Variant &value) { tl::MutexLocker locker (&m_lock); @@ -383,9 +383,9 @@ PropertiesRepository::prop_value_id (const tl::Variant &value) m_property_values_heap.push_back (value); const tl::Variant &new_value = m_property_values_heap.back (); m_propvalues.insert (&new_value); - return property_names_id_type (&new_value); + return property_values_id_type (&new_value); } else { - return property_names_id_type (*pi); + return property_values_id_type (*pi); } } diff --git a/src/db/db/dbPropertiesRepository.h b/src/db/db/dbPropertiesRepository.h index a5bbf7a9f..99388e326 100644 --- a/src/db/db/dbPropertiesRepository.h +++ b/src/db/db/dbPropertiesRepository.h @@ -358,7 +358,7 @@ public: * This method will assign a new ID to the given value if required and * return the ID associated with it. */ - property_names_id_type prop_value_id (const tl::Variant &name); + property_values_id_type prop_value_id (const tl::Variant &name); /** * @brief Get the ID for a name diff --git a/src/pex/pex/pexRNetExtractor.cc b/src/pex/pex/pexRNetExtractor.cc index 78f4f6a4b..592d48859 100644 --- a/src/pex/pex/pexRNetExtractor.cc +++ b/src/pex/pex/pexRNetExtractor.cc @@ -33,6 +33,7 @@ #include "dbRegionProcessors.h" #include "dbCompoundOperation.h" #include "dbPolygonNeighborhood.h" +#include "dbPropertiesRepository.h" namespace pex { @@ -121,10 +122,11 @@ class ViaAggregationVisitor : public db::PolygonNeighborhoodVisitor { public: - ViaAggregationVisitor (const RExtractorTechVia *via_tech, std::vector > *conductances, double dbu) - : mp_via_tech (via_tech), mp_conductances (conductances), m_dbu (dbu) + ViaAggregationVisitor (const RExtractorTechVia *via_tech, double dbu) + : mp_via_tech (via_tech), m_dbu (dbu) { - // .. nothing yet .. + // this is just for consistency - we actually do not produce output + set_result_type (db::CompoundRegionCheckOperationNode::Region); } virtual void neighbors (const db::Layout * /*layout*/, const db::Cell * /*cell*/, const db::PolygonWithProperties &polygon, const neighbors_type &neighbors) @@ -145,15 +147,44 @@ public: } } - mp_conductances->push_back (std::make_pair (c, polygon.box ().center ())); + db::PropertiesSet ps; + ps.insert (prop_name_id, tl::Variant (c)); + + output_polygon (db::PolygonWithProperties (polygon, db::properties_id (ps))); } + static db::property_names_id_type prop_name_id; + private: const RExtractorTechVia *mp_via_tech; std::vector > *mp_conductances; + db::property_names_id_type m_prop_name_id; double m_dbu; }; +db::property_names_id_type ViaAggregationVisitor::prop_name_id = db::property_names_id (tl::Variant ()); + +} + +void +RNetExtractor::create_via_port (const pex::RExtractorTechVia &tech, + double conductance, + const db::Polygon &poly, + unsigned int &port_index, + std::map > &vias, + RNetwork &rnetwork) +{ + RNode *a = rnetwork.create_node (RNode::Internal, port_index++); + RNode *b = rnetwork.create_node (RNode::Internal, port_index++); + + db::CplxTrans to_um (m_dbu); + db::Box box = poly.box (); + b->location = a->location = to_um * box; + + rnetwork.create_element (conductance, a, b); + + vias[tech.bottom_conductor].push_back (ViaPort (box.center (), a)); + vias[tech.top_conductor].push_back (ViaPort (box.center (), b)); } void @@ -162,7 +193,7 @@ RNetExtractor::create_via_ports (const RExtractorTech &tech, std::map > &vias, RNetwork &rnetwork) { - std::vector > via_conductances; + unsigned int port_index = 0; for (auto v = tech.vias.begin (); v != tech.vias.end (); ++v) { @@ -171,8 +202,6 @@ RNetExtractor::create_via_ports (const RExtractorTech &tech, continue; } - via_conductances.clear (); - if (v->merge_distance > db::epsilon) { // with merge, follow this scheme: @@ -189,32 +218,23 @@ RNetExtractor::create_via_ports (const RExtractorTech &tech, children.push_back (new db::CompoundRegionOperationPrimaryNode ()); children.push_back (new db::CompoundRegionOperationSecondaryNode (const_cast (&g->second))); - ViaAggregationVisitor visitor (v.operator-> (), &via_conductances, m_dbu); + ViaAggregationVisitor visitor (v.operator-> (), m_dbu); db::PolygonNeighborhoodCompoundOperationNode en_node (children, &visitor, 0); - merged_vias.cop_to_region (en_node); + auto aggregated = merged_vias.cop_to_region (en_node); + + for (auto p = aggregated.begin (); ! p.at_end (); ++p) { + double c = db::properties (p.prop_id ()).value (ViaAggregationVisitor::prop_name_id).to_double (); + create_via_port (*v, c, *p, port_index, vias, rnetwork); + } } else { for (auto p = g->second.begin_merged (); ! p.at_end (); ++p) { - via_conductances.push_back (std::make_pair (via_conductance (*v, *p, m_dbu), p->box ().center ())); + create_via_port (*v, via_conductance (*v, *p, m_dbu), *p, port_index, vias, rnetwork); } } - // create the via resistor elements - unsigned int port_index = 0; - for (auto vc = via_conductances.begin (); vc != via_conductances.end (); ++vc) { - - RNode *a = rnetwork.create_node (RNode::Internal, port_index++); - RNode *b = rnetwork.create_node (RNode::Internal, port_index++); - - rnetwork.create_element (vc->first, a, b); - - vias[v->bottom_conductor].push_back (ViaPort (vc->second, a)); - vias[v->top_conductor].push_back (ViaPort (vc->second, b)); - - } - } } diff --git a/src/pex/pex/pexRNetExtractor.h b/src/pex/pex/pexRNetExtractor.h index a2df7d6aa..20ea0ab77 100644 --- a/src/pex/pex/pexRNetExtractor.h +++ b/src/pex/pex/pexRNetExtractor.h @@ -30,6 +30,7 @@ namespace pex { class RExtractorTech; +class RExtractorTechVia; class RExtractorTechConductor; class RNetwork; class RNode; @@ -37,7 +38,7 @@ class RNode; /** * @brief Implementation of the R extractor for a multi-polygon/multi-layer net */ -class RNetExtractor +class PEX_PUBLIC RNetExtractor { public: /** @@ -74,9 +75,7 @@ public: RNode *node; }; -private: - double m_dbu; - +protected: void create_via_ports (const RExtractorTech &tech, const std::map &geo, std::map > &vias, @@ -90,6 +89,16 @@ private: unsigned int polygon_ports_index_offset, const std::vector &via_ports, RNetwork &rnetwork); + +private: + double m_dbu; + + void create_via_port (const RExtractorTechVia &tech, + double conductance, + const db::Polygon &poly, + unsigned int &port_index, + std::map > &vias, + RNetwork &rnetwork); }; } diff --git a/src/pex/pex/pexRNetwork.cc b/src/pex/pex/pexRNetwork.cc index 0a552e2e2..435590d81 100644 --- a/src/pex/pex/pexRNetwork.cc +++ b/src/pex/pex/pexRNetwork.cc @@ -30,9 +30,10 @@ namespace pex // ----------------------------------------------------------------------------- std::string -RNode::to_string () const +RNode::to_string (bool with_coords) const { std::string res; + switch (type) { default: res += "$" + tl::to_string (port_index); @@ -44,24 +45,29 @@ RNode::to_string () const res += "P" + tl::to_string (port_index); break; } + + if (with_coords) { + res += location.to_string (); + } + return res; } // ----------------------------------------------------------------------------- std::string -RElement::to_string () const +RElement::to_string (bool with_coords) const { std::string na; if (a ()) { - na = a ()->to_string (); + na = a ()->to_string (with_coords); } else { na = "(nil)"; } std::string nb; if (b ()) { - nb = b ()->to_string (); + nb = b ()->to_string (with_coords); } else { nb = "(nil)"; } @@ -88,14 +94,14 @@ RNetwork::~RNetwork () } std::string -RNetwork::to_string () const +RNetwork::to_string (bool with_coords) const { std::string res; for (auto e = m_elements.begin (); e != m_elements.end (); ++e) { if (! res.empty ()) { res += "\n"; } - res += e->to_string (); + res += e->to_string (with_coords); } return res; } diff --git a/src/pex/pex/pexRNetwork.h b/src/pex/pex/pexRNetwork.h index d0fe65a9a..45f4ec36a 100644 --- a/src/pex/pex/pexRNetwork.h +++ b/src/pex/pex/pexRNetwork.h @@ -91,7 +91,7 @@ public: /** * @brief Returns a string representation of the node */ - std::string to_string () const; + std::string to_string (bool with_coords = false) const; /** * @brief Gets the network the node lives in @@ -180,7 +180,7 @@ struct PEX_PUBLIC RElement /** * @brief Returns a string representation of the element */ - std::string to_string () const; + std::string to_string (bool with_coords = false) const; /** * @brief Gets the network the node lives in @@ -357,7 +357,7 @@ public: /** * @brief Returns a string representation of the graph */ - std::string to_string () const; + std::string to_string (bool with_coords = false) const; private: node_list m_nodes; diff --git a/src/pex/pex/pexSquareCountingRExtractor.cc b/src/pex/pex/pexSquareCountingRExtractor.cc index 9170239d0..5c69b1fb3 100644 --- a/src/pex/pex/pexSquareCountingRExtractor.cc +++ b/src/pex/pex/pexSquareCountingRExtractor.cc @@ -29,6 +29,9 @@ namespace pex { +// Value used for number of squares for width 0 (should not happen) +const double infinite_squares = 1e10; + namespace { @@ -103,7 +106,7 @@ double calculate_squares (db::Coord x1, db::Coord x2, const std::set & // integrate the resistance along the axis x1->x2 with w=w1->w2 if (w1 < db::epsilon) { - return 1e9; // @@@ + return infinite_squares; } else if (fabs (w1 - w2) < db::epsilon) { return (x2 - x1) / w1; } else { diff --git a/src/pex/unit_tests/pexRNetExtractorTests.cc b/src/pex/unit_tests/pexRNetExtractorTests.cc new file mode 100644 index 000000000..241ded8df --- /dev/null +++ b/src/pex/unit_tests/pexRNetExtractorTests.cc @@ -0,0 +1,139 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "pexRNetExtractor.h" +#include "pexRExtractorTech.h" +#include "pexRNetwork.h" +#include "dbReader.h" +#include "dbLayout.h" +#include "tlUnitTest.h" + +class TestableRNetExtractor + : public pex::RNetExtractor +{ +public: + TestableRNetExtractor (double dbu) : pex::RNetExtractor (dbu) { } + + using pex::RNetExtractor::create_via_ports; +}; + +TEST(netex_viagen1) +{ + db::Layout ly; + + { + std::string fn = tl::testdata () + "/pex/netex_viagen1.gds"; + tl::InputStream is (fn); + db::Reader reader (is); + reader.read (ly); + } + + TestableRNetExtractor rex (ly.dbu ()); + + auto tc = ly.cell_by_name ("TOP"); + tl_assert (tc.first); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + + std::map geo; + geo.insert (std::make_pair (l2, db::Region (db::RecursiveShapeIterator (ly, ly.cell (tc.second), l2)))); + + pex::RNetwork network; + + pex::RExtractorTech tech; + + pex::RExtractorTechVia via1; + via1.bottom_conductor = l1; + via1.cut_layer = l2; + via1.top_conductor = l3; + via1.resistance = 2.0; + tech.vias.push_back (via1); + + std::map > via_ports; + rex.create_via_ports (tech, geo, via_ports, network); + + EXPECT_EQ (via_ports [l1].size (), size_t (4)); + EXPECT_EQ (via_ports [l2].size (), size_t (0)); + EXPECT_EQ (via_ports [l3].size (), size_t (4)); + + EXPECT_EQ (network.to_string (true), + "R $0(1.7,0.1;1.9,0.3) $1(1.7,0.1;1.9,0.3) 50\n" + "R $2(0.4,0.5;0.6,0.7) $3(0.4,0.5;0.6,0.7) 50\n" + "R $4(0.8,0.5;1,0.7) $5(0.8,0.5;1,0.7) 50\n" + "R $6(2.9,0.5;3.1,0.7) $7(2.9,0.5;3.1,0.7) 50" + ); +} + +TEST(netex_viagen2) +{ + db::Layout ly; + + { + std::string fn = tl::testdata () + "/pex/netex_viagen2.gds"; + tl::InputStream is (fn); + db::Reader reader (is); + reader.read (ly); + } + + TestableRNetExtractor rex (ly.dbu ()); + + auto tc = ly.cell_by_name ("TOP"); + tl_assert (tc.first); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + + std::map geo; + geo.insert (std::make_pair (l2, db::Region (db::RecursiveShapeIterator (ly, ly.cell (tc.second), l2)))); + + pex::RNetwork network; + + pex::RExtractorTech tech; + + pex::RExtractorTechVia via1; + via1.bottom_conductor = l1; + via1.cut_layer = l2; + via1.top_conductor = l3; + via1.resistance = 2.0; + via1.merge_distance = 0.2; + tech.vias.push_back (via1); + + std::map > via_ports; + rex.create_via_ports (tech, geo, via_ports, network); + + EXPECT_EQ (via_ports [l1].size (), size_t (6)); + EXPECT_EQ (via_ports [l2].size (), size_t (0)); + EXPECT_EQ (via_ports [l3].size (), size_t (6)); + + EXPECT_EQ (network.to_string (true), + "R $0(4.6,2.8;4.8,3) $1(4.6,2.8;4.8,3) 50\n" + "R $2(2.5,3.7;2.7,3.9) $3(2.5,3.7;2.7,3.9) 50\n" + "R $4(3,3.7;3.2,3.9) $5(3,3.7;3.2,3.9) 50\n" + "R $6(2.2,1.2;3.4,3.4) $7(2.2,1.2;3.4,3.4) 2.77778\n" + "R $8(0.4,0.4;2.2,4.2) $9(0.4,0.4;2.2,4.2) 1\n" + "R $10(0.6,4.9;1.2,5.1) $11(0.6,4.9;1.2,5.1) 25" + ); +} diff --git a/src/pex/unit_tests/unit_tests.pro b/src/pex/unit_tests/unit_tests.pro index ad99cd169..6ef8b5f93 100644 --- a/src/pex/unit_tests/unit_tests.pro +++ b/src/pex/unit_tests/unit_tests.pro @@ -8,6 +8,7 @@ include($$PWD/../../lib_ut.pri) SOURCES = \ pexRExtractorTests.cc \ + pexRNetExtractorTests.cc \ pexSquareCountingRExtractorTests.cc \ pexTriangulationRExtractorTests.cc diff --git a/testdata/pex/netex_viagen1.gds b/testdata/pex/netex_viagen1.gds new file mode 100644 index 0000000000000000000000000000000000000000..2d7195da23071671e1fc7991e954bc22ef37f67a GIT binary patch literal 414 zcmZQzV_;&6V31*CVt>iN%D}?F&0xqNgUn{&U}E#}bYfr-VP>^+>@@d2w)}&o%MSeo zv!g;7WLWX&V`B^RbYx&);b353<7HxCVqoKAVqjp<5n%rR|Nk5q28yI0W0*LIW?&Hn z>St#lWKW2H08kw_0|SQ;#7;ITMxdEIGOSKfKpF%%gqau^flx+(nSp_O2Ll7p*XT6Z zHX=-6f|$bG0x<JKopMim}1n3T+8W7E>!@$6vgP>vJKpN&}78V8o@})XB literal 0 HcmV?d00001 diff --git a/testdata/pex/netex_viagen2.gds b/testdata/pex/netex_viagen2.gds new file mode 100644 index 0000000000000000000000000000000000000000..d218db4937350f9e918ba427f1fb5c306b2bcaf4 GIT binary patch literal 630 zcmb7>ze)o^5Qo2=+uba=Gvr?-TZ=`&CLlIKf=w`>#UT@4ih$*qwPLC;-=-_Ee}&Fm~5k#nMv)*B8>&X}F`GeP`(Hicp44ktJJ$Ip-N2S-nP z4|j`_=zhub{%ke@8^CV~g6{}0>Sk3{-LFg|m+Q8QFBRL<{1$)JDNj!IUJjw3YWi)X z%p$t!LUp;qKrp&vH-r8K Date: Sun, 4 May 2025 14:36:34 +0200 Subject: [PATCH 192/392] WIP: debugging, tests. Triangulation should be safer now against linear chains of vertexes. --- src/db/db/dbPLCTriangulation.cc | 22 ++++- src/db/unit_tests/dbPLCTriangulationTests.cc | 22 ++++- src/pex/pex/pexRNetExtractor.cc | 16 +++- src/pex/pex/pexSquareCountingRExtractor.cc | 5 +- src/pex/unit_tests/pexRNetExtractorTests.cc | 81 +++++++++++++++++++ testdata/pex/netex_test1.gds | Bin 0 -> 1066 bytes 6 files changed, 134 insertions(+), 12 deletions(-) create mode 100644 testdata/pex/netex_test1.gds diff --git a/src/db/db/dbPLCTriangulation.cc b/src/db/db/dbPLCTriangulation.cc index f8f4fdcc9..70f784f3a 100644 --- a/src/db/db/dbPLCTriangulation.cc +++ b/src/db/db/dbPLCTriangulation.cc @@ -1471,12 +1471,17 @@ Triangulation::triangulate (const db::Region ®ion, const std::vector > edge_contours; + for (auto p = region.begin_merged (); ! p.at_end (); ++p) { + make_contours (*p, trans, edge_contours); + } + unsigned int id = 0; for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { insert_point (trans * *v)->set_is_precious (true, id++); } - create_constrained_delaunay (region, trans); + constrain (edge_contours); refine (parameters); } @@ -1495,12 +1500,15 @@ Triangulation::triangulate (const db::Polygon &poly, const std::vector > edge_contours; + make_contours (poly, trans, edge_contours); + unsigned int id = 0; for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { insert_point (trans * *v)->set_is_precious (true, id++); } - create_constrained_delaunay (poly, trans); + constrain (edge_contours); refine (parameters); } @@ -1517,12 +1525,15 @@ Triangulation::triangulate (const db::Polygon &poly, const std::vector > edge_contours; + make_contours (poly, trans, edge_contours); + unsigned int id = 0; for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { insert_point (trans * *v)->set_is_precious (true, id++); } - create_constrained_delaunay (poly, trans); + constrain (edge_contours); refine (parameters); } @@ -1539,12 +1550,15 @@ Triangulation::triangulate (const db::DPolygon &poly, const std::vector > edge_contours; + make_contours (poly, trans, edge_contours); + unsigned int id = 0; for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { insert_point (trans * *v)->set_is_precious (true, id++); } - create_constrained_delaunay (poly, trans); + constrain (edge_contours); refine (parameters); } diff --git a/src/db/unit_tests/dbPLCTriangulationTests.cc b/src/db/unit_tests/dbPLCTriangulationTests.cc index f31a049b2..2e3845e54 100644 --- a/src/db/unit_tests/dbPLCTriangulationTests.cc +++ b/src/db/unit_tests/dbPLCTriangulationTests.cc @@ -1118,11 +1118,29 @@ TEST(triangulate_with_vertexes) EXPECT_EQ (vp, 0); } + // normal triangulation vertexes.clear (); vertexes.push_back (db::Point (50, 50)); tri.triangulate (poly, vertexes, param, trans); - EXPECT_EQ (plc.to_string (), "((-0.45, 0), (-0.5, -0.05), (-0.5, 0.05)), ((0.5, 0.05), (-0.45, 0), (-0.5, 0.05)), ((-0.45, 0), (0.5, -0.05), (-0.5, -0.05)), ((-0.45, 0), (0.5, 0.05), (0.5, -0.05))"); + EXPECT_EQ (plc.to_string (), "((-0.5, -0.05), (-0.5, 0.05), (-0.45, 0)), ((-0.5, 0.05), (0.5, 0.05), (-0.45, 0)), ((0.5, -0.05), (-0.45, 0), (0.5, 0.05)), ((0.5, -0.05), (-0.5, -0.05), (-0.45, 0))"); + + for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { + auto *vp = tri.find_vertex_for_point (trans * *v); + if (! vp) { + tl::warn << "Vertex not present in output: " << v->to_string (); + EXPECT_EQ (1, 0); + } + } + + // linear chain of vertexes must not break triangulation + vertexes.clear (); + vertexes.push_back (db::Point (50, 50)); + vertexes.push_back (db::Point (100, 50)); + vertexes.push_back (db::Point (150, 50)); + tri.triangulate (poly, vertexes, param, trans); + + EXPECT_EQ (plc.to_string (), "((-0.5, -0.05), (-0.5, 0.05), (-0.45, 0)), ((-0.4, 0), (-0.45, 0), (-0.5, 0.05)), ((-0.5, -0.05), (-0.45, 0), (-0.4, 0)), ((0.5, -0.05), (-0.35, 0), (0.5, 0.05)), ((-0.5, -0.05), (-0.35, 0), (0.5, -0.05)), ((-0.5, -0.05), (-0.4, 0), (-0.35, 0)), ((-0.35, 0), (-0.5, 0.05), (0.5, 0.05)), ((-0.35, 0), (-0.4, 0), (-0.5, 0.05))"); for (auto v = vertexes.begin (); v != vertexes.end (); ++v) { auto *vp = tri.find_vertex_for_point (trans * *v); @@ -1139,7 +1157,7 @@ TEST(triangulate_with_vertexes) tri.triangulate (poly, vertexes, param, trans); EXPECT_GT (plc.num_polygons (), size_t (380)); - EXPECT_LT (plc.num_polygons (), size_t (400)); + EXPECT_LT (plc.num_polygons (), size_t (420)); for (auto t = plc.begin (); t != plc.end (); ++t) { EXPECT_LE (t->area (), param.max_area); diff --git a/src/pex/pex/pexRNetExtractor.cc b/src/pex/pex/pexRNetExtractor.cc index 592d48859..5894ac356 100644 --- a/src/pex/pex/pexRNetExtractor.cc +++ b/src/pex/pex/pexRNetExtractor.cc @@ -79,7 +79,7 @@ RNetExtractor::extract (const RExtractorTech &tech, vp_offset += p->second.size (); } for (auto p = polygon_ports.begin (); p != polygon_ports.end () && p->first < g->first; ++p) { - vp_offset += p->second.size (); + pp_offset += p->second.size (); } // fetch the port list for vertex ports @@ -377,6 +377,7 @@ private: // for internal nodes always create a node in the target network global = mp_rnetwork->create_node (local->type, ++m_next_internal_port_index); + global->location = local->location; } else if (local->type == RNode::VertexPort) { @@ -409,7 +410,7 @@ private: if (i2n != m_id_to_node.end ()) { global = i2n->second; } else { - global = mp_rnetwork->create_node (RNode::VertexPort, index_from_id (id) + m_polygon_port_index_offset); + global = mp_rnetwork->create_node (RNode::PolygonPort, index_from_id (id) + m_polygon_port_index_offset); global->location = local->location; m_id_to_node.insert (std::make_pair (id, global)); } @@ -422,7 +423,7 @@ private: } // create the R elements in the target network - for (auto e = local_network.begin_elements (); e != local_network.begin_elements (); ++e) { + for (auto e = local_network.begin_elements (); e != local_network.end_elements (); ++e) { const RElement *local = e.operator-> (); @@ -431,7 +432,14 @@ private: tl_assert (ia != n2n.end ()); tl_assert (ia != n2n.end ()); - mp_rnetwork->create_element (local->conductance, ia->second, ib->second); + double c; + if (mp_cond->resistance < 1e-10) { + c = RElement::short_value (); + } else { + c = local->conductance / mp_cond->resistance; + } + + mp_rnetwork->create_element (c, ia->second, ib->second); } } diff --git a/src/pex/pex/pexSquareCountingRExtractor.cc b/src/pex/pex/pexSquareCountingRExtractor.cc index 5c69b1fb3..acf9689d1 100644 --- a/src/pex/pex/pexSquareCountingRExtractor.cc +++ b/src/pex/pex/pexSquareCountingRExtractor.cc @@ -181,7 +181,8 @@ SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector { rnetwork.clear (); - db::CplxTrans trans = db::CplxTrans (m_dbu) * db::ICplxTrans (db::Trans (db::Point () - polygon.box ().center ())); + db::CplxTrans to_um (m_dbu); + db::CplxTrans trans = to_um * db::ICplxTrans (db::Trans (db::Point () - polygon.box ().center ())); auto inv_trans = trans.inverted (); db::plc::Graph plc; @@ -285,7 +286,7 @@ SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector auto n4p = nodes_for_ports.find (p->first); if (n4p == nodes_for_ports.end ()) { pex::RNode *node = rnetwork.create_node (p->first.type, p->first.port_index); - node->location = trans * p->first.location; + node->location = to_um * p->first.location; n4p = nodes_for_ports.insert (std::make_pair (p->first, node)).first; } p->second = n4p->second; diff --git a/src/pex/unit_tests/pexRNetExtractorTests.cc b/src/pex/unit_tests/pexRNetExtractorTests.cc index 241ded8df..5227a323b 100644 --- a/src/pex/unit_tests/pexRNetExtractorTests.cc +++ b/src/pex/unit_tests/pexRNetExtractorTests.cc @@ -137,3 +137,84 @@ TEST(netex_viagen2) "R $10(0.6,4.9;1.2,5.1) $11(0.6,4.9;1.2,5.1) 25" ); } + +TEST(netex_2layer) +{ + db::Layout ly; + + { + std::string fn = tl::testdata () + "/pex/netex_test1.gds"; + tl::InputStream is (fn); + db::Reader reader (is); + reader.read (ly); + } + + TestableRNetExtractor rex (ly.dbu ()); + + auto tc = ly.cell_by_name ("TOP"); + tl_assert (tc.first); + + unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0)); + unsigned int l1p = ly.get_layer (db::LayerProperties (1, 1)); + unsigned int l1v = ly.get_layer (db::LayerProperties (1, 2)); + unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0)); + unsigned int l3 = ly.get_layer (db::LayerProperties (3, 0)); + unsigned int l3p = ly.get_layer (db::LayerProperties (3, 1)); + unsigned int l3v = ly.get_layer (db::LayerProperties (3, 2)); + + std::map geo; + geo.insert (std::make_pair (l1, db::Region (db::RecursiveShapeIterator (ly, ly.cell (tc.second), l1)))); + geo.insert (std::make_pair (l2, db::Region (db::RecursiveShapeIterator (ly, ly.cell (tc.second), l2)))); + geo.insert (std::make_pair (l3, db::Region (db::RecursiveShapeIterator (ly, ly.cell (tc.second), l3)))); + + pex::RNetwork network; + + pex::RExtractorTech tech; + + pex::RExtractorTechVia via1; + via1.bottom_conductor = l1; + via1.cut_layer = l2; + via1.top_conductor = l3; + via1.resistance = 2.0; + via1.merge_distance = 0.2; + tech.vias.push_back (via1); + + pex::RExtractorTechConductor cond1; + cond1.layer = l1; + cond1.resistance = 0.5; + tech.conductors.push_back (cond1); + + pex::RExtractorTechConductor cond2; + cond2.layer = l3; + cond2.resistance = 0.25; + tech.conductors.push_back (cond2); + + std::map > vertex_ports; + std::map > polygon_ports; + + db::Region l1p_region (db::RecursiveShapeIterator (ly, ly.cell (tc.second), l1p)); + for (auto p = l1p_region.begin_merged (); ! p.at_end (); ++p) { + polygon_ports[l1].push_back (*p); + } + + db::Region l3p_region (db::RecursiveShapeIterator (ly, ly.cell (tc.second), l3p)); + for (auto p = l3p_region.begin_merged (); ! p.at_end (); ++p) { + polygon_ports[l3].push_back (*p); + } + + db::Region l1v_region (db::RecursiveShapeIterator (ly, ly.cell (tc.second), l1v)); + for (auto p = l1v_region.begin_merged (); ! p.at_end (); ++p) { + vertex_ports[l1].push_back (p->box ().center ()); + } + + db::Region l3v_region (db::RecursiveShapeIterator (ly, ly.cell (tc.second), l3v)); + for (auto p = l3v_region.begin_merged (); ! p.at_end (); ++p) { + vertex_ports[l3].push_back (p->box ().center ()); + } + + rex.extract (tech, geo, vertex_ports, polygon_ports, network); + + EXPECT_EQ (network.to_string (true), + "" + ); +} diff --git a/testdata/pex/netex_test1.gds b/testdata/pex/netex_test1.gds new file mode 100644 index 0000000000000000000000000000000000000000..3eef63f00905aea0289f9ba77a3478d07e320a94 GIT binary patch literal 1066 zcmb7@u}i~16vn??l50RK(dr^W0+F_ZAc&$aLW@I-1s#e==l%%}4*mmf-F0wtb?net z{8L)%P>P+;cX>H`SlVjf_$BZ1z3+QR0tq2*5IG_@fDaFO>>#%95eV6+SAk8}KdU?( zU%b7HPS3mh&yTGpB7cES%IkUs@B%>Q1q9@p0I(OjN~tHasM5BVscXVpCw&gfe43Y6 z^xg^(Y_O-?cBtoS z1&-n=O`T}#UE4nyu|65;$=RQ2>M7^yOQBNZrq(Gnxn+MtQzy>V>)f1^=hn0HGW8jM zK0il)zraD8_P?0J25{J;`7@e2(e`J(V0~eij*h;2P0<6k?x!?$qOIriv;4goUUJbVN9W!lC7 literal 0 HcmV?d00001 From aca3095efa99281f664714fd04a2b6ecc692963b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 4 May 2025 15:38:45 +0200 Subject: [PATCH 193/392] WIP: allowing multiple vertex ports on the same location --- src/db/db/dbPLC.cc | 62 ++++++++++++++++--- src/db/db/dbPLC.h | 13 ++-- src/db/db/dbPLCTriangulation.h | 42 +++++++++---- .../dbPLCConvexDecompositionTests.cc | 2 +- src/db/unit_tests/dbPLCGraphTests.cc | 7 ++- src/pex/pex/pexRNetExtractor.cc | 4 +- src/pex/pex/pexSquareCountingRExtractor.cc | 4 +- src/pex/pex/pexTriangulationRExtractor.cc | 30 ++++++--- src/pex/unit_tests/pexRNetExtractorTests.cc | 14 ++++- .../pexTriangulationRExtractorTests.cc | 6 +- 10 files changed, 137 insertions(+), 47 deletions(-) diff --git a/src/db/db/dbPLC.cc b/src/db/db/dbPLC.cc index 7fbb26b44..8f7c650a5 100644 --- a/src/db/db/dbPLC.cc +++ b/src/db/db/dbPLC.cc @@ -43,47 +43,58 @@ namespace plc // Vertex implementation Vertex::Vertex (Graph *graph) - : DPoint (), mp_graph (graph), m_is_precious (false) + : DPoint (), mp_graph (graph), mp_ids (0) { // .. nothing yet .. } Vertex::Vertex (Graph *graph, const db::DPoint &p) - : DPoint (p), mp_graph (graph), m_is_precious (false) + : DPoint (p), mp_graph (graph), mp_ids (0) { // .. nothing yet .. } Vertex::Vertex (Graph *graph, const Vertex &v) - : DPoint (), mp_graph (graph), m_is_precious (false), m_id (0) + : DPoint (), mp_graph (graph), mp_ids (0) { operator= (v); } Vertex::Vertex (Graph *graph, db::DCoord x, db::DCoord y) - : DPoint (x, y), mp_graph (graph), m_is_precious (false), m_id (0) + : DPoint (x, y), mp_graph (graph), mp_ids (0) { // .. nothing yet .. } Vertex::Vertex (const Vertex &v) - : DPoint (v), mp_graph (v.mp_graph), m_is_precious (v.m_is_precious), m_id (v.m_id) + : DPoint (), mp_graph (v.mp_graph), mp_ids (0) { - // NOTE: edges are not copied! + operator= (v); } Vertex::~Vertex () { - // .. nothing yet .. + if (mp_ids) { + delete mp_ids; + mp_ids = 0; + } } Vertex &Vertex::operator= (const Vertex &v) { if (this != &v) { + // NOTE: edges are not copied! db::DPoint::operator= (v); - m_is_precious = v.m_is_precious; - m_id = v.m_id; + + if (mp_ids) { + delete mp_ids; + mp_ids = 0; + } + if (v.mp_ids) { + mp_ids = new std::set (*v.mp_ids); + } + } return *this; } @@ -99,6 +110,39 @@ Vertex::is_outside () const return false; } +void +Vertex::set_is_precious (bool f, unsigned int id) +{ + if (f) { + if (! mp_ids) { + mp_ids = new std::set (); + } + mp_ids->insert (id); + } else { + if (mp_ids) { + delete mp_ids; + mp_ids = 0; + } + } +} + +bool +Vertex::is_precious () const +{ + return mp_ids != 0; +} + +const std::set & +Vertex::ids () const +{ + if (mp_ids != 0) { + return *mp_ids; + } else { + static std::set empty; + return empty; + } +} + std::vector Vertex::polygons () const { diff --git a/src/db/db/dbPLC.h b/src/db/db/dbPLC.h index ea5ed4e38..6a65e61d1 100644 --- a/src/db/db/dbPLC.h +++ b/src/db/db/dbPLC.h @@ -131,16 +131,12 @@ public: * * "precious" vertexes are not removed during triangulation for example. */ - void set_is_precious (bool f, unsigned int id) - { - m_is_precious = f; - m_id = id; - } + void set_is_precious (bool f, unsigned int id); /** * @brief Gets a value indicating whether the vertex is precious */ - bool is_precious () const { return m_is_precious; } + bool is_precious () const; /** * @brief Gets the ID passed to "set_is_precious" @@ -148,7 +144,7 @@ public: * This ID can be used to identify the vertex in the context it came from (e.g. * index in point vector). */ - unsigned int id () const { return m_id; } + const std::set &ids () const; /** * @brief Returns a string representation of the vertex @@ -187,8 +183,7 @@ private: Graph *mp_graph; edges_type mp_edges; - bool m_is_precious : 1; - unsigned int m_id : 31; + std::set *mp_ids; }; /** diff --git a/src/db/db/dbPLCTriangulation.h b/src/db/db/dbPLCTriangulation.h index db97f9651..3d3f30d4d 100644 --- a/src/db/db/dbPLCTriangulation.h +++ b/src/db/db/dbPLCTriangulation.h @@ -236,10 +236,38 @@ public: /** * @brief Refines the triangulation using the given parameters * - * This method is used internally by the "triangulation" method after creating the basic triangulation. + * This method is used internally by the "triangulate" method after creating the basic triangulation. + * + * This method is provided as a partial solution of a triangulation for special cases. */ void refine (const TriangulationParameters ¶m); + /** + * @brief Given a set of contours with edges, mark outer triangles + * + * The edges must be made from existing vertexes. Edge orientation is + * clockwise. + * + * This will also mark triangles as outside ones. + * This method is used internally by the "triangulate" method after creating the basic triangulation. + * + * This method is provided as a partial solution of a triangulation for special cases. + */ + void constrain (const std::vector > &contours); + + /** + * @brief Inserts a contours of a polygon + * + * This method fills the contours of the given polygon by doint an "insert_point" + * on all points and logging the outer edges ("segments") into the "contours" + * array. The latter can be passed to "constrain" to create a constrained + * triangulation. + * + * This method is used internally by the "triangulate" method to create the basic triangulation. + * This method is provided as a partial solution of a triangulation for special cases. + */ + template void make_contours (const Poly &poly, const Trans &trans, std::vector > &contours); + protected: /** * @brief Checks the polygon graph for consistency @@ -287,16 +315,6 @@ protected: */ std::vector ensure_edge (Vertex *from, Vertex *to); - /** - * @brief Given a set of contours with edges, mark outer triangles - * - * The edges must be made from existing vertexes. Edge orientation is - * clockwise. - * - * This will also mark triangles as outside ones. - */ - void constrain (const std::vector > &contours); - /** * @brief Returns a value indicating whether the edge is "illegal" (violates the Delaunay criterion) */ @@ -313,8 +331,6 @@ private: size_t m_id; mutable size_t m_flips, m_hops; - template void make_contours (const Poly &poly, const Trans &trans, std::vector > &contours); - void remove_outside_vertex (Vertex *vertex, std::list > *new_triangles = 0); void remove_inside_vertex (Vertex *vertex, std::list > *new_triangles_out = 0); std::vector fill_concave_corners (const std::vector &edges); diff --git a/src/db/unit_tests/dbPLCConvexDecompositionTests.cc b/src/db/unit_tests/dbPLCConvexDecompositionTests.cc index 5ab556ab8..6b57ef505 100644 --- a/src/db/unit_tests/dbPLCConvexDecompositionTests.cc +++ b/src/db/unit_tests/dbPLCConvexDecompositionTests.cc @@ -145,7 +145,7 @@ TEST(internal_vertex) std::vector ip; for (size_t i = 0; i < p->internal_vertexes (); ++i) { - ip.push_back (p->internal_vertex (i)->to_string () + "#" + tl::to_string (p->internal_vertex (i)->id ())); + ip.push_back (p->internal_vertex (i)->to_string () + "#" + tl::to_string (p->internal_vertex (i)->ids ())); } std::sort (ip.begin (), ip.end ()); EXPECT_EQ (tl::join (ip, "/"), "(0, 0)#2/(0, 0.05)#0/(0.2, 0.07)#1"); diff --git a/src/db/unit_tests/dbPLCGraphTests.cc b/src/db/unit_tests/dbPLCGraphTests.cc index 29b7d9597..9a907a1c6 100644 --- a/src/db/unit_tests/dbPLCGraphTests.cc +++ b/src/db/unit_tests/dbPLCGraphTests.cc @@ -56,7 +56,12 @@ TEST(basic) EXPECT_EQ (v1->is_precious (), false); v1->set_is_precious (true, 17); EXPECT_EQ (v1->is_precious (), true); - EXPECT_EQ (v1->id (), 17u); + EXPECT_EQ (v1->ids ().size (), 1u); + EXPECT_EQ (*v1->ids ().begin (), 17u); + v1->set_is_precious (true, 1); + EXPECT_EQ (v1->is_precious (), true); + EXPECT_EQ (v1->ids ().size (), 2u); + EXPECT_EQ (*v1->ids ().begin (), 2u); } TEST(edge) diff --git a/src/pex/pex/pexRNetExtractor.cc b/src/pex/pex/pexRNetExtractor.cc index 5894ac356..3ce59e3ba 100644 --- a/src/pex/pex/pexRNetExtractor.cc +++ b/src/pex/pex/pexRNetExtractor.cc @@ -468,14 +468,14 @@ RNetExtractor::extract_conductor (const RExtractorTechConductor &cond, // type 0 objects (vertex ports) for (auto i = vertex_ports.begin (); i != vertex_ports.end (); ++i) { - // @@@ could be without enlarge? + // TODO: could be without enlarge? box_heap.push_back (db::Box (*i, *i).enlarged (db::Vector (1, 1))); scanner.insert2 (&box_heap.back (), make_id (i - vertex_ports.begin (), 0)); } // type 1 objects (via ports) for (auto i = via_ports.begin (); i != via_ports.end (); ++i) { - // @@@ could be without enlarge? + // TODO: could be without enlarge? box_heap.push_back (db::Box (i->position, i->position).enlarged (db::Vector (1, 1))); scanner.insert2 (&box_heap.back (), make_id (i - via_ports.begin (), 1)); } diff --git a/src/pex/pex/pexSquareCountingRExtractor.cc b/src/pex/pex/pexSquareCountingRExtractor.cc index acf9689d1..1abf32d95 100644 --- a/src/pex/pex/pexSquareCountingRExtractor.cc +++ b/src/pex/pex/pexSquareCountingRExtractor.cc @@ -269,7 +269,9 @@ SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector for (size_t i = 0; i < plc_poly->internal_vertexes (); ++i) { auto v = plc_poly->internal_vertex (i); db::Point loc = inv_trans * *v; - ports.push_back (std::make_pair (PortDefinition (pex::RNode::VertexPort, loc, v->id ()), (pex::RNode *) 0)); + for (auto pi = v->ids ().begin (); pi != v->ids ().end (); ++pi) { + ports.push_back (std::make_pair (PortDefinition (pex::RNode::VertexPort, loc, *pi), (pex::RNode *) 0)); + } } // 3. polygon ports diff --git a/src/pex/pex/pexTriangulationRExtractor.cc b/src/pex/pex/pexTriangulationRExtractor.cc index b39a6a37d..18f2e70da 100644 --- a/src/pex/pex/pexTriangulationRExtractor.cc +++ b/src/pex/pex/pexTriangulationRExtractor.cc @@ -77,6 +77,14 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector< tri.clear (); + std::vector > edge_contours; + + // first step of the triangulation + + for (auto p = residual_poly.begin_merged (); ! p.at_end (); ++p) { + tri.make_contours (*p, trans, edge_contours); + } + unsigned int id = 0; for (auto v = vertex_ports.begin (); v != vertex_ports.end (); ++v) { tri.insert_point (trans * *v)->set_is_precious (true, id++); @@ -91,9 +99,9 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector< } } - // perform the triangulation + // constrain and refine the triangulation - tri.create_constrained_delaunay (residual_poly, trans); + tri.constrain (edge_contours); tri.refine (param); // identify the vertexes present for the polygon port -> store them inside pp_vertexes @@ -147,11 +155,19 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector< } else if (vertex->is_precious ()) { - size_t port_index = size_t (vertex->id ()); - if (port_index < vertex_ports.size ()) { - n = rnetwork.create_node (pex::RNode::VertexPort, port_index); - n->location = db::DBox (*vertex, *vertex); - vports_present.insert (port_index); + for (auto pi = vertex->ids ().begin (); pi != vertex->ids ().end (); ++pi) { + size_t port_index = size_t (*pi); + if (port_index < vertex_ports.size ()) { + RNode *nn = rnetwork.create_node (pex::RNode::VertexPort, port_index); + nn->location = db::DBox (*vertex, *vertex); + if (n) { + // in case of multiple vertexes on the same spot, short them + rnetwork.create_element (RElement::short_value (), n, nn); + } else { + n = nn; + } + vports_present.insert (port_index); + } } } else { diff --git a/src/pex/unit_tests/pexRNetExtractorTests.cc b/src/pex/unit_tests/pexRNetExtractorTests.cc index 5227a323b..fac83d200 100644 --- a/src/pex/unit_tests/pexRNetExtractorTests.cc +++ b/src/pex/unit_tests/pexRNetExtractorTests.cc @@ -215,6 +215,18 @@ TEST(netex_2layer) rex.extract (tech, geo, vertex_ports, polygon_ports, network); EXPECT_EQ (network.to_string (true), - "" + "R $0(0.3,-5.7;0.5,-5.5) $1(0.3,-5.7;0.5,-5.5) 50\n" + "R $2(9.3,-5.9;9.9,-5.3) $3(9.3,-5.9;9.9,-5.3) 12.5\n" + "R $4(9.3,0.1;9.9,0.3) $5(9.3,0.1;9.9,0.3) 25\n" + "R $6(0.1,0.1;0.7,0.7) $7(0.1,0.1;0.7,0.7) 12.5\n" + "R $0(0.3,-5.7;0.5,-5.5) $2(9.3,-5.9;9.9,-5.3) 5.75\n" + "R $2(9.3,-5.9;9.9,-5.3) P0(12.9,-5.9;13.5,-5.3) 2.25\n" + "R $6(0.1,0.1;0.7,0.7) V0(5.2,0.4;5.2,0.4) 3\n" + "R $4(9.3,0.1;9.9,0.3) V0(5.2,0.4;5.2,0.4) 2.75\n" + "R $5(9.3,0.1;9.9,0.3) $8(10,-3.5;10,-2.7) 1.03125\n" + "R $3(9.3,-5.9;9.9,-5.3) $8(10,-3.5;10,-2.7) 0.78125\n" + "R $8(10,-3.5;10,-2.7) P1(12.9,-3.4;13.5,-2.8) 1\n" + "R $7(0.1,0.1;0.7,0.7) V1(0.4,-5.6;0.4,-5.6) 1.875\n" + "R $1(0.3,-5.7;0.5,-5.5) V1(0.4,-5.6;0.4,-5.6) 0" ); } diff --git a/src/pex/unit_tests/pexTriangulationRExtractorTests.cc b/src/pex/unit_tests/pexTriangulationRExtractorTests.cc index 09f1fce8f..ab485e7cb 100644 --- a/src/pex/unit_tests/pexTriangulationRExtractorTests.cc +++ b/src/pex/unit_tests/pexTriangulationRExtractorTests.cc @@ -301,7 +301,7 @@ TEST(extraction_analytic_disc) rex.extract (disc, vertex_ports, polygon_ports, rn); EXPECT_EQ (rn.to_string (), - "R P0 P1 0.245379" // theoretical: 1/(2*PI)*log(r2/r1) = 0.25615 with r2=10000, r1=2000 + "R P0 P1 0.245558" // theoretical: 1/(2*PI)*log(r2/r1) = 0.25615 with r2=10000, r1=2000 ) rex.triangulation_parameters ().max_area = 100000 * dbu * dbu; @@ -309,7 +309,7 @@ TEST(extraction_analytic_disc) rex.extract (disc, vertex_ports, polygon_ports, rn); EXPECT_EQ (rn.to_string (), - "R P0 P1 0.255614" // theoretical: 1/(2*PI)*log(r2/r1) = 0.25615 with r2=10000, r1=2000 + "R P0 P1 0.255609" // theoretical: 1/(2*PI)*log(r2/r1) = 0.25615 with r2=10000, r1=2000 ) } @@ -357,7 +357,7 @@ TEST(extraction_meander) rex.extract (poly, vertex_ports, polygon_ports, rn); EXPECT_EQ (rn.to_string (), - "R V0 V1 8.75751" // what is the "real" value? + "R V0 V1 8.61417" // what is the "real" value? ) } From f7c7e8b0be20578b6daf8dc865d4e319cf31b761 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 4 May 2025 15:41:26 +0200 Subject: [PATCH 194/392] Fixed unit tests --- src/db/unit_tests/dbPLCConvexDecompositionTests.cc | 2 +- src/db/unit_tests/dbPLCGraphTests.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db/unit_tests/dbPLCConvexDecompositionTests.cc b/src/db/unit_tests/dbPLCConvexDecompositionTests.cc index 6b57ef505..007e6d4d6 100644 --- a/src/db/unit_tests/dbPLCConvexDecompositionTests.cc +++ b/src/db/unit_tests/dbPLCConvexDecompositionTests.cc @@ -145,7 +145,7 @@ TEST(internal_vertex) std::vector ip; for (size_t i = 0; i < p->internal_vertexes (); ++i) { - ip.push_back (p->internal_vertex (i)->to_string () + "#" + tl::to_string (p->internal_vertex (i)->ids ())); + ip.push_back (p->internal_vertex (i)->to_string () + "#" + tl::join (p->internal_vertex (i)->ids ().begin (), p->internal_vertex (i)->ids ().end (), ",")); } std::sort (ip.begin (), ip.end ()); EXPECT_EQ (tl::join (ip, "/"), "(0, 0)#2/(0, 0.05)#0/(0.2, 0.07)#1"); diff --git a/src/db/unit_tests/dbPLCGraphTests.cc b/src/db/unit_tests/dbPLCGraphTests.cc index 9a907a1c6..1e91948f3 100644 --- a/src/db/unit_tests/dbPLCGraphTests.cc +++ b/src/db/unit_tests/dbPLCGraphTests.cc @@ -61,7 +61,7 @@ TEST(basic) v1->set_is_precious (true, 1); EXPECT_EQ (v1->is_precious (), true); EXPECT_EQ (v1->ids ().size (), 2u); - EXPECT_EQ (*v1->ids ().begin (), 2u); + EXPECT_EQ (*v1->ids ().begin (), 1u); } TEST(edge) From 2bc8ac235aeddfc639b66737b3385b0c51f926d1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 4 May 2025 16:22:03 +0200 Subject: [PATCH 195/392] Fixed unit tests --- src/pex/pex/gsiDeclRExtractor.cc | 13 ++++++++----- testdata/ruby/dbPolygonTest.rb | 6 +++--- testdata/ruby/dbSimplePolygonTest.rb | 12 ++++++------ 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/pex/pex/gsiDeclRExtractor.cc b/src/pex/pex/gsiDeclRExtractor.cc index 740765e33..8aadb05d3 100644 --- a/src/pex/pex/gsiDeclRExtractor.cc +++ b/src/pex/pex/gsiDeclRExtractor.cc @@ -38,7 +38,7 @@ public: pex::RNode::node_type type () const { return checked_pointer ()->type; } db::DBox location () const { return checked_pointer ()->location; } unsigned int port_index () const { return checked_pointer ()->port_index; } - std::string to_string () const { return checked_pointer ()->to_string (); } + std::string to_string (bool with_coords = false) const { return checked_pointer ()->to_string (with_coords); } size_t obj_id () const { @@ -89,7 +89,7 @@ public: RNode *a () const { return RNode::make_node_object (checked_pointer ()->a ()); } RNode *b () const { return RNode::make_node_object (checked_pointer ()->b ()); } - std::string to_string () const { return checked_pointer ()->to_string (); } + std::string to_string (bool with_coords = false) const { return checked_pointer ()->to_string (with_coords); } size_t obj_id () const { @@ -193,8 +193,9 @@ Class decl_RNode ("pex", "RNode", "ones may represent the same internal object. The 'object_id' is a ID that " "indicates the internal object. Same object_id means same node." ) + - gsi::method ("to_s", &RNode::to_string, + gsi::method ("to_s", &RNode::to_string, gsi::arg ("with_coords", false), "@brief Returns a string representation of this object\n" + "Nodes are printed with coordinates with 'with_coords' is true." ) + gsi::iterator_ext ("each_element", gsi::return_new_object (), &begin_node_elements, &end_network_elements, "@brief Iterates the \\RElement objects attached to the node\n" @@ -226,8 +227,9 @@ Class decl_RElement ("pex", "RElement", "ones may represent the same internal object. The 'object_id' is a ID that " "indicates the internal object. Same object_id means same element." ) + - gsi::method ("to_s", &RElement::to_string, + gsi::method ("to_s", &RElement::to_string, gsi::arg ("with_coords", false), "@brief Returns a string representation of this object\n" + "Nodes are printed with coordinates with 'with_coords' is true." ) + gsi::method ("resistance", &RElement::resistance, "@brief Gets the resistance value of the object\n" @@ -387,8 +389,9 @@ Class decl_RNetwork ("pex", "RNetwork", gsi::method ("num_elements", &pex::RNetwork::num_elements, "@brief Gets the number of elements in the network\n" ) + - gsi::method ("to_s", &pex::RNetwork::to_string, + gsi::method ("to_s", &pex::RNetwork::to_string, gsi::arg ("with_coords", false), "@brief Returns a string representation of the network\n" + "Nodes are printed with coordinates with 'with_coords' is true." ), "@brief Represents a network of resistors\n" "\n" diff --git a/testdata/ruby/dbPolygonTest.rb b/testdata/ruby/dbPolygonTest.rb index c9de10f8e..fc039c9a7 100644 --- a/testdata/ruby/dbPolygonTest.rb +++ b/testdata/ruby/dbPolygonTest.rb @@ -994,15 +994,15 @@ class DBPolygon_TestClass < TestBase assert_equal(p.delaunay(0.0, 0.5).to_s, "(0,0;0,100;250,0);(250,0;500,100;500,0);(250,0;0,100;500,100);(750,0;1000,100;1000,0);(500,0;500,100;750,0);(750,0;500,100;1000,100)") assert_equal(p.delaunay(20000, 0.0).to_s, "(0,0;250,50;500,0);(500,0;250,50;500,100);(250,50;0,100;500,100);(0,0;0,100;250,50);(500,0;500,100;750,50);(500,0;750,50;1000,0);(1000,0;750,50;1000,100);(750,50;500,100;1000,100)") - assert_equal(p.delaunay([ RBA::Point::new(50, 50) ]).to_s, "(0,0;0,100;50,50);(50,50;0,100;1000,100);(0,0;50,50;1000,0);(1000,0;50,50;1000,100)") + assert_equal(p.delaunay([ RBA::Point::new(50, 50) ]).to_s, "(0,0;0,100;50,50);(50,50;0,100;1000,100);(1000,0;50,50;1000,100);(0,0;50,50;1000,0)") p = RBA::DPolygon::new(RBA::DBox::new(0, 0, 1000, 100)) - assert_equal(p.delaunay.collect(&:to_s).join(";"), "(0,0;0,100;1000,100);(0,0;1000,100;1000,0)") + assert_equal(p.delaunay.each.collect(&:to_s).join(";"), "(0,0;0,100;1000,100);(0,0;1000,100;1000,0)") assert_equal(p.delaunay(0.0, 0.5).collect(&:to_s).join(";"), "(0,0;0,100;250,0);(250,0;500,100;500,0);(250,0;0,100;500,100);(750,0;1000,100;1000,0);(500,0;500,100;750,0);(750,0;500,100;1000,100)") assert_equal(p.delaunay(20000, 0.0).collect(&:to_s).join(";"), "(0,0;250,50;500,0);(500,0;250,50;500,100);(250,50;0,100;500,100);(0,0;0,100;250,50);(500,0;500,100;750,50);(500,0;750,50;1000,0);(1000,0;750,50;1000,100);(750,50;500,100;1000,100)") - assert_equal(p.delaunay([ RBA::DPoint::new(50, 50) ]).collect(&:to_s).join(";"), "(0,0;0,100;50,50);(50,50;0,100;1000,100);(0,0;50,50;1000,0);(1000,0;50,50;1000,100)") + assert_equal(p.delaunay([ RBA::DPoint::new(50, 50) ]).collect(&:to_s).join(";"), "(0,0;0,100;50,50);(50,50;0,100;1000,100);(1000,0;50,50;1000,100);(0,0;50,50;1000,0)") end diff --git a/testdata/ruby/dbSimplePolygonTest.rb b/testdata/ruby/dbSimplePolygonTest.rb index ee30323fe..e598228ae 100644 --- a/testdata/ruby/dbSimplePolygonTest.rb +++ b/testdata/ruby/dbSimplePolygonTest.rb @@ -426,15 +426,15 @@ class DBSimplePolygon_TestClass < TestBase assert_equal(p.delaunay(0.0, 0.5).to_s, "(0,0;0,100;250,0);(250,0;500,100;500,0);(250,0;0,100;500,100);(750,0;1000,100;1000,0);(500,0;500,100;750,0);(750,0;500,100;1000,100)") assert_equal(p.delaunay(20000, 0.0).to_s, "(0,0;250,50;500,0);(500,0;250,50;500,100);(250,50;0,100;500,100);(0,0;0,100;250,50);(500,0;500,100;750,50);(500,0;750,50;1000,0);(1000,0;750,50;1000,100);(750,50;500,100;1000,100)") - assert_equal(p.delaunay([ RBA::Point::new(50, 50) ]).to_s, "(0,0;0,100;50,50);(50,50;0,100;1000,100);(0,0;50,50;1000,0);(1000,0;50,50;1000,100)") + assert_equal(p.delaunay([ RBA::Point::new(50, 50) ]).to_s, "(0,0;0,100;50,50);(50,50;0,100;1000,100);(1000,0;50,50;1000,100);(0,0;50,50;1000,0)") - p = RBA::DSimplePolygon::new(RBA::DBox::new(0, 0, 1000, 100)) - assert_equal(p.delaunay.collect(&:to_s).join(";"), "(0,0;0,100;1000,100);(0,0;1000,100;1000,0)") + assert_equal(p.delaunay(20000, 0.0).each.collect(&:to_s).join(";"), "(0,0;250,50;500,0) props={};(500,0;250,50;500,100) props={};(250,50;0,100;500,100) props={};(0,0;0,100;250,50) props={};(500,0;500,100;750,50) props={};(500,0;750,50;1000,0) props={};(1000,0;750,50;1000,100) props={};(750,50;500,100;1000,100) props={}") + assert_equal(p.delaunay.each.collect(&:to_s).join(";"), "(0,0;0,100;1000,100) props={};(0,0;1000,100;1000,0) props={}") - assert_equal(p.delaunay(0.0, 0.5).collect(&:to_s).join(";"), "(0,0;0,100;250,0);(250,0;500,100;500,0);(250,0;0,100;500,100);(750,0;1000,100;1000,0);(500,0;500,100;750,0);(750,0;500,100;1000,100)") - assert_equal(p.delaunay(20000, 0.0).collect(&:to_s).join(";"), "(0,0;250,50;500,0);(500,0;250,50;500,100);(250,50;0,100;500,100);(0,0;0,100;250,50);(500,0;500,100;750,50);(500,0;750,50;1000,0);(1000,0;750,50;1000,100);(750,50;500,100;1000,100)") + assert_equal(p.delaunay(0.0, 0.5).each.collect(&:to_s).join(";"), "(0,0;0,100;250,0) props={};(250,0;500,100;500,0) props={};(250,0;0,100;500,100) props={};(750,0;1000,100;1000,0) props={};(500,0;500,100;750,0) props={};(750,0;500,100;1000,100) props={}") + assert_equal(p.delaunay(20000, 0.0).each.collect(&:to_s).join(";"), "(0,0;250,50;500,0) props={};(500,0;250,50;500,100) props={};(250,50;0,100;500,100) props={};(0,0;0,100;250,50) props={};(500,0;500,100;750,50) props={};(500,0;750,50;1000,0) props={};(1000,0;750,50;1000,100) props={};(750,50;500,100;1000,100) props={}") - assert_equal(p.delaunay([ RBA::DPoint::new(50, 50) ]).collect(&:to_s).join(";"), "(0,0;0,100;50,50);(50,50;0,100;1000,100);(0,0;50,50;1000,0);(1000,0;50,50;1000,100)") + assert_equal(p.delaunay([ RBA::DPoint::new(50, 50) ]).each.collect(&:to_s).join(";"), "(0,0;0,100;50,50) props={};(50,50;0,100;1000,100) props={};(1000,0;50,50;1000,100) props={};(0,0;50,50;1000,0) props={}") end From fc25590dd724cac2822cee220d05116b8a646ca8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 4 May 2025 17:15:47 +0200 Subject: [PATCH 196/392] Include a simplification step in the net extraction --- src/pex/pex/pexRExtractorTech.cc | 6 ++++++ src/pex/pex/pexRExtractorTech.h | 16 +++++++++++++--- src/pex/pex/pexRNetExtractor.cc | 4 ++++ src/pex/unit_tests/pexRNetExtractorTests.cc | 14 ++++++++++++++ 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/pex/pex/pexRExtractorTech.cc b/src/pex/pex/pexRExtractorTech.cc index 65e1a6604..38cc77554 100644 --- a/src/pex/pex/pexRExtractorTech.cc +++ b/src/pex/pex/pexRExtractorTech.cc @@ -24,6 +24,12 @@ #include "pexRExtractorTech.h" namespace pex +{ + +RExtractorTech::RExtractorTech () + : skip_simplify (false) { // .. nothing yet .. } + +} diff --git a/src/pex/pex/pexRExtractorTech.h b/src/pex/pex/pexRExtractorTech.h index b2840d020..c833e666a 100644 --- a/src/pex/pex/pexRExtractorTech.h +++ b/src/pex/pex/pexRExtractorTech.h @@ -36,7 +36,7 @@ namespace pex * Note that the layers are generic IDs. These are usigned ints specifying * a layer. */ -class RExtractorTechVia +class PEX_PUBLIC RExtractorTechVia { public: RExtractorTechVia () @@ -82,7 +82,7 @@ public: * Note that the layers are generic IDs. These are usigned ints specifying * a layer. */ -class RExtractorTechConductor +class PEX_PUBLIC RExtractorTechConductor { public: /** @@ -147,9 +147,14 @@ public: /** * @brief Specifies the extraction parameters */ -class RExtractorTech +class PEX_PUBLIC RExtractorTech { public: + /** + * @brief Constructor + */ + RExtractorTech (); + /** * @brief A list of via definitions */ @@ -159,6 +164,11 @@ public: * @brief A list of conductor definitions */ std::list conductors; + + /** + * @brief A flag indicating to skip the simplify step after extraction + */ + bool skip_simplify; }; } diff --git a/src/pex/pex/pexRNetExtractor.cc b/src/pex/pex/pexRNetExtractor.cc index 3ce59e3ba..4233a4571 100644 --- a/src/pex/pex/pexRNetExtractor.cc +++ b/src/pex/pex/pexRNetExtractor.cc @@ -101,6 +101,10 @@ RNetExtractor::extract (const RExtractorTech &tech, extract_conductor (*cond, g->second, vp, vp_offset, pp, pp_offset, viap, rnetwork); } + + if (! tech.skip_simplify) { + rnetwork.simplify (); + } } static double diff --git a/src/pex/unit_tests/pexRNetExtractorTests.cc b/src/pex/unit_tests/pexRNetExtractorTests.cc index fac83d200..00c8143ef 100644 --- a/src/pex/unit_tests/pexRNetExtractorTests.cc +++ b/src/pex/unit_tests/pexRNetExtractorTests.cc @@ -170,6 +170,7 @@ TEST(netex_2layer) pex::RNetwork network; pex::RExtractorTech tech; + tech.skip_simplify = true; pex::RExtractorTechVia via1; via1.bottom_conductor = l1; @@ -229,4 +230,17 @@ TEST(netex_2layer) "R $7(0.1,0.1;0.7,0.7) V1(0.4,-5.6;0.4,-5.6) 1.875\n" "R $1(0.3,-5.7;0.5,-5.5) V1(0.4,-5.6;0.4,-5.6) 0" ); + + tech.skip_simplify = false; + + rex.extract (tech, geo, vertex_ports, polygon_ports, network); + + EXPECT_EQ (network.to_string (true), + "R $2(9.3,-5.9;9.9,-5.3) P0(12.9,-5.9;13.5,-5.3) 2.25\n" + "R $8(10,-3.5;10,-2.7) P1(12.9,-3.4;13.5,-2.8) 1\n" + "R $2(9.3,-5.9;9.9,-5.3) V1(0.3,-5.7;0.5,-5.5) 55.75\n" + "R $2(9.3,-5.9;9.9,-5.3) $8(10,-3.5;10,-2.7) 13.2813\n" + "R $8(10,-3.5;10,-2.7) V0(5.2,0.4;5.2,0.4) 28.7812\n" + "R V0(5.2,0.4;5.2,0.4) V1(0.3,-5.7;0.5,-5.5) 17.375" + ); } From 77aa729b0668905d089dd74b725a456e9d492a02 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 4 May 2025 19:45:30 +0200 Subject: [PATCH 197/392] GSI binding of RNetExtractor, introducing layers for nodes --- src/pex/pex/gsiDeclRExtractor.cc | 375 +--------------- src/pex/pex/gsiDeclRNetExtractor.cc | 393 +++++++++++++++++ src/pex/pex/gsiDeclRNetwork.cc | 412 ++++++++++++++++++ src/pex/pex/pex.pro | 2 + src/pex/pex/pexRExtractorTech.h | 4 +- src/pex/pex/pexRNetExtractor.cc | 37 +- src/pex/pex/pexRNetExtractor.h | 2 - src/pex/pex/pexRNetwork.cc | 23 +- src/pex/pex/pexRNetwork.h | 15 +- src/pex/pex/pexSquareCountingRExtractor.cc | 2 +- src/pex/pex/pexTriangulationRExtractor.cc | 8 +- src/pex/unit_tests/pexRExtractorTests.cc | 97 ++++- src/pex/unit_tests/pexRNetExtractorTests.cc | 63 +-- .../pexSquareCountingRExtractorTests.cc | 4 +- 14 files changed, 962 insertions(+), 475 deletions(-) create mode 100644 src/pex/pex/gsiDeclRNetExtractor.cc create mode 100644 src/pex/pex/gsiDeclRNetwork.cc diff --git a/src/pex/pex/gsiDeclRExtractor.cc b/src/pex/pex/gsiDeclRExtractor.cc index 8aadb05d3..a92fa42ec 100644 --- a/src/pex/pex/gsiDeclRExtractor.cc +++ b/src/pex/pex/gsiDeclRExtractor.cc @@ -30,379 +30,6 @@ namespace gsi { -class RNode -{ -public: - ~RNode () { } - - pex::RNode::node_type type () const { return checked_pointer ()->type; } - db::DBox location () const { return checked_pointer ()->location; } - unsigned int port_index () const { return checked_pointer ()->port_index; } - std::string to_string (bool with_coords = false) const { return checked_pointer ()->to_string (with_coords); } - - size_t obj_id () const - { - return size_t (mp_ptr); - } - - static RNode *make_node_object (const pex::RNode *node) - { - return new RNode (node); - } - - const pex::RNode *checked_pointer () const - { - if (! mp_graph.get ()) { - throw tl::Exception (tl::to_string (tr ("Network graph has been destroyed - RNode object no longer is valid"))); - } - return mp_ptr; - } - - pex::RNode *checked_pointer () - { - if (! mp_graph.get ()) { - throw tl::Exception (tl::to_string (tr ("Network graph has been destroyed - RNode object no longer is valid"))); - } - return const_cast (mp_ptr); - } - -private: - tl::weak_ptr mp_graph; - const pex::RNode *mp_ptr; - - RNode (const pex::RNode *node) - : mp_graph (node->graph ()), - mp_ptr (node) - { - // .. nothing yet .. - } -}; - -class RElement -{ -public: - ~RElement () { } - - double conductance () const { return checked_pointer ()->conductance; } - double resistance () const { return checked_pointer ()->resistance (); } - - RNode *a () const { return RNode::make_node_object (checked_pointer ()->a ()); } - RNode *b () const { return RNode::make_node_object (checked_pointer ()->b ()); } - - std::string to_string (bool with_coords = false) const { return checked_pointer ()->to_string (with_coords); } - - size_t obj_id () const - { - return size_t (mp_ptr); - } - - static RElement *make_element_object (const pex::RElement *element) - { - return new RElement (element); - } - - const pex::RElement *checked_pointer () const - { - if (! mp_graph.get ()) { - throw tl::Exception (tl::to_string (tr ("Network graph has been destroyed - RElement object no longer is valid"))); - } - return mp_ptr; - } - - pex::RElement *checked_pointer () - { - if (! mp_graph.get ()) { - throw tl::Exception (tl::to_string (tr ("Network graph has been destroyed - RElement object no longer is valid"))); - } - return const_cast (mp_ptr); - } - -private: - tl::weak_ptr mp_graph; - const pex::RElement *mp_ptr; - - RElement (const pex::RElement *node) - : mp_graph (node->graph ()), - mp_ptr (node) - { - // .. nothing yet .. - } -}; - -class RElementIterator -{ -public: - typedef std::list::const_iterator basic_iter; - typedef std::forward_iterator_tag iterator_category; - typedef RElement *value_type; - typedef RElement *reference; - typedef void pointer; - typedef void difference_type; - - RElementIterator (basic_iter it) - : m_it (it) - { } - - bool operator== (const RElementIterator &it) const { return m_it == it.m_it; } - void operator++ () { ++m_it; } - - RElement *operator* () const - { - return RElement::make_element_object (*m_it); - } - -private: - basic_iter m_it; -}; - -static RElementIterator begin_node_elements (RNode *node) -{ - return RElementIterator (node->checked_pointer ()->elements ().begin ()); -} - -static RElementIterator end_network_elements (RNode *node) -{ - return RElementIterator (node->checked_pointer ()->elements ().end ()); -} - -gsi::Enum decl_NodeType ("pex", "RNodeType", - gsi::enum_const ("Internal", pex::RNode::Internal, - "@brief Specifies an internal node in a R network\n" - "Internal nodes are generated during the R extraction process. " - "The port index of such a node is an arbitrary index." - ) + - gsi::enum_const ("VertexPort", pex::RNode::VertexPort, - "@brief Specifies a vertex port node in a R network\n" - "Vertex port nodes are generated for vertex ports in \\RExtractor#extract, see 'vertex_ports' argument. " - "The port index of such a node refers to the position in that list." - ) + - gsi::enum_const ("PolygonPort", pex::RNode::PolygonPort, - "@brief Specifies a polygon port node in a R network\n" - "Polygon port nodes are generated for polygon ports in \\RExtractor#extract, see 'polygon_ports' argument. " - "The port index of such a node refers to the position in that list." - ), - "@brief This class represents the node type for RNode.\n" - "\n" - "This class has been introduced in version 0.30.1" -); - -Class decl_RNode ("pex", "RNode", - gsi::method ("object_id", &RNode::obj_id, - "@brief Returns an ID representing the actual object\n" - "For every call, a new instance of this object is created, while multiple " - "ones may represent the same internal object. The 'object_id' is a ID that " - "indicates the internal object. Same object_id means same node." - ) + - gsi::method ("to_s", &RNode::to_string, gsi::arg ("with_coords", false), - "@brief Returns a string representation of this object\n" - "Nodes are printed with coordinates with 'with_coords' is true." - ) + - gsi::iterator_ext ("each_element", gsi::return_new_object (), &begin_node_elements, &end_network_elements, - "@brief Iterates the \\RElement objects attached to the node\n" - ) + - gsi::method ("type", &RNode::type, - "@brief Gets the type attribute of the node\n" - ) + - gsi::method ("location", &RNode::location, - "@brief Gets the location attribute of the node\n" - "The location defined the original position of the node" - ) + - gsi::method ("port_index", &RNode::port_index, - "@brief Gets the port index of the node\n" - "The port index associates a node with a original port definition." - ), - "@brief Represents a node in a R network graph\n" - "See \\RNetwork for a description of this object\n" - "\n" - "This class has been introduced in version 0.30.1" -); - -// Inject the RNode::node_type declarations into RNode -gsi::ClassExt inject_NodeType_in_RNode (decl_NodeType.defs ()); - -Class decl_RElement ("pex", "RElement", - gsi::method ("object_id", &RElement::obj_id, - "@brief Returns an ID representing the actual object\n" - "For every call, a new instance of this object is created, while multiple " - "ones may represent the same internal object. The 'object_id' is a ID that " - "indicates the internal object. Same object_id means same element." - ) + - gsi::method ("to_s", &RElement::to_string, gsi::arg ("with_coords", false), - "@brief Returns a string representation of this object\n" - "Nodes are printed with coordinates with 'with_coords' is true." - ) + - gsi::method ("resistance", &RElement::resistance, - "@brief Gets the resistance value of the object\n" - ) + - gsi::factory ("a", &RElement::a, - "@brief Gets the first node the element connects\n" - ) + - gsi::factory ("b", &RElement::b, - "@brief Gets the second node the element connects\n" - ), - "@brief Represents an edge (also called element) in a R network graph\n" - "See \\RNetwork for a description of this object" - "\n" - "This class has been introduced in version 0.30.1" -); - -static RNode *create_node (pex::RNetwork *network, pex::RNode::node_type type, unsigned int port_index) -{ - return RNode::make_node_object (network->create_node (type, port_index)); -} - -static RElement *create_element (pex::RNetwork *network, double r, RNode *a, RNode *b) -{ - double s = fabs (r) < 1e-10 ? pex::RElement::short_value () : 1.0 / r; - return RElement::make_element_object (network->create_element (s, a->checked_pointer (), b->checked_pointer ())); -} - -static void remove_element (pex::RNetwork *network, RElement *element) -{ - network->remove_element (element->checked_pointer ()); -} - -static void remove_node (pex::RNetwork *network, RNode *node) -{ - network->remove_node (node->checked_pointer ()); -} - -class NetworkElementIterator -{ -public: - typedef pex::RNetwork::element_iterator basic_iter; - typedef std::forward_iterator_tag iterator_category; - typedef RElement *value_type; - typedef RElement *reference; - typedef void pointer; - typedef void difference_type; - - NetworkElementIterator (basic_iter it) - : m_it (it) - { } - - bool operator== (const NetworkElementIterator &it) const { return m_it == it.m_it; } - void operator++ () { ++m_it; } - - RElement *operator* () const - { - return RElement::make_element_object (m_it.operator-> ()); - } - -private: - basic_iter m_it; -}; - -static NetworkElementIterator begin_network_elements (pex::RNetwork *network) -{ - return NetworkElementIterator (network->begin_elements ()); -} - -static NetworkElementIterator end_network_elements (pex::RNetwork *network) -{ - return NetworkElementIterator (network->end_elements ()); -} - -class NetworkNodeIterator -{ -public: - typedef pex::RNetwork::node_iterator basic_iter; - typedef std::forward_iterator_tag iterator_category; - typedef RNode *value_type; - typedef RNode *reference; - typedef void pointer; - typedef void difference_type; - - NetworkNodeIterator (basic_iter it) - : m_it (it) - { } - - bool operator== (const NetworkNodeIterator &it) const { return m_it == it.m_it; } - void operator++ () { ++m_it; } - - RNode *operator* () const - { - return RNode::make_node_object (m_it.operator-> ()); - } - -private: - basic_iter m_it; -}; - -static NetworkNodeIterator begin_network_nodes (pex::RNetwork *network) -{ - return NetworkNodeIterator (network->begin_nodes ()); -} - -static NetworkNodeIterator end_network_nodes (pex::RNetwork *network) -{ - return NetworkNodeIterator (network->end_nodes ()); -} - -Class decl_RNetwork ("pex", "RNetwork", - gsi::factory_ext ("create_node", &create_node, gsi::arg ("type"), gsi::arg ("port_index"), - "@brief Creates a new node with the given type and index'.\n" - "@return A reference to the new nbode object." - ) + - gsi::factory_ext ("create_element", &create_element, gsi::arg ("resistance"), gsi::arg ("a"), gsi::arg ("b"), - "@brief Creates a new element between the nodes given by 'a' abd 'b'.\n" - "If a resistor already exists between the two nodes, both resistors are combined into one.\n" - "@return A reference to the new resistor object." - ) + - gsi::method_ext ("remove_element", &remove_element, gsi::arg ("element"), - "@brief Removes the given element\n" - "If removing the element renders an internal node orphan (i.e. without elements), this " - "node is removed too." - ) + - gsi::method_ext ("remove_node", &remove_node, gsi::arg ("node"), - "@brief Removes the given node\n" - "Only internal nodes can be removed. Removing a node will also remove the " - "elements attached to this node." - ) + - gsi::method ("clear", &pex::RNetwork::clear, - "@brief Clears the network\n" - ) + - gsi::method ("simplify", &pex::RNetwork::simplify, - "@brief Simplifies the network\n" - "\n" - "This will:\n" - "@ul\n" - "@li Join serial resistors if connected by an internal node @/li\n" - "@li Remove shorts and join the nodes, if one of them is\n" - " an internal node. The non-internal node will persist @/li\n" - "@li Remove \"dangling\" resistors if the dangling node is\n" - " an internal one @/li\n" - "@/ul\n" - ) + - gsi::iterator_ext ("each_element", gsi::return_new_object (), &begin_network_elements, &end_network_elements, - "@brief Iterates the \\RElement objects inside the network\n" - ) + - gsi::iterator_ext ("each_node", gsi::return_new_object (), &begin_network_nodes, &end_network_nodes, - "@brief Iterates the \\RNode objects inside the network\n" - ) + - gsi::method ("num_nodes", &pex::RNetwork::num_nodes, - "@brief Gets the total number of nodes in the network\n" - ) + - gsi::method ("num_internal_nodes", &pex::RNetwork::num_internal_nodes, - "@brief Gets the number of internal nodes in the network\n" - ) + - gsi::method ("num_elements", &pex::RNetwork::num_elements, - "@brief Gets the number of elements in the network\n" - ) + - gsi::method ("to_s", &pex::RNetwork::to_string, gsi::arg ("with_coords", false), - "@brief Returns a string representation of the network\n" - "Nodes are printed with coordinates with 'with_coords' is true." - ), - "@brief Represents a network of resistors\n" - "\n" - "The network is basically a graph with nodes and edges (the resistors). " - "The resistors are called 'elements' and are represented by \\RElement objects. " - "The nodes are represented by \\RNode objects. " - "The network is created by \\RExtractor#extract, which turns a polygon into a resistor network.\n" - "\n" - "This class has been introduced in version 0.30.1\n" -); - static pex::RExtractor *new_sqc_rextractor (double dbu, bool skip_simplify) { auto res = new pex::SquareCountingRExtractor (dbu); @@ -490,7 +117,7 @@ Class decl_RExtractor ("pex", "RExtractor", "Use \\tesselation_extractor and \\square_counting_extractor to create an actual extractor object.\n" "To use the extractor, call the \\extract method on a given polygon with ports that define the network attachment points.\n" "\n" - "This class has been introduced in version 0.30.1\n" + "This class has been introduced in version 0.30.2\n" ); } diff --git a/src/pex/pex/gsiDeclRNetExtractor.cc b/src/pex/pex/gsiDeclRNetExtractor.cc new file mode 100644 index 000000000..5a65f0367 --- /dev/null +++ b/src/pex/pex/gsiDeclRNetExtractor.cc @@ -0,0 +1,393 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "pexRExtractorTech.h" +#include "pexRNetExtractor.h" +#include "pexRNetwork.h" +#include "gsiEnums.h" + +namespace gsi +{ + +static unsigned int via_get_bottom_conductor (const pex::RExtractorTechVia *via) +{ + return via->bottom_conductor; +} + +static void via_set_bottom_conductor (pex::RExtractorTechVia *via, unsigned int l) +{ + via->bottom_conductor = l; +} + +static unsigned int via_get_cut_layer (const pex::RExtractorTechVia *via) +{ + return via->cut_layer; +} + +static void via_set_cut_layer (pex::RExtractorTechVia *via, unsigned int l) +{ + via->cut_layer = l; +} + +static unsigned int via_get_top_conductor (const pex::RExtractorTechVia *via) +{ + return via->top_conductor; +} + +static void via_set_top_conductor (pex::RExtractorTechVia *via, unsigned int l) +{ + via->top_conductor = l; +} + +static double via_get_resistance (const pex::RExtractorTechVia *via) +{ + return via->resistance; +} + +static void via_set_resistance (pex::RExtractorTechVia *via, double r) +{ + via->resistance = r; +} + +static double via_get_merge_distance (const pex::RExtractorTechVia *via) +{ + return via->merge_distance; +} + +static void via_set_merge_distance (pex::RExtractorTechVia *via, double dist) +{ + via->merge_distance = dist; +} + +Class decl_RExtractorTechVia ("pex", "RExtractorTechVia", + gsi::method_ext ("merge_distance", &via_get_merge_distance, + "@brief Gets the merge distance\n" + "If this value is not zero, it specifies the distance below (or equal) which " + "vias are merged into bigger blocks. This is an optimization to reduce the " + "complexity of the via extraction. The value is given in micrometers." + ) + + gsi::method_ext ("merge_distance=", &via_set_merge_distance, gsi::arg ("d"), + "@brief Sets the merge distance\n" + "See \\merge_distance for a description of this attribute." + ) + + gsi::method_ext ("resistance", &via_get_resistance, + "@brief Gets the area resistance value of the vias\n" + "This value specifies the via resistance in Ohm * square micrometers. " + "The actual resistance is obtained by dividing this value by the via area." + ) + + gsi::method_ext ("resistance=", &via_set_resistance, gsi::arg ("d"), + "@brief Sets the via area resistance value\n" + "See \\resistance for a description of this attribute." + ) + + gsi::method_ext ("bottom_conductor", &via_get_bottom_conductor, + "@brief Gets the bottom conductor layer index\n" + "The layer index is a generic identifier for the layer. It is the value used as key in the " + "geometry and port arguments of \\RNetExtractor#extract." + ) + + gsi::method_ext ("bottom_conductor=", &via_set_bottom_conductor, gsi::arg ("l"), + "@brief Sets the via bottom conductor layer index\n" + "See \\bottom_conductor for a description of this attribute." + ) + + gsi::method_ext ("cut_layer", &via_get_cut_layer, + "@brief Gets the cut layer index\n" + "The layer index is a generic identifier for the layer. It is the value used as key in the " + "geometry and port arguments of \\RNetExtractor#extract. " + "The cut layer is the layer where the via exists." + ) + + gsi::method_ext ("cut_layer=", &via_set_cut_layer, gsi::arg ("l"), + "@brief Sets the cut layer index\n" + "See \\cut_layer for a description of this attribute." + ) + + gsi::method_ext ("top_conductor", &via_get_top_conductor, + "@brief Gets the top conductor layer index\n" + "The layer index is a generic identifier for the layer. It is the value used as key in the " + "geometry and port arguments of \\RNetExtractor#extract." + ) + + gsi::method_ext ("top_conductor=", &via_set_top_conductor, gsi::arg ("l"), + "@brief Sets the via top conductor layer index\n" + "See \\top_conductor for a description of this attribute." + ), + "@brief Describes a via for the network extraction.\n" + "This class is used to describe a via type in the context of " + "the \\RExtractorTech#extract method.\n" + "\n" + "This class has been introduced in version 0.30.2." +); + +static pex::RExtractorTechConductor::Algorithm cond_get_algorithm (const pex::RExtractorTechConductor *cond) +{ + return cond->algorithm; +} + +static void cond_set_algorithm (pex::RExtractorTechConductor *cond, pex::RExtractorTechConductor::Algorithm a) +{ + cond->algorithm = a; +} + +static unsigned int cond_get_layer (const pex::RExtractorTechConductor *cond) +{ + return cond->layer; +} + +static void cond_set_layer (pex::RExtractorTechConductor *cond, unsigned int l) +{ + cond->layer = l; +} + +static double cond_get_resistance (const pex::RExtractorTechConductor *cond) +{ + return cond->resistance; +} + +static void cond_set_resistance (pex::RExtractorTechConductor *cond, double r) +{ + cond->resistance = r; +} + +static double cond_get_triangulation_min_b (const pex::RExtractorTechConductor *cond) +{ + return cond->triangulation_min_b; +} + +static void cond_set_triangulation_min_b (pex::RExtractorTechConductor *cond, double min_b) +{ + cond->triangulation_min_b = min_b; +} + +static double cond_get_triangulation_max_area (const pex::RExtractorTechConductor *cond) +{ + return cond->triangulation_max_area; +} + +static void cond_set_triangulation_max_area (pex::RExtractorTechConductor *cond, double max_area) +{ + cond->triangulation_max_area = max_area; +} + +Class decl_RExtractorTechConductor ("pex", "RExtractorTechConductor", + gsi::method_ext ("algorithm", &cond_get_algorithm, + "@brief Gets the algorithm to use\n" + "Specifies the algorithm to use. The default algorithm is 'SquareCounting'." + ) + + gsi::method_ext ("algorithm=", &cond_set_algorithm, gsi::arg ("d"), + "@brief Sets the algorithm to use\n" + "See \\algorithm for a description of this attribute." + ) + + gsi::method_ext ("resistance", &cond_get_resistance, + "@brief Gets the sheet resistance value of the conductor layer\n" + "This value specifies the cond resistance in Ohm per square. " + "The actual resistance is obtained by multiplying this value with the number of squares." + ) + + gsi::method_ext ("resistance=", &cond_set_resistance, gsi::arg ("r"), + "@brief Sets the sheet resistance value of the conductor layer\n" + "See \\resistance for a description of this attribute." + ) + + gsi::method_ext ("layer", &cond_get_layer, + "@brief Gets the layer index\n" + "The layer index is a generic identifier for the layer. It is the value used as key in the " + "geometry and port arguments of \\RNetExtractor#extract. " + "This attribute specifies the layer the conductor is on." + ) + + gsi::method_ext ("layer=", &cond_set_layer, gsi::arg ("l"), + "@brief Sets the layer index\n" + "See \\layer for a description of this attribute." + ) + + gsi::method_ext ("triangulation_min_b", &cond_get_triangulation_min_b, + "@brief Gets the triangulation 'min_b' parameter\n" + "This parameter is used for the 'Tesselation' algorithm and specifies the shortest edge to circle radius ratio of " + "the Delaunay triangulation. " + ) + + gsi::method_ext ("triangulation_min_b=", &cond_set_triangulation_min_b, gsi::arg ("min_b"), + "@brief Sets the triangulation 'min_b' parameter\n" + "See \\triangulation_min_b for a description of this attribute." + ) + + gsi::method_ext ("triangulation_max_area", &cond_get_triangulation_max_area, + "@brief Gets the triangulation 'max_area' parameter\n" + "This parameter is used for the 'Tesselation' algorithm and specifies the maximum area of " + "the triangles in square micrometers." + ) + + gsi::method_ext ("triangulation_max_area=", &cond_set_triangulation_max_area, gsi::arg ("max_area"), + "@brief Sets the triangulation 'max_area' parameter\n" + "See \\triangulation_max_area for a description of this attribute." + ), + "@brief Describes a conductor layer for the network extraction.\n" + "This class is used to describe a conductor layer in the context of " + "the \\RExtractorTech#extract method.\n" + "\n" + "This class has been introduced in version 0.30.2." +); + +gsi::Enum decl_RExtractorTechConductor_Algorithm ("pex", "Algorithm", + gsi::enum_const ("SquareCounting", pex::RExtractorTechConductor::SquareCounting, + "@brief Specifies the square counting algorithm for \\RExtractorTechConductor#algorithm.\n" + "See \\RExtractor#square_counting_extractor for more details." + ) + + gsi::enum_const ("Tesselation", pex::RExtractorTechConductor::Tesselation, + "@brief Specifies the square counting algorithm for \\RExtractorTechConductor#algorithm.\n" + "See \\RExtractor#tesselation_extractor for more details." + ), + "@brief This enum represents the extraction algorithm for \\RExtractorTechConductor.\n" + "\n" + "This enum has been introduced in version 0.30.2." +); + +gsi::ClassExt inject_RExtractorTechConductor_in_parent (decl_RExtractorTechConductor_Algorithm.defs ()); + +static bool tech_get_skip_simplify (const pex::RExtractorTech *tech) +{ + return tech->skip_simplify; +} + +static void tech_set_skip_simplify (pex::RExtractorTech *tech, bool f) +{ + tech->skip_simplify = f; +} + +static auto tech_begin_vias (const pex::RExtractorTech *tech) +{ + return tech->vias.begin (); +} + +static auto tech_end_vias (const pex::RExtractorTech *tech) +{ + return tech->vias.end (); +} + +static void tech_clear_vias (pex::RExtractorTech *tech) +{ + tech->vias.clear (); +} + +static void tech_add_via (pex::RExtractorTech *tech, const pex::RExtractorTechVia &via) +{ + tech->vias.push_back (via); +} + +static auto tech_begin_conductors (const pex::RExtractorTech *tech) +{ + return tech->conductors.begin (); +} + +static auto tech_end_conductors (const pex::RExtractorTech *tech) +{ + return tech->conductors.end (); +} + +static void tech_clear_conductors (pex::RExtractorTech *tech) +{ + tech->conductors.clear (); +} + +static void tech_add_conductor (pex::RExtractorTech *tech, const pex::RExtractorTechConductor &conductor) +{ + tech->conductors.push_back (conductor); +} + +Class decl_RExtractorTech ("pex", "RExtractorTech", + gsi::method_ext ("skip_simplify", &tech_get_skip_simplify, + "@brief Gets a value indicating whether to skip the simplify step\n" + "This values specifies to skip the simplify step of the network after the extraction has " + "been done. By default, the network is simplified - i.e. serial resistors are joined etc. " + "By setting this attribute to 'false', this step is skipped." + ) + + gsi::method_ext ("skip_simplify=", &tech_set_skip_simplify, gsi::arg ("f"), + "@brief Sets a value indicating whether to skip the simplify step\n" + "See \\skip_simplify for a description of this attribute." + ) + + gsi::method_ext ("each_via", &tech_begin_vias, &tech_end_vias, + "@brief Iterates the list of via definitions\n" + ) + + gsi::method_ext ("clear_vias", &tech_clear_vias, + "@brief Clears the list of via definitions\n" + ) + + gsi::method_ext ("add_via", &tech_add_via, gsi::arg ("via"), + "@brief Adds the given via definition to the list of vias\n" + ) + + gsi::method_ext ("each_conductor", &tech_begin_conductors, &tech_end_conductors, + "@brief Iterates the list of conductor definitions\n" + ) + + gsi::method_ext ("clear_conductors", &tech_clear_conductors, + "@brief Clears the list of conductor definitions\n" + ) + + gsi::method_ext ("add_conductor", &tech_add_conductor, gsi::arg ("conductor"), + "@brief Adds the given conductor definition to the list of conductors\n" + ), + "@brief Specifies the tech stack for the R extraction.\n" + "The tech stack is a collection of via and conductor definitions and some other attributes. " + "It is used for the \\RNetExtractor#extract method.\n" + "\n" + "This enum has been introduced in version 0.30.2." +); + +static pex::RNetExtractor *new_net_rextractor (double dbu) +{ + return new pex::RNetExtractor (dbu); +} + +static pex::RNetwork *rex_extract (pex::RNetExtractor *rex, + const pex::RExtractorTech *tech, + const std::map *geo, + const std::map > *vertex_ports, + const std::map > *polygon_ports) +{ + std::unique_ptr network (new pex::RNetwork ()); + std::map empty_geo; + std::map > empty_vertex_ports; + std::map > empty_polygon_ports; + rex->extract (*tech, geo ? *geo : empty_geo, vertex_ports ? *vertex_ports : empty_vertex_ports, polygon_ports ? *polygon_ports : empty_polygon_ports, *network); + return network.release (); +} + +Class decl_RNetExtractor ("pex", "RNetExtractor", + gsi::constructor ("new", &new_net_rextractor, gsi::arg ("dbu"), + "@brief Creates a network R extractor\n" + "\n" + "@param dbu The database unit of the polygons the extractor will work on\n" + "@param skip_simplify If true, the final step to simplify the netlist will be skipped. This feature is for testing mainly.\n" + "@return A new \\RNetExtractor object that implements the net extractor\n" + ) + + gsi::factory_ext ("extract", &rex_extract, gsi::arg ("tech_stack"), gsi::arg ("geo"), gsi::arg ("vertex_ports"), gsi::arg ("polygon_ports"), + "@brief Runs the extraction on the given multi-layer geometry\n" + "See the description of the class for more details." + ), + "@brief The network R extractor class\n" + "\n" + "This class provides the algorithms for extracting a R network from a multi-layer arrangement of conductors and vias.\n" + "The main feature is the \\extract method. It takes a multi-layer geometry, a tech stack and a number of port definitions\n" + "and returns a R network. The nodes in that network are annotated, so the corresponding port can be deduced from a node of\n" + "VertexPort or PolygonPort type.\n" + "\n" + "Layers are given by layer indexes - those are generic IDs. Every layer has to be given a unique ID, which must be used throughout " + "the different specifications (geometry, vias, conductors, ports).\n" + "\n" + "Two kind of ports are provided: point-like vertex ports and polygon ports. Polygons for polygon ports should be convex and sit inside " + "the geometry they mark. Ports become nodes in the network. Beside ports, the network can have internal nodes. Nodes are annotated with " + "a type (vertex, polygon, internal) and an index and layer. The layer is the layer ID, the index specifies the position of the " + "corresponding port in the 'vertex_ports' or 'polygon_ports' list of the \\extract call.\n" + "\n" + "This class has been introduced in version 0.30.2\n" +); + +} + diff --git a/src/pex/pex/gsiDeclRNetwork.cc b/src/pex/pex/gsiDeclRNetwork.cc new file mode 100644 index 000000000..fb1c2a4f7 --- /dev/null +++ b/src/pex/pex/gsiDeclRNetwork.cc @@ -0,0 +1,412 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2025 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 "pexRExtractor.h" +#include "pexSquareCountingRExtractor.h" +#include "pexTriangulationRExtractor.h" +#include "gsiEnums.h" + +namespace gsi +{ + +class RNode +{ +public: + ~RNode () { } + + pex::RNode::node_type type () const { return checked_pointer ()->type; } + db::DBox location () const { return checked_pointer ()->location; } + unsigned int port_index () const { return checked_pointer ()->port_index; } + unsigned int layer () const { return checked_pointer ()->layer; } + std::string to_string (bool with_coords = false) const { return checked_pointer ()->to_string (with_coords); } + + size_t obj_id () const + { + return size_t (mp_ptr); + } + + static RNode *make_node_object (const pex::RNode *node) + { + return new RNode (node); + } + + const pex::RNode *checked_pointer () const + { + if (! mp_graph.get ()) { + throw tl::Exception (tl::to_string (tr ("Network graph has been destroyed - RNode object no longer is valid"))); + } + return mp_ptr; + } + + pex::RNode *checked_pointer () + { + if (! mp_graph.get ()) { + throw tl::Exception (tl::to_string (tr ("Network graph has been destroyed - RNode object no longer is valid"))); + } + return const_cast (mp_ptr); + } + +private: + tl::weak_ptr mp_graph; + const pex::RNode *mp_ptr; + + RNode (const pex::RNode *node) + : mp_graph (node->graph ()), + mp_ptr (node) + { + // .. nothing yet .. + } +}; + +class RElement +{ +public: + ~RElement () { } + + double conductance () const { return checked_pointer ()->conductance; } + double resistance () const { return checked_pointer ()->resistance (); } + + RNode *a () const { return RNode::make_node_object (checked_pointer ()->a ()); } + RNode *b () const { return RNode::make_node_object (checked_pointer ()->b ()); } + + std::string to_string (bool with_coords = false) const { return checked_pointer ()->to_string (with_coords); } + + size_t obj_id () const + { + return size_t (mp_ptr); + } + + static RElement *make_element_object (const pex::RElement *element) + { + return new RElement (element); + } + + const pex::RElement *checked_pointer () const + { + if (! mp_graph.get ()) { + throw tl::Exception (tl::to_string (tr ("Network graph has been destroyed - RElement object no longer is valid"))); + } + return mp_ptr; + } + + pex::RElement *checked_pointer () + { + if (! mp_graph.get ()) { + throw tl::Exception (tl::to_string (tr ("Network graph has been destroyed - RElement object no longer is valid"))); + } + return const_cast (mp_ptr); + } + +private: + tl::weak_ptr mp_graph; + const pex::RElement *mp_ptr; + + RElement (const pex::RElement *node) + : mp_graph (node->graph ()), + mp_ptr (node) + { + // .. nothing yet .. + } +}; + +class RElementIterator +{ +public: + typedef std::list::const_iterator basic_iter; + typedef std::forward_iterator_tag iterator_category; + typedef RElement *value_type; + typedef RElement *reference; + typedef void pointer; + typedef void difference_type; + + RElementIterator (basic_iter it) + : m_it (it) + { } + + bool operator== (const RElementIterator &it) const { return m_it == it.m_it; } + void operator++ () { ++m_it; } + + RElement *operator* () const + { + return RElement::make_element_object (*m_it); + } + +private: + basic_iter m_it; +}; + +static RElementIterator begin_node_elements (RNode *node) +{ + return RElementIterator (node->checked_pointer ()->elements ().begin ()); +} + +static RElementIterator end_network_elements (RNode *node) +{ + return RElementIterator (node->checked_pointer ()->elements ().end ()); +} + +gsi::Enum decl_NodeType ("pex", "RNodeType", + gsi::enum_const ("Internal", pex::RNode::Internal, + "@brief Specifies an internal node in a R network\n" + "Internal nodes are generated during the R extraction process. " + "The port index of such a node is an arbitrary index." + ) + + gsi::enum_const ("VertexPort", pex::RNode::VertexPort, + "@brief Specifies a vertex port node in a R network\n" + "Vertex port nodes are generated for vertex ports in \\RExtractor#extract, see 'vertex_ports' argument. " + "The port index of such a node refers to the position in that list." + ) + + gsi::enum_const ("PolygonPort", pex::RNode::PolygonPort, + "@brief Specifies a polygon port node in a R network\n" + "Polygon port nodes are generated for polygon ports in \\RExtractor#extract, see 'polygon_ports' argument. " + "The port index of such a node refers to the position in that list." + ), + "@brief This class represents the node type for RNode.\n" + "\n" + "This class has been introduced in version 0.30.2" +); + +Class decl_RNode ("pex", "RNode", + gsi::method ("object_id", &RNode::obj_id, + "@brief Returns an ID representing the actual object\n" + "For every call, a new instance of this object is created, while multiple " + "ones may represent the same internal object. The 'object_id' is a ID that " + "indicates the internal object. Same object_id means same node." + ) + + gsi::method ("to_s", &RNode::to_string, gsi::arg ("with_coords", false), + "@brief Returns a string representation of this object\n" + "Nodes are printed with coordinates with 'with_coords' is true." + ) + + gsi::iterator_ext ("each_element", gsi::return_new_object (), &begin_node_elements, &end_network_elements, + "@brief Iterates the \\RElement objects attached to the node\n" + ) + + gsi::method ("type", &RNode::type, + "@brief Gets the type attribute of the node\n" + ) + + gsi::method ("location", &RNode::location, + "@brief Gets the location attribute of the node\n" + "The location defined the original position of the node" + ) + + gsi::method ("port_index", &RNode::port_index, + "@brief Gets the port index of the node\n" + "The port index associates a node with a original port definition." + ) + + gsi::method ("layer", &RNode::layer, + "@brief Gets the Layer ID of the node\n" + "The port index associates a node with a original port definition layer-wise." + ), + "@brief Represents a node in a R network graph\n" + "See \\RNetwork for a description of this object\n" + "\n" + "This class has been introduced in version 0.30.2" +); + +// Inject the RNode::node_type declarations into RNode +gsi::ClassExt inject_NodeType_in_RNode (decl_NodeType.defs ()); + +Class decl_RElement ("pex", "RElement", + gsi::method ("object_id", &RElement::obj_id, + "@brief Returns an ID representing the actual object\n" + "For every call, a new instance of this object is created, while multiple " + "ones may represent the same internal object. The 'object_id' is a ID that " + "indicates the internal object. Same object_id means same element." + ) + + gsi::method ("to_s", &RElement::to_string, gsi::arg ("with_coords", false), + "@brief Returns a string representation of this object\n" + "Nodes are printed with coordinates with 'with_coords' is true." + ) + + gsi::method ("resistance", &RElement::resistance, + "@brief Gets the resistance value of the object\n" + ) + + gsi::factory ("a", &RElement::a, + "@brief Gets the first node the element connects\n" + ) + + gsi::factory ("b", &RElement::b, + "@brief Gets the second node the element connects\n" + ), + "@brief Represents an edge (also called element) in a R network graph\n" + "See \\RNetwork for a description of this object" + "\n" + "This class has been introduced in version 0.30.2" +); + +static RNode *create_node (pex::RNetwork *network, pex::RNode::node_type type, unsigned int port_index, unsigned int layer) +{ + return RNode::make_node_object (network->create_node (type, port_index, layer)); +} + +static RElement *create_element (pex::RNetwork *network, double r, RNode *a, RNode *b) +{ + double s = fabs (r) < 1e-10 ? pex::RElement::short_value () : 1.0 / r; + return RElement::make_element_object (network->create_element (s, a->checked_pointer (), b->checked_pointer ())); +} + +static void remove_element (pex::RNetwork *network, RElement *element) +{ + network->remove_element (element->checked_pointer ()); +} + +static void remove_node (pex::RNetwork *network, RNode *node) +{ + network->remove_node (node->checked_pointer ()); +} + +class NetworkElementIterator +{ +public: + typedef pex::RNetwork::element_iterator basic_iter; + typedef std::forward_iterator_tag iterator_category; + typedef RElement *value_type; + typedef RElement *reference; + typedef void pointer; + typedef void difference_type; + + NetworkElementIterator (basic_iter it) + : m_it (it) + { } + + bool operator== (const NetworkElementIterator &it) const { return m_it == it.m_it; } + void operator++ () { ++m_it; } + + RElement *operator* () const + { + return RElement::make_element_object (m_it.operator-> ()); + } + +private: + basic_iter m_it; +}; + +static NetworkElementIterator begin_network_elements (pex::RNetwork *network) +{ + return NetworkElementIterator (network->begin_elements ()); +} + +static NetworkElementIterator end_network_elements (pex::RNetwork *network) +{ + return NetworkElementIterator (network->end_elements ()); +} + +class NetworkNodeIterator +{ +public: + typedef pex::RNetwork::node_iterator basic_iter; + typedef std::forward_iterator_tag iterator_category; + typedef RNode *value_type; + typedef RNode *reference; + typedef void pointer; + typedef void difference_type; + + NetworkNodeIterator (basic_iter it) + : m_it (it) + { } + + bool operator== (const NetworkNodeIterator &it) const { return m_it == it.m_it; } + void operator++ () { ++m_it; } + + RNode *operator* () const + { + return RNode::make_node_object (m_it.operator-> ()); + } + +private: + basic_iter m_it; +}; + +static NetworkNodeIterator begin_network_nodes (pex::RNetwork *network) +{ + return NetworkNodeIterator (network->begin_nodes ()); +} + +static NetworkNodeIterator end_network_nodes (pex::RNetwork *network) +{ + return NetworkNodeIterator (network->end_nodes ()); +} + +Class decl_RNetwork ("pex", "RNetwork", + gsi::factory_ext ("create_node", &create_node, gsi::arg ("type"), gsi::arg ("port_index"), gsi::arg ("layer", (unsigned int) 0), + "@brief Creates a new node with the given type and index'.\n" + "@return A reference to the new nbode object." + ) + + gsi::factory_ext ("create_element", &create_element, gsi::arg ("resistance"), gsi::arg ("a"), gsi::arg ("b"), + "@brief Creates a new element between the nodes given by 'a' abd 'b'.\n" + "If a resistor already exists between the two nodes, both resistors are combined into one.\n" + "@return A reference to the new resistor object." + ) + + gsi::method_ext ("remove_element", &remove_element, gsi::arg ("element"), + "@brief Removes the given element\n" + "If removing the element renders an internal node orphan (i.e. without elements), this " + "node is removed too." + ) + + gsi::method_ext ("remove_node", &remove_node, gsi::arg ("node"), + "@brief Removes the given node\n" + "Only internal nodes can be removed. Removing a node will also remove the " + "elements attached to this node." + ) + + gsi::method ("clear", &pex::RNetwork::clear, + "@brief Clears the network\n" + ) + + gsi::method ("simplify", &pex::RNetwork::simplify, + "@brief Simplifies the network\n" + "\n" + "This will:\n" + "@ul\n" + "@li Join serial resistors if connected by an internal node @/li\n" + "@li Remove shorts and join the nodes, if one of them is\n" + " an internal node. The non-internal node will persist @/li\n" + "@li Remove \"dangling\" resistors if the dangling node is\n" + " an internal one @/li\n" + "@/ul\n" + ) + + gsi::iterator_ext ("each_element", gsi::return_new_object (), &begin_network_elements, &end_network_elements, + "@brief Iterates the \\RElement objects inside the network\n" + ) + + gsi::iterator_ext ("each_node", gsi::return_new_object (), &begin_network_nodes, &end_network_nodes, + "@brief Iterates the \\RNode objects inside the network\n" + ) + + gsi::method ("num_nodes", &pex::RNetwork::num_nodes, + "@brief Gets the total number of nodes in the network\n" + ) + + gsi::method ("num_internal_nodes", &pex::RNetwork::num_internal_nodes, + "@brief Gets the number of internal nodes in the network\n" + ) + + gsi::method ("num_elements", &pex::RNetwork::num_elements, + "@brief Gets the number of elements in the network\n" + ) + + gsi::method ("to_s", &pex::RNetwork::to_string, gsi::arg ("with_coords", false), + "@brief Returns a string representation of the network\n" + "Nodes are printed with coordinates with 'with_coords' is true." + ), + "@brief Represents a network of resistors\n" + "\n" + "The network is basically a graph with nodes and edges (the resistors). " + "The resistors are called 'elements' and are represented by \\RElement objects. " + "The nodes are represented by \\RNode objects. " + "The network is created by \\RExtractor#extract, which turns a polygon into a resistor network.\n" + "\n" + "This class has been introduced in version 0.30.2\n" +); + +} + diff --git a/src/pex/pex/pex.pro b/src/pex/pex/pex.pro index 6d69e018c..ab52eaf29 100644 --- a/src/pex/pex/pex.pro +++ b/src/pex/pex/pex.pro @@ -7,6 +7,8 @@ include($$PWD/../../lib.pri) DEFINES += MAKE_PEX_LIBRARY SOURCES = \ + gsiDeclRNetExtractor.cc \ + gsiDeclRNetwork.cc \ pexForceLink.cc \ pexRExtractor.cc \ gsiDeclRExtractor.cc \ diff --git a/src/pex/pex/pexRExtractorTech.h b/src/pex/pex/pexRExtractorTech.h index c833e666a..6062910ae 100644 --- a/src/pex/pex/pexRExtractorTech.h +++ b/src/pex/pex/pexRExtractorTech.h @@ -97,10 +97,10 @@ public: SquareCounting = 0, /** - * @brief The triangulation algorithm + * @brief The tesselation algorithm * This algorithm is suitable to "large" sheets, specifically substrate. */ - Triangulation = 1 + Tesselation = 1 }; /** diff --git a/src/pex/pex/pexRNetExtractor.cc b/src/pex/pex/pexRNetExtractor.cc index 4233a4571..e93d6e745 100644 --- a/src/pex/pex/pexRNetExtractor.cc +++ b/src/pex/pex/pexRNetExtractor.cc @@ -69,19 +69,6 @@ RNetExtractor::extract (const RExtractorTech &tech, continue; } - // Compute the offsets for the port indexes - // The port indexes are assigned incrementally, so the first index of a port - // for layer n is: - // size(ports for layer 0) + ... + size(ports for layer n-1) - // (size is 0 for empty port list) - unsigned int vp_offset = 0, pp_offset = 0; - for (auto p = vertex_ports.begin (); p != vertex_ports.end () && p->first < g->first; ++p) { - vp_offset += p->second.size (); - } - for (auto p = polygon_ports.begin (); p != polygon_ports.end () && p->first < g->first; ++p) { - pp_offset += p->second.size (); - } - // fetch the port list for vertex ports auto ivp = vertex_ports.find (g->first); static std::vector empty_vertex_ports; @@ -98,7 +85,7 @@ RNetExtractor::extract (const RExtractorTech &tech, const std::vector &viap = iviap == via_ports.end () ? empty_via_ports : iviap->second; // extract the conductor polygon and integrate the results into the target network - extract_conductor (*cond, g->second, vp, vp_offset, pp, pp_offset, viap, rnetwork); + extract_conductor (*cond, g->second, vp, pp, viap, rnetwork); } @@ -178,8 +165,8 @@ RNetExtractor::create_via_port (const pex::RExtractorTechVia &tech, std::map > &vias, RNetwork &rnetwork) { - RNode *a = rnetwork.create_node (RNode::Internal, port_index++); - RNode *b = rnetwork.create_node (RNode::Internal, port_index++); + RNode *a = rnetwork.create_node (RNode::Internal, port_index++, tech.bottom_conductor); + RNode *b = rnetwork.create_node (RNode::Internal, port_index++, tech.top_conductor); db::CplxTrans to_um (m_dbu); db::Box box = poly.box (); @@ -266,9 +253,7 @@ class ExtractingReceiver public: ExtractingReceiver (const RExtractorTechConductor *cond, const std::vector *vertex_ports, - unsigned int vertex_port_index_offset, const std::vector *polygon_ports, - unsigned int polygon_port_index_offset, const std::vector *via_ports, double dbu, RNetwork *rnetwork) @@ -277,8 +262,6 @@ public: mp_polygon_ports (polygon_ports), mp_via_ports (via_ports), m_next_internal_port_index (0), - m_vertex_port_index_offset (vertex_port_index_offset), - m_polygon_port_index_offset (polygon_port_index_offset), m_dbu (dbu), mp_rnetwork (rnetwork) { @@ -316,8 +299,6 @@ private: const std::vector *mp_via_ports; std::map m_id_to_node; unsigned int m_next_internal_port_index; - unsigned int m_vertex_port_index_offset; - unsigned int m_polygon_port_index_offset; double m_dbu; RNetwork *mp_rnetwork; @@ -355,7 +336,7 @@ private: rex.extract (poly, local_vertex_ports, local_polygon_ports, local_network); } break; - case RExtractorTechConductor::Triangulation: + case RExtractorTechConductor::Tesselation: { pex::TriangulationRExtractor rex (m_dbu); rex.extract (poly, local_vertex_ports, local_polygon_ports, local_network); @@ -380,7 +361,7 @@ private: if (local->type == RNode::Internal) { // for internal nodes always create a node in the target network - global = mp_rnetwork->create_node (local->type, ++m_next_internal_port_index); + global = mp_rnetwork->create_node (local->type, ++m_next_internal_port_index, mp_cond->layer); global->location = local->location; } else if (local->type == RNode::VertexPort) { @@ -395,7 +376,7 @@ private: global = i2n->second; } else { if (type_from_id (id) == 0) { // vertex port - global = mp_rnetwork->create_node (RNode::VertexPort, index_from_id (id) + m_vertex_port_index_offset); + global = mp_rnetwork->create_node (RNode::VertexPort, index_from_id (id), mp_cond->layer); global->location = local->location; } else if (type_from_id (id) == 1) { // via port global = (*mp_via_ports) [index_from_id (id)].node; @@ -414,7 +395,7 @@ private: if (i2n != m_id_to_node.end ()) { global = i2n->second; } else { - global = mp_rnetwork->create_node (RNode::PolygonPort, index_from_id (id) + m_polygon_port_index_offset); + global = mp_rnetwork->create_node (RNode::PolygonPort, index_from_id (id), mp_cond->layer); global->location = local->location; m_id_to_node.insert (std::make_pair (id, global)); } @@ -455,9 +436,7 @@ void RNetExtractor::extract_conductor (const RExtractorTechConductor &cond, const db::Region ®ion, const std::vector &vertex_ports, - unsigned int vertex_ports_index_offset, const std::vector &polygon_ports, - unsigned int polygon_ports_index_offset, const std::vector &via_ports, RNetwork &rnetwork) { @@ -490,7 +469,7 @@ RNetExtractor::extract_conductor (const RExtractorTechConductor &cond, scanner.insert2 (&box_heap.back (), make_id (i - polygon_ports.begin (), 2)); } - ExtractingReceiver rec (&cond, &vertex_ports, vertex_ports_index_offset, &polygon_ports, polygon_ports_index_offset, &via_ports, m_dbu, &rnetwork); + ExtractingReceiver rec (&cond, &vertex_ports, &polygon_ports, &via_ports, m_dbu, &rnetwork); scanner.process (rec, 0, db::box_convert (), db::box_convert ()); } diff --git a/src/pex/pex/pexRNetExtractor.h b/src/pex/pex/pexRNetExtractor.h index 20ea0ab77..befd52384 100644 --- a/src/pex/pex/pexRNetExtractor.h +++ b/src/pex/pex/pexRNetExtractor.h @@ -84,9 +84,7 @@ protected: void extract_conductor (const RExtractorTechConductor &cond, const db::Region ®ion, const std::vector &vertex_ports, - unsigned int vertex_ports_index_offset, const std::vector &polygon_ports, - unsigned int polygon_ports_index_offset, const std::vector &via_ports, RNetwork &rnetwork); diff --git a/src/pex/pex/pexRNetwork.cc b/src/pex/pex/pexRNetwork.cc index 435590d81..f64c60360 100644 --- a/src/pex/pex/pexRNetwork.cc +++ b/src/pex/pex/pexRNetwork.cc @@ -33,19 +33,24 @@ std::string RNode::to_string (bool with_coords) const { std::string res; - switch (type) { default: - res += "$" + tl::to_string (port_index); + res += "$"; break; case VertexPort: - res += "V" + tl::to_string (port_index); + res += "V"; break; case PolygonPort: - res += "P" + tl::to_string (port_index); + res += "P"; break; } + res += tl::to_string (port_index); + if (layer > 0) { + res += "."; + res += tl::to_string (layer); + } + if (with_coords) { res += location.to_string (); } @@ -116,27 +121,27 @@ RNetwork::clear () } RNode * -RNetwork::create_node (RNode::node_type type, unsigned int port_index) +RNetwork::create_node (RNode::node_type type, unsigned int port_index, unsigned int layer) { if (type != RNode::Internal) { - auto i = m_nodes_by_type.find (std::make_pair (type, port_index)); + auto i = m_nodes_by_type.find (std::make_pair (type, std::make_pair (port_index, layer))); if (i != m_nodes_by_type.end ()) { return i->second; } else { - RNode *new_node = new RNode (this, type, db::DBox (), port_index); + RNode *new_node = new RNode (this, type, db::DBox (), port_index, layer); m_nodes.push_back (new_node); - m_nodes_by_type.insert (std::make_pair (std::make_pair (type, port_index), new_node)); + m_nodes_by_type.insert (std::make_pair (std::make_pair (type, std::make_pair (port_index, layer)), new_node)); return new_node; } } else { - RNode *new_node = new RNode (this, type, db::DBox (), port_index); + RNode *new_node = new RNode (this, type, db::DBox (), port_index, layer); m_nodes.push_back (new_node); return new_node; diff --git a/src/pex/pex/pexRNetwork.h b/src/pex/pex/pexRNetwork.h index 45f4ec36a..b9438b0d9 100644 --- a/src/pex/pex/pexRNetwork.h +++ b/src/pex/pex/pexRNetwork.h @@ -80,6 +80,13 @@ public: */ unsigned int port_index; + /** + * @brief An index locating the node in a layer + * + * For internal nodes, the layer is 0. + */ + unsigned int layer; + /** * @brief Gets the R elements connected to this node */ @@ -106,8 +113,8 @@ protected: friend class RElement; friend class tl::list_impl; - RNode (RNetwork *network, node_type _type, const db::DBox &_location, unsigned int _port_index) - : type (_type), location (_location), port_index (_port_index), mp_network (network) + RNode (RNetwork *network, node_type _type, const db::DBox &_location, unsigned int _port_index, unsigned int _layer) + : type (_type), location (_location), port_index (_port_index), layer (_layer), mp_network (network) { } ~RNode () { } @@ -249,7 +256,7 @@ public: * or port index already. This avoids creating duplicates * for the same port. */ - RNode *create_node (RNode::node_type type, unsigned int port_index); + RNode *create_node (RNode::node_type type, unsigned int port_index, unsigned int layer); /** * @brief Creates a new element between the given nodes @@ -363,7 +370,7 @@ private: node_list m_nodes; element_list m_elements; std::map, RElement *> m_elements_by_nodes; - std::map, RNode *> m_nodes_by_type; + std::map >, RNode *> m_nodes_by_type; RNetwork (const RNetwork &); RNetwork &operator= (const RNetwork &); diff --git a/src/pex/pex/pexSquareCountingRExtractor.cc b/src/pex/pex/pexSquareCountingRExtractor.cc index 1abf32d95..cd956a703 100644 --- a/src/pex/pex/pexSquareCountingRExtractor.cc +++ b/src/pex/pex/pexSquareCountingRExtractor.cc @@ -287,7 +287,7 @@ SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector for (auto p = ports.begin (); p != ports.end (); ++p) { auto n4p = nodes_for_ports.find (p->first); if (n4p == nodes_for_ports.end ()) { - pex::RNode *node = rnetwork.create_node (p->first.type, p->first.port_index); + pex::RNode *node = rnetwork.create_node (p->first.type, p->first.port_index, 0); node->location = to_um * p->first.location; n4p = nodes_for_ports.insert (std::make_pair (p->first, node)).first; } diff --git a/src/pex/pex/pexTriangulationRExtractor.cc b/src/pex/pex/pexTriangulationRExtractor.cc index 18f2e70da..6a867ef43 100644 --- a/src/pex/pex/pexTriangulationRExtractor.cc +++ b/src/pex/pex/pexTriangulationRExtractor.cc @@ -148,7 +148,7 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector< if (pn != pport_nodes.end ()) { n = pn->second; } else { - n = rnetwork.create_node (pex::RNode::PolygonPort, port_index); + n = rnetwork.create_node (pex::RNode::PolygonPort, port_index, 0); pport_nodes.insert (std::make_pair (port_index, n)); n->location = trans * polygon_ports [port_index].box (); } @@ -158,7 +158,7 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector< for (auto pi = vertex->ids ().begin (); pi != vertex->ids ().end (); ++pi) { size_t port_index = size_t (*pi); if (port_index < vertex_ports.size ()) { - RNode *nn = rnetwork.create_node (pex::RNode::VertexPort, port_index); + RNode *nn = rnetwork.create_node (pex::RNode::VertexPort, port_index, 0); nn->location = db::DBox (*vertex, *vertex); if (n) { // in case of multiple vertexes on the same spot, short them @@ -172,7 +172,7 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector< } else { - n = rnetwork.create_node (pex::RNode::Internal, internal_node_id++); + n = rnetwork.create_node (pex::RNode::Internal, internal_node_id++, 0); n->location = db::DBox (*vertex, *vertex); } @@ -204,7 +204,7 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector< if (ip != pport_nodes.end ()) { // create a new vertex port and short it to the polygon port - auto n = rnetwork.create_node (pex::RNode::VertexPort, iv); + auto n = rnetwork.create_node (pex::RNode::VertexPort, iv, 0); n->location = db::DBox (trans * vp, trans * vp); rnetwork.create_element (pex::RElement::short_value (), n, ip->second); diff --git a/src/pex/unit_tests/pexRExtractorTests.cc b/src/pex/unit_tests/pexRExtractorTests.cc index 6e04a6f6a..35ab0169a 100644 --- a/src/pex/unit_tests/pexRExtractorTests.cc +++ b/src/pex/unit_tests/pexRExtractorTests.cc @@ -30,15 +30,74 @@ TEST(network_basic) pex::RNetwork rn; EXPECT_EQ (rn.to_string (), ""); - pex::RNode *n1 = rn.create_node (pex::RNode::Internal, 1); - pex::RNode *n2 = rn.create_node (pex::RNode::Internal, 2); + pex::RNode *n1 = rn.create_node (pex::RNode::Internal, 1, 0); + pex::RNode *n2 = rn.create_node (pex::RNode::Internal, 1, 1); + EXPECT_EQ (n1 != n2, true); + pex::RNode *n2_dup = rn.create_node (pex::RNode::Internal, 1, 1); + EXPECT_EQ (n2 != n2_dup, true); + + /* pex::RElement *e12 = */ rn.create_element (0.5, n1, n2); + + EXPECT_EQ (rn.to_string (), + "R $1 $1.1 2" + ); +} + +TEST(network_basic_vertex_nodes) +{ + pex::RNetwork rn; + EXPECT_EQ (rn.to_string (), ""); + + pex::RNode *n1 = rn.create_node (pex::RNode::VertexPort, 1, 0); + pex::RNode *n2 = rn.create_node (pex::RNode::VertexPort, 1, 1); + EXPECT_EQ (n1 != n2, true); + pex::RNode *n2_dup = rn.create_node (pex::RNode::VertexPort, 1, 1); + EXPECT_EQ (n2 == n2_dup, true); + pex::RNode *n2_wrong_type = rn.create_node (pex::RNode::PolygonPort, 1, 1); + EXPECT_EQ (n2 != n2_wrong_type, true); + + /* pex::RElement *e12 = */ rn.create_element (0.5, n1, n2); + + EXPECT_EQ (rn.to_string (), + "R V1 V1.1 2" + ); +} + +TEST(network_basic_polygon_nodes) +{ + pex::RNetwork rn; + EXPECT_EQ (rn.to_string (), ""); + + pex::RNode *n1 = rn.create_node (pex::RNode::PolygonPort, 1, 0); + pex::RNode *n2 = rn.create_node (pex::RNode::PolygonPort, 1, 1); + EXPECT_EQ (n1 != n2, true); + pex::RNode *n2_dup = rn.create_node (pex::RNode::PolygonPort, 1, 1); + EXPECT_EQ (n2 == n2_dup, true); + pex::RNode *n2_wrong_type = rn.create_node (pex::RNode::VertexPort, 1, 1); + EXPECT_EQ (n2 != n2_wrong_type, true); + + /* pex::RElement *e12 = */ rn.create_element (0.5, n1, n2); + + EXPECT_EQ (rn.to_string (), + "R P1 P1.1 2" + ); +} + +TEST(network_basic_elements) +{ + pex::RNetwork rn; + EXPECT_EQ (rn.to_string (), ""); + + pex::RNode *n1 = rn.create_node (pex::RNode::Internal, 1, 0); + pex::RNode *n2 = rn.create_node (pex::RNode::Internal, 2, 0); + /* pex::RElement *e12 = */ rn.create_element (0.5, n1, n2); EXPECT_EQ (rn.to_string (), "R $1 $2 2" ); - pex::RNode *n3 = rn.create_node (pex::RNode::Internal, 3); + pex::RNode *n3 = rn.create_node (pex::RNode::Internal, 3, 0); /* pex::RElement *e13 = */ rn.create_element (0.25, n1, n3); pex::RElement *e23 = rn.create_element (1.0, n2, n3); @@ -89,9 +148,9 @@ TEST(network_simplify1) pex::RNetwork rn; EXPECT_EQ (rn.to_string (), ""); - pex::RNode *n1 = rn.create_node (pex::RNode::VertexPort, 1); - pex::RNode *n2 = rn.create_node (pex::RNode::Internal, 2); - pex::RNode *n3 = rn.create_node (pex::RNode::VertexPort, 3); + pex::RNode *n1 = rn.create_node (pex::RNode::VertexPort, 1, 0); + pex::RNode *n2 = rn.create_node (pex::RNode::Internal, 2, 0); + pex::RNode *n3 = rn.create_node (pex::RNode::VertexPort, 3, 0); rn.create_element (1, n1, n2); rn.create_element (pex::RElement::short_value (), n2, n3); @@ -115,11 +174,11 @@ TEST(network_simplify2) pex::RNetwork rn; EXPECT_EQ (rn.to_string (), ""); - pex::RNode *n1 = rn.create_node (pex::RNode::VertexPort, 1); - pex::RNode *n2 = rn.create_node (pex::RNode::Internal, 2); - pex::RNode *n3 = rn.create_node (pex::RNode::Internal, 3); - pex::RNode *n4 = rn.create_node (pex::RNode::VertexPort, 4); - pex::RNode *n5 = rn.create_node (pex::RNode::VertexPort, 5); + pex::RNode *n1 = rn.create_node (pex::RNode::VertexPort, 1, 0); + pex::RNode *n2 = rn.create_node (pex::RNode::Internal, 2, 0); + pex::RNode *n3 = rn.create_node (pex::RNode::Internal, 3, 0); + pex::RNode *n4 = rn.create_node (pex::RNode::VertexPort, 4, 0); + pex::RNode *n5 = rn.create_node (pex::RNode::VertexPort, 5, 0); rn.create_element (1, n1, n2); rn.create_element (pex::RElement::short_value (), n2, n3); @@ -147,10 +206,10 @@ TEST(network_simplify3) pex::RNetwork rn; EXPECT_EQ (rn.to_string (), ""); - pex::RNode *n1 = rn.create_node (pex::RNode::VertexPort, 1); - pex::RNode *n2 = rn.create_node (pex::RNode::Internal, 2); - pex::RNode *n3 = rn.create_node (pex::RNode::Internal, 3); - pex::RNode *n4 = rn.create_node (pex::RNode::VertexPort, 4); + pex::RNode *n1 = rn.create_node (pex::RNode::VertexPort, 1, 0); + pex::RNode *n2 = rn.create_node (pex::RNode::Internal, 2, 0); + pex::RNode *n3 = rn.create_node (pex::RNode::Internal, 3, 0); + pex::RNode *n4 = rn.create_node (pex::RNode::VertexPort, 4, 0); rn.create_element (1, n1, n2); rn.create_element (pex::RElement::short_value (), n2, n3); @@ -174,10 +233,10 @@ TEST(network_simplify4) pex::RNetwork rn; EXPECT_EQ (rn.to_string (), ""); - pex::RNode *n1 = rn.create_node (pex::RNode::VertexPort, 1); - pex::RNode *n2 = rn.create_node (pex::RNode::Internal, 2); - pex::RNode *n3 = rn.create_node (pex::RNode::Internal, 3); - pex::RNode *n4 = rn.create_node (pex::RNode::VertexPort, 4); + pex::RNode *n1 = rn.create_node (pex::RNode::VertexPort, 1, 0); + pex::RNode *n2 = rn.create_node (pex::RNode::Internal, 2, 0); + pex::RNode *n3 = rn.create_node (pex::RNode::Internal, 3, 0); + pex::RNode *n4 = rn.create_node (pex::RNode::VertexPort, 4, 0); rn.create_element (1, n1, n4); rn.create_element (1, n2, n1); diff --git a/src/pex/unit_tests/pexRNetExtractorTests.cc b/src/pex/unit_tests/pexRNetExtractorTests.cc index 00c8143ef..9b4d0bd49 100644 --- a/src/pex/unit_tests/pexRNetExtractorTests.cc +++ b/src/pex/unit_tests/pexRNetExtractorTests.cc @@ -79,10 +79,10 @@ TEST(netex_viagen1) EXPECT_EQ (via_ports [l3].size (), size_t (4)); EXPECT_EQ (network.to_string (true), - "R $0(1.7,0.1;1.9,0.3) $1(1.7,0.1;1.9,0.3) 50\n" - "R $2(0.4,0.5;0.6,0.7) $3(0.4,0.5;0.6,0.7) 50\n" - "R $4(0.8,0.5;1,0.7) $5(0.8,0.5;1,0.7) 50\n" - "R $6(2.9,0.5;3.1,0.7) $7(2.9,0.5;3.1,0.7) 50" + "R $0.1(1.7,0.1;1.9,0.3) $1.2(1.7,0.1;1.9,0.3) 50\n" + "R $2.1(0.4,0.5;0.6,0.7) $3.2(0.4,0.5;0.6,0.7) 50\n" + "R $4.1(0.8,0.5;1,0.7) $5.2(0.8,0.5;1,0.7) 50\n" + "R $6.1(2.9,0.5;3.1,0.7) $7.2(2.9,0.5;3.1,0.7) 50" ); } @@ -129,12 +129,12 @@ TEST(netex_viagen2) EXPECT_EQ (via_ports [l3].size (), size_t (6)); EXPECT_EQ (network.to_string (true), - "R $0(4.6,2.8;4.8,3) $1(4.6,2.8;4.8,3) 50\n" - "R $2(2.5,3.7;2.7,3.9) $3(2.5,3.7;2.7,3.9) 50\n" - "R $4(3,3.7;3.2,3.9) $5(3,3.7;3.2,3.9) 50\n" - "R $6(2.2,1.2;3.4,3.4) $7(2.2,1.2;3.4,3.4) 2.77778\n" - "R $8(0.4,0.4;2.2,4.2) $9(0.4,0.4;2.2,4.2) 1\n" - "R $10(0.6,4.9;1.2,5.1) $11(0.6,4.9;1.2,5.1) 25" + "R $0.1(4.6,2.8;4.8,3) $1.2(4.6,2.8;4.8,3) 50\n" + "R $2.1(2.5,3.7;2.7,3.9) $3.2(2.5,3.7;2.7,3.9) 50\n" + "R $4.1(3,3.7;3.2,3.9) $5.2(3,3.7;3.2,3.9) 50\n" + "R $6.1(2.2,1.2;3.4,3.4) $7.2(2.2,1.2;3.4,3.4) 2.77778\n" + "R $8.1(0.4,0.4;2.2,4.2) $9.2(0.4,0.4;2.2,4.2) 1\n" + "R $10.1(0.6,4.9;1.2,5.1) $11.2(0.6,4.9;1.2,5.1) 25" ); } @@ -162,6 +162,11 @@ TEST(netex_2layer) unsigned int l3p = ly.get_layer (db::LayerProperties (3, 1)); unsigned int l3v = ly.get_layer (db::LayerProperties (3, 2)); + // That is coincidence, but it needs to be that way for the strings to match + EXPECT_EQ (l1, 1u); + EXPECT_EQ (l2, 0u); + EXPECT_EQ (l3, 2u); + std::map geo; geo.insert (std::make_pair (l1, db::Region (db::RecursiveShapeIterator (ly, ly.cell (tc.second), l1)))); geo.insert (std::make_pair (l2, db::Region (db::RecursiveShapeIterator (ly, ly.cell (tc.second), l2)))); @@ -216,19 +221,19 @@ TEST(netex_2layer) rex.extract (tech, geo, vertex_ports, polygon_ports, network); EXPECT_EQ (network.to_string (true), - "R $0(0.3,-5.7;0.5,-5.5) $1(0.3,-5.7;0.5,-5.5) 50\n" - "R $2(9.3,-5.9;9.9,-5.3) $3(9.3,-5.9;9.9,-5.3) 12.5\n" - "R $4(9.3,0.1;9.9,0.3) $5(9.3,0.1;9.9,0.3) 25\n" - "R $6(0.1,0.1;0.7,0.7) $7(0.1,0.1;0.7,0.7) 12.5\n" - "R $0(0.3,-5.7;0.5,-5.5) $2(9.3,-5.9;9.9,-5.3) 5.75\n" - "R $2(9.3,-5.9;9.9,-5.3) P0(12.9,-5.9;13.5,-5.3) 2.25\n" - "R $6(0.1,0.1;0.7,0.7) V0(5.2,0.4;5.2,0.4) 3\n" - "R $4(9.3,0.1;9.9,0.3) V0(5.2,0.4;5.2,0.4) 2.75\n" - "R $5(9.3,0.1;9.9,0.3) $8(10,-3.5;10,-2.7) 1.03125\n" - "R $3(9.3,-5.9;9.9,-5.3) $8(10,-3.5;10,-2.7) 0.78125\n" - "R $8(10,-3.5;10,-2.7) P1(12.9,-3.4;13.5,-2.8) 1\n" - "R $7(0.1,0.1;0.7,0.7) V1(0.4,-5.6;0.4,-5.6) 1.875\n" - "R $1(0.3,-5.7;0.5,-5.5) V1(0.4,-5.6;0.4,-5.6) 0" + "R $0.1(0.3,-5.7;0.5,-5.5) $1.2(0.3,-5.7;0.5,-5.5) 50\n" + "R $2.1(9.3,-5.9;9.9,-5.3) $3.2(9.3,-5.9;9.9,-5.3) 12.5\n" + "R $4.1(9.3,0.1;9.9,0.3) $5.2(9.3,0.1;9.9,0.3) 25\n" + "R $6.1(0.1,0.1;0.7,0.7) $7.2(0.1,0.1;0.7,0.7) 12.5\n" + "R $0.1(0.3,-5.7;0.5,-5.5) $2.1(9.3,-5.9;9.9,-5.3) 5.75\n" + "R $2.1(9.3,-5.9;9.9,-5.3) P0.1(12.9,-5.9;13.5,-5.3) 2.25\n" + "R $6.1(0.1,0.1;0.7,0.7) V0.1(5.2,0.4;5.2,0.4) 3\n" + "R $4.1(9.3,0.1;9.9,0.3) V0.1(5.2,0.4;5.2,0.4) 2.75\n" + "R $5.2(9.3,0.1;9.9,0.3) $8.2(10,-3.5;10,-2.7) 1.03125\n" + "R $3.2(9.3,-5.9;9.9,-5.3) $8.2(10,-3.5;10,-2.7) 0.78125\n" + "R $8.2(10,-3.5;10,-2.7) P0.2(12.9,-3.4;13.5,-2.8) 1\n" + "R $7.2(0.1,0.1;0.7,0.7) V0.2(0.4,-5.6;0.4,-5.6) 1.875\n" + "R $1.2(0.3,-5.7;0.5,-5.5) V0.2(0.4,-5.6;0.4,-5.6) 0" ); tech.skip_simplify = false; @@ -236,11 +241,11 @@ TEST(netex_2layer) rex.extract (tech, geo, vertex_ports, polygon_ports, network); EXPECT_EQ (network.to_string (true), - "R $2(9.3,-5.9;9.9,-5.3) P0(12.9,-5.9;13.5,-5.3) 2.25\n" - "R $8(10,-3.5;10,-2.7) P1(12.9,-3.4;13.5,-2.8) 1\n" - "R $2(9.3,-5.9;9.9,-5.3) V1(0.3,-5.7;0.5,-5.5) 55.75\n" - "R $2(9.3,-5.9;9.9,-5.3) $8(10,-3.5;10,-2.7) 13.2813\n" - "R $8(10,-3.5;10,-2.7) V0(5.2,0.4;5.2,0.4) 28.7812\n" - "R V0(5.2,0.4;5.2,0.4) V1(0.3,-5.7;0.5,-5.5) 17.375" + "R $2.1(9.3,-5.9;9.9,-5.3) P0.1(12.9,-5.9;13.5,-5.3) 2.25\n" + "R $8.2(10,-3.5;10,-2.7) P0.2(12.9,-3.4;13.5,-2.8) 1\n" + "R $2.1(9.3,-5.9;9.9,-5.3) V0.2(0.3,-5.7;0.5,-5.5) 55.75\n" + "R $2.1(9.3,-5.9;9.9,-5.3) $8.2(10,-3.5;10,-2.7) 13.2813\n" + "R $8.2(10,-3.5;10,-2.7) V0.1(5.2,0.4;5.2,0.4) 28.7812\n" + "R V0.1(5.2,0.4;5.2,0.4) V0.2(0.3,-5.7;0.5,-5.5) 17.375" ); } diff --git a/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc b/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc index 3da3d7b58..e3b6e057e 100644 --- a/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc +++ b/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc @@ -69,7 +69,7 @@ TEST(basic) std::vector > ports; for (auto pd = pds.begin (); pd != pds.end (); ++pd) { - ports.push_back (std::make_pair (*pd, rn.create_node (pd->type, pd->port_index))); + ports.push_back (std::make_pair (*pd, rn.create_node (pd->type, pd->port_index, 0))); } rex.do_extract (poly, ports, rn); @@ -90,7 +90,7 @@ TEST(basic) ports.clear (); for (auto pd = pds.begin (); pd != pds.end (); ++pd) { - ports.push_back (std::make_pair (*pd, rn.create_node (pd->type, pd->port_index))); + ports.push_back (std::make_pair (*pd, rn.create_node (pd->type, pd->port_index, 0))); ports.back ().first.location.transform (r90); } From 91005d5cb6896eb3862643f18b5d81ef3b1356e2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 4 May 2025 20:42:12 +0200 Subject: [PATCH 198/392] Tests for RBA+pex, bug fixes --- src/pex/pex/gsiDeclRNetExtractor.cc | 4 +- testdata/ruby/pexTests.rb | 133 +++++++++++++++++++++++++++- 2 files changed, 134 insertions(+), 3 deletions(-) diff --git a/src/pex/pex/gsiDeclRNetExtractor.cc b/src/pex/pex/gsiDeclRNetExtractor.cc index 5a65f0367..3fff2dad5 100644 --- a/src/pex/pex/gsiDeclRNetExtractor.cc +++ b/src/pex/pex/gsiDeclRNetExtractor.cc @@ -315,7 +315,7 @@ Class decl_RExtractorTech ("pex", "RExtractorTech", "@brief Sets a value indicating whether to skip the simplify step\n" "See \\skip_simplify for a description of this attribute." ) + - gsi::method_ext ("each_via", &tech_begin_vias, &tech_end_vias, + gsi::iterator_ext ("each_via", &tech_begin_vias, &tech_end_vias, "@brief Iterates the list of via definitions\n" ) + gsi::method_ext ("clear_vias", &tech_clear_vias, @@ -324,7 +324,7 @@ Class decl_RExtractorTech ("pex", "RExtractorTech", gsi::method_ext ("add_via", &tech_add_via, gsi::arg ("via"), "@brief Adds the given via definition to the list of vias\n" ) + - gsi::method_ext ("each_conductor", &tech_begin_conductors, &tech_end_conductors, + gsi::iterator_ext ("each_conductor", &tech_begin_conductors, &tech_end_conductors, "@brief Iterates the list of conductor definitions\n" ) + gsi::method_ext ("clear_conductors", &tech_clear_conductors, diff --git a/testdata/ruby/pexTests.rb b/testdata/ruby/pexTests.rb index c18de6e03..477f07ed3 100644 --- a/testdata/ruby/pexTests.rb +++ b/testdata/ruby/pexTests.rb @@ -34,6 +34,7 @@ class PEX_TestClass < TestBase a = rn.create_node(RBA::RNode::VertexPort, 1) b = rn.create_node(RBA::RNode::Internal, 2) c = rn.create_node(RBA::RNode::PolygonPort, 3) + d = rn.create_node(RBA::RNode::PolygonPort, 3, 17) assert_equal(a.type, RBA::RNode::VertexPort) assert_equal(a.port_index, 1) @@ -43,6 +44,7 @@ class PEX_TestClass < TestBase assert_equal(b.to_s, "$2") assert_equal(c.to_s, "P3") + assert_equal(d.to_s, "P3.17") rab = rn.create_element(1.0, a, b) assert_equal(rab.a.object_id, a.object_id) @@ -58,7 +60,7 @@ class PEX_TestClass < TestBase assert_equal(b.each_element.collect(&:to_s).sort.join(";"), "R $2 P3 1;R $2 V1 0.5") assert_equal(rn.each_element.collect(&:to_s).sort.join(";"), "R $2 P3 1;R $2 V1 0.5") - assert_equal(rn.each_node.collect(&:to_s).sort.join(";"), "$2;P3;V1") + assert_equal(rn.each_node.collect(&:to_s).sort.join(";"), "$2;P3;P3.17;V1") rn.simplify assert_equal(rn.to_s, "R P3 V1 1.5") @@ -121,6 +123,135 @@ class PEX_TestClass < TestBase end + def test_4_ExtractorTech + + l1 = 1 + l2 = 2 + l3 = 3 + + tech = RBA::RExtractorTech::new + + tech.skip_simplify = true + assert_equal(tech.skip_simplify, true) + tech.skip_simplify = false + assert_equal(tech.skip_simplify, false) + + via1 = RBA::RExtractorTechVia::new + via1.bottom_conductor = l1 + via1.cut_layer = l2 + via1.top_conductor = l3 + via1.resistance = 2.0 + via1.merge_distance = 0.2 + + assert_equal(via1.bottom_conductor, l1) + assert_equal(via1.cut_layer, l2) + assert_equal(via1.top_conductor, l3) + assert_equal(via1.resistance, 2.0) + assert_equal(via1.merge_distance.to_s, "0.2") + + tech.add_via(via1) + assert_equal(tech.each_via.collect { |v| v.cut_layer }, [ l2 ]) + + tech.clear_vias + assert_equal(tech.each_via.collect { |v| v.cut_layer }, []) + + tech.add_via(via1) + assert_equal(tech.each_via.collect { |v| v.cut_layer }, [ l2 ]) + + cond1 = RBA::RExtractorTechConductor::new + cond1.layer = l1 + cond1.resistance = 0.5 + + assert_equal(cond1.layer, l1) + assert_equal(cond1.resistance, 0.5) + + cond2 = RBA::RExtractorTechConductor::new + cond2.layer = l3 + cond2.resistance = 0.25 + + tech.add_conductor(cond2) + assert_equal(tech.each_conductor.collect { |c| c.layer }, [ l3 ]) + + tech.clear_conductors + assert_equal(tech.each_conductor.collect { |c| c.layer }, []) + + tech.add_conductor(cond1) + tech.add_conductor(cond2) + assert_equal(tech.each_conductor.collect { |c| c.layer }, [ l1, l3 ]) + + end + + # A complete, small example for a R network extraction + + def test_5_NetEx + + ly = RBA::Layout::new + ly.read(File.join($ut_testsrc, "testdata", "pex", "netex_test1.gds")) + + rex = RBA::RNetExtractor::new(ly.dbu) + + tc = ly.top_cell + + l1 = ly.layer(1, 0) + l1p = ly.layer(1, 1) + l1v = ly.layer(1, 2) + l2 = ly.layer(2, 0) + l3 = ly.layer(3, 0) + l3p = ly.layer(3, 1) + l3v = ly.layer(3, 2) + + # That is coincidence, but it needs to be that way for the strings to match + assert_equal(l1, 1) + assert_equal(l2, 0) + assert_equal(l3, 2) + + geo = {} + [ l1, l2, l3 ].each do |l| + geo[l] = RBA::Region::new(tc.begin_shapes_rec(l)) + end + + tech = RBA::RExtractorTech::new + + via1 = RBA::RExtractorTechVia::new + via1.bottom_conductor = l1 + via1.cut_layer = l2 + via1.top_conductor = l3 + via1.resistance = 2.0 + via1.merge_distance = 0.2 + + tech.add_via(via1) + + cond1 = RBA::RExtractorTechConductor::new + cond1.layer = l1 + cond1.resistance = 0.5 + + cond2 = RBA::RExtractorTechConductor::new + cond2.layer = l3 + cond2.resistance = 0.25 + + tech.add_conductor(cond1) + tech.add_conductor(cond2) + + polygon_ports = { } + polygon_ports[l1] = RBA::Region::new(tc.begin_shapes_rec(l1p)).each_merged.to_a + polygon_ports[l3] = RBA::Region::new(tc.begin_shapes_rec(l3p)).each_merged.to_a + + vertex_ports = { } + vertex_ports[l1] = RBA::Region::new(tc.begin_shapes_rec(l1v)).each_merged.collect { |p| p.bbox.center } + vertex_ports[l3] = RBA::Region::new(tc.begin_shapes_rec(l3v)).each_merged.collect { |p| p.bbox.center } + + network = rex.extract(tech, geo, vertex_ports, polygon_ports) + + assert_equal(network.to_s(true) + "\n", <<"END") +R $2.1(9.3,-5.9;9.9,-5.3) P0.1(12.9,-5.9;13.5,-5.3) 2.25 +R $8.2(10,-3.5;10,-2.7) P0.2(12.9,-3.4;13.5,-2.8) 1 +R $2.1(9.3,-5.9;9.9,-5.3) V0.2(0.3,-5.7;0.5,-5.5) 55.75 +R $2.1(9.3,-5.9;9.9,-5.3) $8.2(10,-3.5;10,-2.7) 13.2813 +R $8.2(10,-3.5;10,-2.7) V0.1(5.2,0.4;5.2,0.4) 28.7812 +R V0.1(5.2,0.4;5.2,0.4) V0.2(0.3,-5.7;0.5,-5.5) 17.375 +END + end + end load("test_epilogue.rb") From 38df5aa39203e4b8d2bb6037dfc48f26e7bed9d6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 4 May 2025 23:06:22 +0200 Subject: [PATCH 199/392] Updating changelog for 0.30.1 --- Changelog | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Changelog b/Changelog index 387183e50..8357044b8 100644 --- a/Changelog +++ b/Changelog @@ -1,16 +1,17 @@ 0.30.1 (2025-04-27): +* Bugfix: %GITHUB%/issues/2038 DEdgePairWithProperties not working properly in scripts * Bugfix: %GITHUB%/issues/2011 Some DRC bugs fixed * Bug: %GITHUB%/issues/2014 Bug fixes in LEF/DEF reader * Enhancement: %GITHUB%/issues/2016 Lazy evaluation of PCell also when changing guiding shape properties * Enhancement: %GITHUB%/issues/2019 Support for Qt 6.9 -* Bugfix: %GITHUB%/issues/2020 String weak ordering issue fixed in edge processor +* Bugfix: %GITHUB%/issues/2020 Strict weak ordering issue fixed in edge processor * Enhancement: %GITHUB%/issues/2024 Option to configure grid density * Bugfix: %GITHUB%/issues/2025 Brackets get added in List type PCell parameter edit field * Bugfix: %GITHUB%/issues/2026 Display is dead after opening 2.5d view * Bugfix/Enhancement: some updates of "strmxor" tool - strmxor was giving wrong results if cell variants are present where one variant is covered entirely by a large shape - - parallelization now happens on a per-layer basis (same as for + - Parallelization now happens on a per-layer basis (same as for XOR tool in KLayout) - Shape count was not consistent in deep mode - All buddy tools print total runtime with -d11 From 6d871166f72803db142788bdf93ba1d414a90d79 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 4 May 2025 23:12:47 +0200 Subject: [PATCH 200/392] Updating changelog for 0.30.2 --- Changelog | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Changelog b/Changelog index 8357044b8..1138f86d5 100644 --- a/Changelog +++ b/Changelog @@ -1,8 +1,10 @@ +0.30.2 (2025-xx-xx): +* Enhancement: %GITHUB%/issues/2016 Lazy evaluation of PCell also when changing guiding shape properties + 0.30.1 (2025-04-27): * Bugfix: %GITHUB%/issues/2038 DEdgePairWithProperties not working properly in scripts * Bugfix: %GITHUB%/issues/2011 Some DRC bugs fixed * Bug: %GITHUB%/issues/2014 Bug fixes in LEF/DEF reader -* Enhancement: %GITHUB%/issues/2016 Lazy evaluation of PCell also when changing guiding shape properties * Enhancement: %GITHUB%/issues/2019 Support for Qt 6.9 * Bugfix: %GITHUB%/issues/2020 Strict weak ordering issue fixed in edge processor * Enhancement: %GITHUB%/issues/2024 Option to configure grid density From e232bf8127dbda94a7a54aa63b92722b33ca86cb Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 10 May 2025 19:59:19 +0000 Subject: [PATCH 201/392] Specify build system in pyproject.toml, using SPDX compliant license spec. --- pyproject.toml | 4 ++++ setup.py | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index eafccb77f..a436bf5c6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,7 @@ +[build-system] +requires = ["setuptools >= 77.0.3"] +build-backend = "setuptools.build_meta" + [tool.cibuildwheel] build-verbosity = "3" test-command = [ diff --git a/setup.py b/setup.py index c816a0c5e..04fb5c4f9 100644 --- a/setup.py +++ b/setup.py @@ -987,7 +987,7 @@ if __name__ == "__main__": setup( name=config.root, version=config.version(), - license="GNU GPLv3", + license="GPL-3.0-or-later", description="KLayout standalone Python package", long_description="This package is a standalone distribution of KLayout's Python API.\n\nFor more details see here: https://www.klayout.org/klayout-pypi", author="Matthias Koefferlein", @@ -996,7 +996,6 @@ if __name__ == "__main__": # Recommended classifiers "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", - "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX :: Linux", From e279888f52f43be514361db6c806f9045782fe99 Mon Sep 17 00:00:00 2001 From: Charlie Lin Date: Sun, 11 May 2025 09:07:55 -0400 Subject: [PATCH 202/392] Use Py_IncRef in pyaCallables.cc --- src/pya/pya/pyaCallables.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pya/pya/pyaCallables.cc b/src/pya/pya/pyaCallables.cc index 829f7076f..27f5fa790 100644 --- a/src/pya/pya/pyaCallables.cc +++ b/src/pya/pya/pyaCallables.cc @@ -49,7 +49,7 @@ pya_object_deallocate (PyObject *self) // may trigger a GC (https://github.com/KLayout/klayout/issues/1054). // According to the comments this may be turned into a release mode assertion, so // we better work around it. - ++self->ob_refcnt; + Py_IncRef(self); // Mute Python warnings in debug case PyObject_GC_UnTrack (self); From 7bee9ebcec43f90e5eb08dddb770f532add0a600 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 11 May 2025 21:48:01 +0200 Subject: [PATCH 203/392] Fixing a linker issue on Windows. --- src/plugins/streamers/maly/db_plugin/dbMALY.h | 9 +++++---- src/plugins/streamers/streamers.pro | 1 - 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/streamers/maly/db_plugin/dbMALY.h b/src/plugins/streamers/maly/db_plugin/dbMALY.h index 84f2b3347..32a5e07a7 100644 --- a/src/plugins/streamers/maly/db_plugin/dbMALY.h +++ b/src/plugins/streamers/maly/db_plugin/dbMALY.h @@ -27,6 +27,7 @@ #include "dbPoint.h" #include "dbTrans.h" #include "dbBox.h" +#include "dbPluginCommon.h" #include "tlException.h" #include "tlInternational.h" @@ -61,7 +62,7 @@ public: /** * @brief A class representing a title field on a mask */ -class DB_PUBLIC MALYTitle +class DB_PLUGIN_PUBLIC MALYTitle { public: /** @@ -138,7 +139,7 @@ public: /** * @brief A class representing a structure (pattern) on a mask */ -class DB_PUBLIC MALYStructure +class DB_PLUGIN_PUBLIC MALYStructure { public: /** @@ -220,7 +221,7 @@ public: /** * @brief A class representing one mask */ -class DB_PUBLIC MALYMask +class DB_PLUGIN_PUBLIC MALYMask { public: /** @@ -261,7 +262,7 @@ public: /** * @brief A class representing the MALY file */ -class DB_PUBLIC MALYData +class DB_PLUGIN_PUBLIC MALYData { public: /** diff --git a/src/plugins/streamers/streamers.pro b/src/plugins/streamers/streamers.pro index 5d7bbf3ad..8bfcea845 100644 --- a/src/plugins/streamers/streamers.pro +++ b/src/plugins/streamers/streamers.pro @@ -6,4 +6,3 @@ SUBDIR_LIST = $$files($$PWD/*) SUBDIR_LIST -= $$PWD/streamers.pro SUBDIRS = $$SUBDIR_LIST - From 6cd4b97cd3357fe92ebd9846ce68ea31b940868b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 15 May 2025 21:17:15 +0200 Subject: [PATCH 204/392] Including internal net names in LVS DB Problem: so far, these names have not been conveyed. But after the net IDs changed in the LVS DB (renumbering), the names should be retained. Otherwise it is difficult to relate log level entries or Spice netlists to them. --- src/db/db/dbLayoutToNetlistWriter.cc | 2 + src/lvs/unit_tests/lvsTests.cc | 4 +- testdata/lvs/bbdevices1.lvsdb | 4 +- testdata/lvs/bbdevices1b.lvsdb | 4 +- testdata/lvs/bbdevices2.lvsdb | 8 ++-- testdata/lvs/bbdevices2b.lvsdb | 8 ++-- testdata/lvs/bbdevices3.lvsdb | 4 +- testdata/lvs/bbdevices3b.lvsdb | 4 +- testdata/lvs/bbdevices4.lvsdb | 4 +- testdata/lvs/bbdevices4b.lvsdb | 4 +- testdata/lvs/bbdevices5.lvsdb | 6 +-- testdata/lvs/bbdevices5b.lvsdb | 6 +-- testdata/lvs/custom_compare.lvsdb | 4 +- testdata/lvs/must_connect1.lvsdb | 2 +- testdata/lvs/must_connect1_tl.lvsdb | 2 +- testdata/lvs/must_connect3.lvsdb | 6 +-- testdata/lvs/res_combine1.lvsdb.1 | 2 +- testdata/lvs/res_combine1.lvsdb.2 | 2 +- testdata/lvs/res_combine1.lvsdb.3 | 2 +- testdata/lvs/res_combine1.lvsdb.4 | 2 +- testdata/lvs/res_combine1.lvsdb.5 | 2 +- testdata/lvs/res_combine1.lvsdb.6 | 2 +- testdata/lvs/res_combine2.lvsdb.1 | 2 +- testdata/lvs/res_combine2.lvsdb.2 | 2 +- testdata/lvs/res_combine2.lvsdb.3 | 2 +- testdata/lvs/res_combine2.lvsdb.4 | 2 +- testdata/lvs/res_combine2.lvsdb.5 | 2 +- testdata/lvs/res_combine2.lvsdb.6 | 2 +- testdata/lvs/res_combine3.lvsdb.1 | 2 +- testdata/lvs/res_combine3.lvsdb.2 | 2 +- testdata/lvs/res_combine3.lvsdb.3 | 2 +- testdata/lvs/res_combine3.lvsdb.4 | 2 +- testdata/lvs/res_combine3.lvsdb.5 | 2 +- testdata/lvs/res_combine3.lvsdb.6 | 2 +- testdata/lvs/ringo_device_subcircuits.lvsdb.1 | 2 +- testdata/lvs/ringo_device_subcircuits.lvsdb.2 | 2 +- testdata/lvs/ringo_layout_var.lvsdb.1 | 2 +- testdata/lvs/ringo_layout_var.lvsdb.2 | 2 +- testdata/lvs/ringo_mixed_hierarchy.lvsdb | 20 ++++---- testdata/lvs/ringo_simple.lvsdb.1 | 2 +- testdata/lvs/ringo_simple.lvsdb.2 | 2 +- testdata/lvs/ringo_simple_blackboxing.lvsdb | 12 ++--- .../lvs/ringo_simple_blackboxing_netter.lvsdb | 12 ++--- testdata/lvs/ringo_simple_compare2.lvsdb.1 | 2 +- testdata/lvs/ringo_simple_compare2.lvsdb.2 | 2 +- .../lvs/ringo_simple_device_scaling.lvsdb.1 | 2 +- .../lvs/ringo_simple_device_scaling.lvsdb.2 | 2 +- .../lvs/ringo_simple_dummy_device.lvsdb.1 | 24 +++++----- .../ringo_simple_implicit_connections.lvsdb.1 | 2 +- .../ringo_simple_implicit_connections.lvsdb.2 | 2 +- testdata/lvs/ringo_simple_io.lvsdb.1 | 2 +- testdata/lvs/ringo_simple_io.lvsdb.2 | 2 +- testdata/lvs/ringo_simple_io2.l2n.1 | 2 +- testdata/lvs/ringo_simple_io2.l2n.2 | 2 +- testdata/lvs/ringo_simple_io2.lvsdb.1 | 2 +- testdata/lvs/ringo_simple_io2.lvsdb.2 | 2 +- ...simple_net_and_circuit_equivalence.lvsdb.1 | 2 +- ...simple_net_and_circuit_equivalence.lvsdb.2 | 2 +- .../lvs/ringo_simple_pin_swapping.lvsdb.1 | 2 +- .../lvs/ringo_simple_pin_swapping.lvsdb.2 | 2 +- .../ringo_simple_same_device_classes.lvsdb.1 | 2 +- .../ringo_simple_same_device_classes.lvsdb.2 | 2 +- .../lvs/ringo_simple_simplification.lvsdb.1 | 14 +++--- ...o_simple_simplification_with_align.lvsdb.1 | 14 +++--- testdata/lvs/ringo_simple_with_tol.lvsdb.1 | 2 +- testdata/lvs/ringo_simple_with_tol.lvsdb.2 | 2 +- .../lvs/ringo_simple_with_tol_early.lvsdb.1 | 2 +- .../lvs/ringo_simple_with_tol_early.lvsdb.2 | 2 +- testdata/lvs/soft_connect3.l2n | 8 ++-- testdata/lvs/soft_connect4.l2n | 6 +-- testdata/lvs/soft_connect5.l2n | 4 +- testdata/lvs/soft_connect6.l2n | 4 +- testdata/lvs/stray_texts1.l2n | 4 +- testdata/lvs/stray_texts2.l2n | 4 +- testdata/lvs/test_22a.lvsdb.1 | 48 +++++++++---------- testdata/lvs/test_22b.lvsdb.1 | 48 +++++++++---------- testdata/lvs/test_22c.lvsdb.1 | 2 +- testdata/lvs/test_22c.lvsdb.2 | 2 +- testdata/lvs/test_22c.lvsdb.3 | 2 +- testdata/lvs/test_22d.lvsdb.1 | 2 +- testdata/lvs/test_22d.lvsdb.2 | 2 +- testdata/lvs/test_22d.lvsdb.3 | 2 +- 82 files changed, 200 insertions(+), 198 deletions(-) diff --git a/src/db/db/dbLayoutToNetlistWriter.cc b/src/db/db/dbLayoutToNetlistWriter.cc index cadd0a711..df2499492 100644 --- a/src/db/db/dbLayoutToNetlistWriter.cc +++ b/src/db/db/dbLayoutToNetlistWriter.cc @@ -692,6 +692,8 @@ void std_writer_impl::write (TokenizedOutput &stream, const db::Net &net, *outp << tl::to_string (id); if (! net.name ().empty ()) { TokenizedOutput (*outp, Keys::name_key, true) << tl::to_word_or_quoted_string (net.name ()); + } else if (net.id () != id) { + TokenizedOutput (*outp, Keys::name_key, true) << tl::to_word_or_quoted_string (net.expanded_name ()); } *outp << endl; diff --git a/src/lvs/unit_tests/lvsTests.cc b/src/lvs/unit_tests/lvsTests.cc index 4d6c30f12..c1aac0071 100644 --- a/src/lvs/unit_tests/lvsTests.cc +++ b/src/lvs/unit_tests/lvsTests.cc @@ -154,7 +154,7 @@ TEST(16_private) TEST(17_private) { test_is_long_runner (); - run_test (_this, "test_17.lylvs", "test_17b.cir.gz", "test_17.gds.gz", true, "test_17b_4.lvsdb"); + run_test (_this, "test_17.lylvs", "test_17b.cir.gz", "test_17.gds.gz", true, "test_17b_5.lvsdb"); } TEST(18_private) @@ -177,7 +177,7 @@ TEST(20_private) TEST(21_private) { - run_test (_this, "test_21.lylvs", "test_21.cir.gz", "test_21.gds.gz", true, "test_21_5.lvsdb"); + run_test (_this, "test_21.lylvs", "test_21.cir.gz", "test_21.gds.gz", true, "test_21_6.lvsdb"); } // issue #1021 diff --git a/testdata/lvs/bbdevices1.lvsdb b/testdata/lvs/bbdevices1.lvsdb index 482fe39a9..71177629c 100644 --- a/testdata/lvs/bbdevices1.lvsdb +++ b/testdata/lvs/bbdevices1.lvsdb @@ -210,7 +210,7 @@ layout( net(2 rect(l2 (-148000 463000) (300000 25000)) ) - net(3 + net(3 name($5) rect(l9 (348500 26500) (25000 179000)) rect(l9 (-57500 -58000) (90000 90000)) rect(l9 (-86000 -288500) (90000 90000)) @@ -224,7 +224,7 @@ layout( rect(l14 (-270500 7500) (25000 193000)) rect(l14 (-87500 -87500) (150000 150000)) ) - net(4 + net(4 name($8) rect(l14 (-126000 -195000) (292000 25000)) ) diff --git a/testdata/lvs/bbdevices1b.lvsdb b/testdata/lvs/bbdevices1b.lvsdb index 0be76fb34..ec7a60026 100644 --- a/testdata/lvs/bbdevices1b.lvsdb +++ b/testdata/lvs/bbdevices1b.lvsdb @@ -140,7 +140,7 @@ layout( net(2 rect(l2 (-148000 463000) (300000 25000)) ) - net(3 + net(3 name($5) rect(l9 (348500 26500) (25000 179000)) rect(l9 (-57500 -58000) (90000 90000)) rect(l9 (-86000 -288500) (90000 90000)) @@ -154,7 +154,7 @@ layout( rect(l14 (-270500 7500) (25000 193000)) rect(l14 (-87500 -87500) (150000 150000)) ) - net(4 + net(4 name($8) rect(l14 (-126000 -195000) (292000 25000)) ) diff --git a/testdata/lvs/bbdevices2.lvsdb b/testdata/lvs/bbdevices2.lvsdb index 49b82b677..0f2a44006 100644 --- a/testdata/lvs/bbdevices2.lvsdb +++ b/testdata/lvs/bbdevices2.lvsdb @@ -220,7 +220,7 @@ layout( net(4 rect(l2 (822690 427000) (63970 82000)) ) - net(5 + net(5 name($7) rect(l9 (348500 26500) (25000 179000)) rect(l9 (-57500 -58000) (90000 90000)) rect(l9 (-86000 -288500) (90000 90000)) @@ -234,13 +234,13 @@ layout( rect(l14 (-270500 7500) (25000 193000)) rect(l14 (-87500 -87500) (150000 150000)) ) - net(6 + net(6 name($10) rect(l14 (-126000 -195000) (292000 25000)) ) - net(7 + net(7 name($12) rect(l14 (-410240 -216150) (83240 87150)) ) - net(8 + net(8 name($13) rect(l14 (846960 -242000) (77190 75500)) ) diff --git a/testdata/lvs/bbdevices2b.lvsdb b/testdata/lvs/bbdevices2b.lvsdb index 2ff9faa34..9cd9cc391 100644 --- a/testdata/lvs/bbdevices2b.lvsdb +++ b/testdata/lvs/bbdevices2b.lvsdb @@ -150,7 +150,7 @@ layout( net(4 rect(l2 (822690 427000) (63970 82000)) ) - net(5 + net(5 name($7) rect(l9 (348500 26500) (25000 179000)) rect(l9 (-57500 -58000) (90000 90000)) rect(l9 (-86000 -288500) (90000 90000)) @@ -164,13 +164,13 @@ layout( rect(l14 (-270500 7500) (25000 193000)) rect(l14 (-87500 -87500) (150000 150000)) ) - net(6 + net(6 name($10) rect(l14 (-126000 -195000) (292000 25000)) ) - net(7 + net(7 name($12) rect(l14 (-410240 -216150) (83240 87150)) ) - net(8 + net(8 name($13) rect(l14 (846960 -242000) (77190 75500)) ) diff --git a/testdata/lvs/bbdevices3.lvsdb b/testdata/lvs/bbdevices3.lvsdb index 1f7335c95..3ab3475c0 100644 --- a/testdata/lvs/bbdevices3.lvsdb +++ b/testdata/lvs/bbdevices3.lvsdb @@ -210,7 +210,7 @@ layout( net(2 rect(l2 (-148000 463000) (300000 25000)) ) - net(3 + net(3 name($5) rect(l9 (348500 26500) (25000 179000)) rect(l9 (-57500 -58000) (90000 90000)) rect(l9 (-86000 -288500) (90000 90000)) @@ -224,7 +224,7 @@ layout( rect(l14 (-270500 7500) (25000 193000)) rect(l14 (-87500 -87500) (150000 150000)) ) - net(4 + net(4 name($8) rect(l14 (-126000 -195000) (292000 25000)) ) diff --git a/testdata/lvs/bbdevices3b.lvsdb b/testdata/lvs/bbdevices3b.lvsdb index d4573f7a4..f60bc5bab 100644 --- a/testdata/lvs/bbdevices3b.lvsdb +++ b/testdata/lvs/bbdevices3b.lvsdb @@ -140,7 +140,7 @@ layout( net(2 rect(l2 (-148000 463000) (300000 25000)) ) - net(3 + net(3 name($5) rect(l9 (348500 26500) (25000 179000)) rect(l9 (-57500 -58000) (90000 90000)) rect(l9 (-86000 -288500) (90000 90000)) @@ -154,7 +154,7 @@ layout( rect(l14 (-270500 7500) (25000 193000)) rect(l14 (-87500 -87500) (150000 150000)) ) - net(4 + net(4 name($8) rect(l14 (-126000 -195000) (292000 25000)) ) diff --git a/testdata/lvs/bbdevices4.lvsdb b/testdata/lvs/bbdevices4.lvsdb index fe5d26b4a..0a74172f8 100644 --- a/testdata/lvs/bbdevices4.lvsdb +++ b/testdata/lvs/bbdevices4.lvsdb @@ -207,7 +207,7 @@ layout( net(2 rect(l2 (-148000 463000) (300000 25000)) ) - net(3 + net(3 name($5) rect(l9 (348500 26500) (25000 179000)) rect(l9 (-57500 -58000) (90000 90000)) rect(l9 (-86000 -288500) (90000 90000)) @@ -220,7 +220,7 @@ layout( rect(l14 (-25000 -193000) (25000 193000)) rect(l14 (-87500 -87500) (150000 150000)) ) - net(4 + net(4 name($7) rect(l14 (-126000 -195000) (292000 25000)) ) diff --git a/testdata/lvs/bbdevices4b.lvsdb b/testdata/lvs/bbdevices4b.lvsdb index 90993b7b2..ba1dfd285 100644 --- a/testdata/lvs/bbdevices4b.lvsdb +++ b/testdata/lvs/bbdevices4b.lvsdb @@ -137,7 +137,7 @@ layout( net(2 rect(l2 (-148000 463000) (300000 25000)) ) - net(3 + net(3 name($5) rect(l9 (348500 26500) (25000 179000)) rect(l9 (-57500 -58000) (90000 90000)) rect(l9 (-86000 -288500) (90000 90000)) @@ -150,7 +150,7 @@ layout( rect(l14 (-25000 -193000) (25000 193000)) rect(l14 (-87500 -87500) (150000 150000)) ) - net(4 + net(4 name($7) rect(l14 (-126000 -195000) (292000 25000)) ) diff --git a/testdata/lvs/bbdevices5.lvsdb b/testdata/lvs/bbdevices5.lvsdb index 42cef8ab3..6b5dee81b 100644 --- a/testdata/lvs/bbdevices5.lvsdb +++ b/testdata/lvs/bbdevices5.lvsdb @@ -210,7 +210,7 @@ layout( net(2 rect(l2 (-148000 463000) (300000 25000)) ) - net(3 + net(3 name($5) rect(l9 (348500 26500) (25000 179000)) rect(l9 (-57500 -58000) (90000 90000)) rect(l9 (-86000 -288500) (90000 90000)) @@ -224,10 +224,10 @@ layout( rect(l14 (-24000 -225500) (118960 25000)) rect(l14 (-182460 113000) (150000 150000)) ) - net(4 + net(4 name($7) rect(l14 (-126000 -195000) (292000 25000)) ) - net(5 + net(5 name($10) rect(l14 (509690 -219000) (113310 25000)) ) diff --git a/testdata/lvs/bbdevices5b.lvsdb b/testdata/lvs/bbdevices5b.lvsdb index dd49d20c6..7e9f9b4c0 100644 --- a/testdata/lvs/bbdevices5b.lvsdb +++ b/testdata/lvs/bbdevices5b.lvsdb @@ -140,7 +140,7 @@ layout( net(2 rect(l2 (-148000 463000) (300000 25000)) ) - net(3 + net(3 name($5) rect(l9 (348500 26500) (25000 179000)) rect(l9 (-57500 -58000) (90000 90000)) rect(l9 (-86000 -288500) (90000 90000)) @@ -154,10 +154,10 @@ layout( rect(l14 (-24000 -225500) (118960 25000)) rect(l14 (-182460 113000) (150000 150000)) ) - net(4 + net(4 name($7) rect(l14 (-126000 -195000) (292000 25000)) ) - net(5 + net(5 name($10) rect(l14 (509690 -219000) (113310 25000)) ) diff --git a/testdata/lvs/custom_compare.lvsdb b/testdata/lvs/custom_compare.lvsdb index 6862bb95d..d10c9dd63 100644 --- a/testdata/lvs/custom_compare.lvsdb +++ b/testdata/lvs/custom_compare.lvsdb @@ -38,12 +38,12 @@ layout( rect((0 0) (10255 5900)) # Nets with their geometries - net(1 + net(1 name($13) rect(l3 (4850 4600) (180 180)) rect(l1 (-245 -250) (310 320)) rect(l1 (0 -250) (200 250)) ) - net(2 + net(2 name($14) rect(l3 (10010 3500) (180 180)) rect(l1 (-245 -250) (310 320)) rect(l1 (-510 -250) (200 250)) diff --git a/testdata/lvs/must_connect1.lvsdb b/testdata/lvs/must_connect1.lvsdb index 1d91c7e7e..26eeb011f 100644 --- a/testdata/lvs/must_connect1.lvsdb +++ b/testdata/lvs/must_connect1.lvsdb @@ -258,7 +258,7 @@ J( N(6 I(Q) R(l12 (13260 2010) (0 0)) ) - N(7 + N(7 I($8) R(l10 (3450 4840) (3055 250)) R(l10 (2885 -250) (1975 250)) ) diff --git a/testdata/lvs/must_connect1_tl.lvsdb b/testdata/lvs/must_connect1_tl.lvsdb index fa05c7401..fa6a8ab2c 100644 --- a/testdata/lvs/must_connect1_tl.lvsdb +++ b/testdata/lvs/must_connect1_tl.lvsdb @@ -258,7 +258,7 @@ J( N(6 I(Q) R(l12 (13260 2010) (0 0)) ) - N(7 + N(7 I($8) R(l10 (3450 4840) (3055 250)) R(l10 (2885 -250) (1975 250)) ) diff --git a/testdata/lvs/must_connect3.lvsdb b/testdata/lvs/must_connect3.lvsdb index 83f89832d..19782493b 100644 --- a/testdata/lvs/must_connect3.lvsdb +++ b/testdata/lvs/must_connect3.lvsdb @@ -240,21 +240,21 @@ J( R(l10 (-1975 -8190) (1975 575)) R(l10 (-1005 -255) (0 0)) ) - N(3 + N(3 I($I1) R(l3 (12950 2130) (2160 250)) R(l3 (-250 0) (250 4990)) R(l3 (-1605 0) (1605 250)) R(l7 (-1545 -250) (240 250)) R(l8 (-560 -375) (690 510)) ) - N(4 + N(4 I($I2) R(l3 (12100 7300) (640 530)) R(l7 (-540 -415) (270 250)) R(l8 (-1695 -250) (1695 250)) R(l8 (-4075 -5650) (2630 250)) R(l8 (-250 0) (250 5150)) ) - N(5 + N(5 I($I3) R(l7 (6465 7325) (220 240)) R(l8 (-4100 -5365) (3125 250)) R(l8 (-250 0) (250 4860)) diff --git a/testdata/lvs/res_combine1.lvsdb.1 b/testdata/lvs/res_combine1.lvsdb.1 index 971e6ddb9..be2dcbb47 100644 --- a/testdata/lvs/res_combine1.lvsdb.1 +++ b/testdata/lvs/res_combine1.lvsdb.1 @@ -68,7 +68,7 @@ layout( rect(l1 (-22675 -1970) (540 2000)) rect(l1 (-21840 -1605) (540 2000)) ) - net(2 + net(2 name($5) rect(l4 (19795 5575) (220 220)) rect(l4 (-220 -745) (220 220)) rect(l4 (-220 -745) (220 220)) diff --git a/testdata/lvs/res_combine1.lvsdb.2 b/testdata/lvs/res_combine1.lvsdb.2 index a482e6a93..725a958ca 100644 --- a/testdata/lvs/res_combine1.lvsdb.2 +++ b/testdata/lvs/res_combine1.lvsdb.2 @@ -68,7 +68,7 @@ layout( rect(l1 (-22675 -1970) (540 2000)) rect(l1 (-21840 -1605) (540 2000)) ) - net(2 + net(2 name($5) rect(l4 (19795 5575) (220 220)) rect(l4 (-220 -745) (220 220)) rect(l4 (-220 -745) (220 220)) diff --git a/testdata/lvs/res_combine1.lvsdb.3 b/testdata/lvs/res_combine1.lvsdb.3 index 4a5eefac3..594bea880 100644 --- a/testdata/lvs/res_combine1.lvsdb.3 +++ b/testdata/lvs/res_combine1.lvsdb.3 @@ -68,7 +68,7 @@ layout( rect(l1 (-22675 -1970) (540 2000)) rect(l1 (-21840 -1605) (540 2000)) ) - net(2 + net(2 name($5) rect(l4 (19795 5575) (220 220)) rect(l4 (-220 -745) (220 220)) rect(l4 (-220 -745) (220 220)) diff --git a/testdata/lvs/res_combine1.lvsdb.4 b/testdata/lvs/res_combine1.lvsdb.4 index 99b0f35c3..f9b232132 100644 --- a/testdata/lvs/res_combine1.lvsdb.4 +++ b/testdata/lvs/res_combine1.lvsdb.4 @@ -68,7 +68,7 @@ layout( rect(l1 (-22675 -1970) (540 2000)) rect(l1 (-21840 -1605) (540 2000)) ) - net(2 + net(2 name($5) rect(l4 (19795 5575) (220 220)) rect(l4 (-220 -745) (220 220)) rect(l4 (-220 -745) (220 220)) diff --git a/testdata/lvs/res_combine1.lvsdb.5 b/testdata/lvs/res_combine1.lvsdb.5 index 6f91be370..d7a796e89 100644 --- a/testdata/lvs/res_combine1.lvsdb.5 +++ b/testdata/lvs/res_combine1.lvsdb.5 @@ -68,7 +68,7 @@ layout( rect(l1 (-22675 -1970) (540 2000)) rect(l1 (-21840 -1605) (540 2000)) ) - net(2 + net(2 name($5) rect(l4 (19795 5575) (220 220)) rect(l4 (-220 -745) (220 220)) rect(l4 (-220 -745) (220 220)) diff --git a/testdata/lvs/res_combine1.lvsdb.6 b/testdata/lvs/res_combine1.lvsdb.6 index 856312de7..456048215 100644 --- a/testdata/lvs/res_combine1.lvsdb.6 +++ b/testdata/lvs/res_combine1.lvsdb.6 @@ -68,7 +68,7 @@ layout( rect(l1 (-22675 -1970) (540 2000)) rect(l1 (-21840 -1605) (540 2000)) ) - net(2 + net(2 name($5) rect(l4 (19795 5575) (220 220)) rect(l4 (-220 -745) (220 220)) rect(l4 (-220 -745) (220 220)) diff --git a/testdata/lvs/res_combine2.lvsdb.1 b/testdata/lvs/res_combine2.lvsdb.1 index 7c5410036..877dec9ae 100644 --- a/testdata/lvs/res_combine2.lvsdb.1 +++ b/testdata/lvs/res_combine2.lvsdb.1 @@ -64,7 +64,7 @@ layout( rect(l1 (-22675 -1970) (540 2000)) rect(l1 (-21840 -1605) (540 2000)) ) - net(2 + net(2 name($5) rect(l4 (19795 5575) (220 220)) rect(l4 (-220 -745) (220 220)) rect(l4 (-220 -745) (220 220)) diff --git a/testdata/lvs/res_combine2.lvsdb.2 b/testdata/lvs/res_combine2.lvsdb.2 index 9e6fd6e42..8019fd0ca 100644 --- a/testdata/lvs/res_combine2.lvsdb.2 +++ b/testdata/lvs/res_combine2.lvsdb.2 @@ -64,7 +64,7 @@ layout( rect(l1 (-22675 -1970) (540 2000)) rect(l1 (-21840 -1605) (540 2000)) ) - net(2 + net(2 name($5) rect(l4 (19795 5575) (220 220)) rect(l4 (-220 -745) (220 220)) rect(l4 (-220 -745) (220 220)) diff --git a/testdata/lvs/res_combine2.lvsdb.3 b/testdata/lvs/res_combine2.lvsdb.3 index ef47b7df8..ce3c2a3de 100644 --- a/testdata/lvs/res_combine2.lvsdb.3 +++ b/testdata/lvs/res_combine2.lvsdb.3 @@ -64,7 +64,7 @@ layout( rect(l1 (-22675 -1970) (540 2000)) rect(l1 (-21840 -1605) (540 2000)) ) - net(2 + net(2 name($5) rect(l4 (19795 5575) (220 220)) rect(l4 (-220 -745) (220 220)) rect(l4 (-220 -745) (220 220)) diff --git a/testdata/lvs/res_combine2.lvsdb.4 b/testdata/lvs/res_combine2.lvsdb.4 index 6d0b65e2d..ac3e9baa4 100644 --- a/testdata/lvs/res_combine2.lvsdb.4 +++ b/testdata/lvs/res_combine2.lvsdb.4 @@ -64,7 +64,7 @@ layout( rect(l1 (-22675 -1970) (540 2000)) rect(l1 (-21840 -1605) (540 2000)) ) - net(2 + net(2 name($5) rect(l4 (19795 5575) (220 220)) rect(l4 (-220 -745) (220 220)) rect(l4 (-220 -745) (220 220)) diff --git a/testdata/lvs/res_combine2.lvsdb.5 b/testdata/lvs/res_combine2.lvsdb.5 index d5911356e..6f538c7ef 100644 --- a/testdata/lvs/res_combine2.lvsdb.5 +++ b/testdata/lvs/res_combine2.lvsdb.5 @@ -64,7 +64,7 @@ layout( rect(l1 (-22675 -1970) (540 2000)) rect(l1 (-21840 -1605) (540 2000)) ) - net(2 + net(2 name($5) rect(l4 (19795 5575) (220 220)) rect(l4 (-220 -745) (220 220)) rect(l4 (-220 -745) (220 220)) diff --git a/testdata/lvs/res_combine2.lvsdb.6 b/testdata/lvs/res_combine2.lvsdb.6 index 89c533976..0b94b61ee 100644 --- a/testdata/lvs/res_combine2.lvsdb.6 +++ b/testdata/lvs/res_combine2.lvsdb.6 @@ -64,7 +64,7 @@ layout( rect(l1 (-22675 -1970) (540 2000)) rect(l1 (-21840 -1605) (540 2000)) ) - net(2 + net(2 name($5) rect(l4 (19795 5575) (220 220)) rect(l4 (-220 -745) (220 220)) rect(l4 (-220 -745) (220 220)) diff --git a/testdata/lvs/res_combine3.lvsdb.1 b/testdata/lvs/res_combine3.lvsdb.1 index f6289f6b2..d067edf33 100644 --- a/testdata/lvs/res_combine3.lvsdb.1 +++ b/testdata/lvs/res_combine3.lvsdb.1 @@ -64,7 +64,7 @@ layout( rect(l1 (-22675 -1970) (540 2000)) rect(l1 (-21840 -1605) (540 2000)) ) - net(2 + net(2 name($5) rect(l4 (19795 5575) (220 220)) rect(l4 (-220 -745) (220 220)) rect(l4 (-220 -745) (220 220)) diff --git a/testdata/lvs/res_combine3.lvsdb.2 b/testdata/lvs/res_combine3.lvsdb.2 index 7353b6eaa..14254a034 100644 --- a/testdata/lvs/res_combine3.lvsdb.2 +++ b/testdata/lvs/res_combine3.lvsdb.2 @@ -64,7 +64,7 @@ layout( rect(l1 (-22675 -1970) (540 2000)) rect(l1 (-21840 -1605) (540 2000)) ) - net(2 + net(2 name($5) rect(l4 (19795 5575) (220 220)) rect(l4 (-220 -745) (220 220)) rect(l4 (-220 -745) (220 220)) diff --git a/testdata/lvs/res_combine3.lvsdb.3 b/testdata/lvs/res_combine3.lvsdb.3 index 63dd2ec89..c4c6ed4e3 100644 --- a/testdata/lvs/res_combine3.lvsdb.3 +++ b/testdata/lvs/res_combine3.lvsdb.3 @@ -64,7 +64,7 @@ layout( rect(l1 (-22675 -1970) (540 2000)) rect(l1 (-21840 -1605) (540 2000)) ) - net(2 + net(2 name($5) rect(l4 (19795 5575) (220 220)) rect(l4 (-220 -745) (220 220)) rect(l4 (-220 -745) (220 220)) diff --git a/testdata/lvs/res_combine3.lvsdb.4 b/testdata/lvs/res_combine3.lvsdb.4 index e7b329f55..c8ae0cd34 100644 --- a/testdata/lvs/res_combine3.lvsdb.4 +++ b/testdata/lvs/res_combine3.lvsdb.4 @@ -64,7 +64,7 @@ layout( rect(l1 (-22675 -1970) (540 2000)) rect(l1 (-21840 -1605) (540 2000)) ) - net(2 + net(2 name($5) rect(l4 (19795 5575) (220 220)) rect(l4 (-220 -745) (220 220)) rect(l4 (-220 -745) (220 220)) diff --git a/testdata/lvs/res_combine3.lvsdb.5 b/testdata/lvs/res_combine3.lvsdb.5 index bdbcf54e9..073211088 100644 --- a/testdata/lvs/res_combine3.lvsdb.5 +++ b/testdata/lvs/res_combine3.lvsdb.5 @@ -64,7 +64,7 @@ layout( rect(l1 (-22675 -1970) (540 2000)) rect(l1 (-21840 -1605) (540 2000)) ) - net(2 + net(2 name($5) rect(l4 (19795 5575) (220 220)) rect(l4 (-220 -745) (220 220)) rect(l4 (-220 -745) (220 220)) diff --git a/testdata/lvs/res_combine3.lvsdb.6 b/testdata/lvs/res_combine3.lvsdb.6 index 1bb644fbe..abb6d99e0 100644 --- a/testdata/lvs/res_combine3.lvsdb.6 +++ b/testdata/lvs/res_combine3.lvsdb.6 @@ -64,7 +64,7 @@ layout( rect(l1 (-22675 -1970) (540 2000)) rect(l1 (-21840 -1605) (540 2000)) ) - net(2 + net(2 name($5) rect(l4 (19795 5575) (220 220)) rect(l4 (-220 -745) (220 220)) rect(l4 (-220 -745) (220 220)) diff --git a/testdata/lvs/ringo_device_subcircuits.lvsdb.1 b/testdata/lvs/ringo_device_subcircuits.lvsdb.1 index 06212f154..a53138333 100644 --- a/testdata/lvs/ringo_device_subcircuits.lvsdb.1 +++ b/testdata/lvs/ringo_device_subcircuits.lvsdb.1 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_device_subcircuits.lvsdb.2 b/testdata/lvs/ringo_device_subcircuits.lvsdb.2 index 4a2536ed7..8eb0b266c 100644 --- a/testdata/lvs/ringo_device_subcircuits.lvsdb.2 +++ b/testdata/lvs/ringo_device_subcircuits.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_layout_var.lvsdb.1 b/testdata/lvs/ringo_layout_var.lvsdb.1 index 0346c3a17..276675045 100644 --- a/testdata/lvs/ringo_layout_var.lvsdb.1 +++ b/testdata/lvs/ringo_layout_var.lvsdb.1 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_layout_var.lvsdb.2 b/testdata/lvs/ringo_layout_var.lvsdb.2 index fbe29c86e..124c4b7b3 100644 --- a/testdata/lvs/ringo_layout_var.lvsdb.2 +++ b/testdata/lvs/ringo_layout_var.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_mixed_hierarchy.lvsdb b/testdata/lvs/ringo_mixed_hierarchy.lvsdb index 35c7fd126..7824c9d2c 100644 --- a/testdata/lvs/ringo_mixed_hierarchy.lvsdb +++ b/testdata/lvs/ringo_mixed_hierarchy.lvsdb @@ -308,31 +308,31 @@ layout( rect(l2 (950 -1500) (425 1500)) rect(l6 (-425 -4890) (425 950)) ) - net(5 + net(5 name($7) rect(l11 (5550 2950) (900 300)) ) - net(6 + net(6 name($8) rect(l11 (7350 2950) (900 300)) ) - net(7 + net(7 name($9) rect(l11 (9150 2950) (900 300)) ) - net(8 + net(8 name($10) rect(l11 (10950 2950) (900 300)) ) - net(9 + net(9 name($11) rect(l11 (12750 2950) (900 300)) ) - net(10 + net(10 name($12) rect(l11 (14550 2950) (900 300)) ) - net(11 + net(11 name($13) rect(l11 (16350 2950) (900 300)) ) - net(12 + net(12 name($14) rect(l11 (18150 2950) (900 300)) ) - net(13 + net(13 name($15) rect(l11 (19950 2950) (900 300)) ) net(14 name(OUT) @@ -341,7 +341,7 @@ layout( rect(l13 (-100 -100) (0 0)) rect(l13 (-200 -200) (400 400)) ) - net(15 + net(15 name($21) rect(l6 (2775 1660) (450 950)) ) net(16 name(VSS) diff --git a/testdata/lvs/ringo_simple.lvsdb.1 b/testdata/lvs/ringo_simple.lvsdb.1 index b5a1aabcb..30cbf6980 100644 --- a/testdata/lvs/ringo_simple.lvsdb.1 +++ b/testdata/lvs/ringo_simple.lvsdb.1 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple.lvsdb.2 b/testdata/lvs/ringo_simple.lvsdb.2 index 4bfcca0be..c8c4ded92 100644 --- a/testdata/lvs/ringo_simple.lvsdb.2 +++ b/testdata/lvs/ringo_simple.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_blackboxing.lvsdb b/testdata/lvs/ringo_simple_blackboxing.lvsdb index 938c32edf..a76727f22 100644 --- a/testdata/lvs/ringo_simple_blackboxing.lvsdb +++ b/testdata/lvs/ringo_simple_blackboxing.lvsdb @@ -169,22 +169,22 @@ layout( rect(l10 (-24850 -800) (500 1500)) rect(l10 (22900 -1500) (500 1500)) ) - net(10 + net(10 name($I22) rect(l11 (7350 2950) (900 300)) ) - net(11 + net(11 name($I18) rect(l11 (16350 2950) (900 300)) ) - net(12 + net(12 name($I36) rect(l11 (9150 2950) (900 300)) ) - net(13 + net(13 name($I37) rect(l11 (10950 2950) (900 300)) ) - net(14 + net(14 name($I38) rect(l11 (12750 2950) (900 300)) ) - net(15 + net(15 name($I39) rect(l11 (14550 2950) (900 300)) ) diff --git a/testdata/lvs/ringo_simple_blackboxing_netter.lvsdb b/testdata/lvs/ringo_simple_blackboxing_netter.lvsdb index 938c32edf..a76727f22 100644 --- a/testdata/lvs/ringo_simple_blackboxing_netter.lvsdb +++ b/testdata/lvs/ringo_simple_blackboxing_netter.lvsdb @@ -169,22 +169,22 @@ layout( rect(l10 (-24850 -800) (500 1500)) rect(l10 (22900 -1500) (500 1500)) ) - net(10 + net(10 name($I22) rect(l11 (7350 2950) (900 300)) ) - net(11 + net(11 name($I18) rect(l11 (16350 2950) (900 300)) ) - net(12 + net(12 name($I36) rect(l11 (9150 2950) (900 300)) ) - net(13 + net(13 name($I37) rect(l11 (10950 2950) (900 300)) ) - net(14 + net(14 name($I38) rect(l11 (12750 2950) (900 300)) ) - net(15 + net(15 name($I39) rect(l11 (14550 2950) (900 300)) ) diff --git a/testdata/lvs/ringo_simple_compare2.lvsdb.1 b/testdata/lvs/ringo_simple_compare2.lvsdb.1 index b5a1aabcb..30cbf6980 100644 --- a/testdata/lvs/ringo_simple_compare2.lvsdb.1 +++ b/testdata/lvs/ringo_simple_compare2.lvsdb.1 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_compare2.lvsdb.2 b/testdata/lvs/ringo_simple_compare2.lvsdb.2 index 4bfcca0be..c8c4ded92 100644 --- a/testdata/lvs/ringo_simple_compare2.lvsdb.2 +++ b/testdata/lvs/ringo_simple_compare2.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_device_scaling.lvsdb.1 b/testdata/lvs/ringo_simple_device_scaling.lvsdb.1 index 6cb29d488..3c8750631 100644 --- a/testdata/lvs/ringo_simple_device_scaling.lvsdb.1 +++ b/testdata/lvs/ringo_simple_device_scaling.lvsdb.1 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_device_scaling.lvsdb.2 b/testdata/lvs/ringo_simple_device_scaling.lvsdb.2 index d77d08cb2..fb6b39193 100644 --- a/testdata/lvs/ringo_simple_device_scaling.lvsdb.2 +++ b/testdata/lvs/ringo_simple_device_scaling.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_dummy_device.lvsdb.1 b/testdata/lvs/ringo_simple_dummy_device.lvsdb.1 index 0bb5af8f8..10f93fc33 100644 --- a/testdata/lvs/ringo_simple_dummy_device.lvsdb.1 +++ b/testdata/lvs/ringo_simple_dummy_device.lvsdb.1 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) @@ -361,41 +361,41 @@ layout( rect((0 350) (27600 7650)) # Nets with their geometries - net(1 + net(1 name($3) rect(l4 (26050 2800) (525 550)) rect(l4 (-525 -300) (300 300)) rect(l4 (-25 -2000) (250 1450)) rect(l8 (-465 310) (180 180)) rect(l11 (-240 -240) (300 300)) ) - net(2 + net(2 name($4) rect(l11 (4040 2950) (610 300)) ) - net(3 + net(3 name($5) rect(l11 (5550 2950) (900 300)) ) - net(4 + net(4 name($6) rect(l11 (7350 2950) (900 300)) ) - net(5 + net(5 name($7) rect(l11 (9150 2950) (900 300)) ) - net(6 + net(6 name($8) rect(l11 (10950 2950) (900 300)) ) - net(7 + net(7 name($9) rect(l11 (12750 2950) (900 300)) ) - net(8 + net(8 name($10) rect(l11 (14550 2950) (900 300)) ) - net(9 + net(9 name($11) rect(l11 (16350 2950) (900 300)) ) - net(10 + net(10 name($12) rect(l11 (18150 2950) (900 300)) ) - net(11 + net(11 name($13) rect(l11 (19950 2950) (900 300)) ) net(12 name(FB) diff --git a/testdata/lvs/ringo_simple_implicit_connections.lvsdb.1 b/testdata/lvs/ringo_simple_implicit_connections.lvsdb.1 index 90d11e8b4..74a756054 100644 --- a/testdata/lvs/ringo_simple_implicit_connections.lvsdb.1 +++ b/testdata/lvs/ringo_simple_implicit_connections.lvsdb.1 @@ -202,7 +202,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_implicit_connections.lvsdb.2 b/testdata/lvs/ringo_simple_implicit_connections.lvsdb.2 index 145f3cd6a..97bf061e7 100644 --- a/testdata/lvs/ringo_simple_implicit_connections.lvsdb.2 +++ b/testdata/lvs/ringo_simple_implicit_connections.lvsdb.2 @@ -202,7 +202,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_io.lvsdb.1 b/testdata/lvs/ringo_simple_io.lvsdb.1 index b57fc7c54..9b2db59ba 100644 --- a/testdata/lvs/ringo_simple_io.lvsdb.1 +++ b/testdata/lvs/ringo_simple_io.lvsdb.1 @@ -176,7 +176,7 @@ J( R(l11 (-150 -150) (300 300)) ) N(7 I(SUBSTRATE)) - N(8 + N(8 I($I3) R(l6 (975 1660) (425 950)) R(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_io.lvsdb.2 b/testdata/lvs/ringo_simple_io.lvsdb.2 index d3768aef4..c5c4ac6f1 100644 --- a/testdata/lvs/ringo_simple_io.lvsdb.2 +++ b/testdata/lvs/ringo_simple_io.lvsdb.2 @@ -176,7 +176,7 @@ J( R(l11 (-150 -150) (300 300)) ) N(7 I(SUBSTRATE)) - N(8 + N(8 I($I3) R(l6 (975 1660) (425 950)) R(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_io2.l2n.1 b/testdata/lvs/ringo_simple_io2.l2n.1 index 5dff4a81c..ef8092802 100644 --- a/testdata/lvs/ringo_simple_io2.l2n.1 +++ b/testdata/lvs/ringo_simple_io2.l2n.1 @@ -175,7 +175,7 @@ X(ND2X1 R(l11 (-150 -150) (300 300)) ) N(7 I(SUBSTRATE)) - N(8 + N(8 I($I3) R(l6 (975 1660) (425 950)) R(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_io2.l2n.2 b/testdata/lvs/ringo_simple_io2.l2n.2 index fdc09bcfd..d0a07ab94 100644 --- a/testdata/lvs/ringo_simple_io2.l2n.2 +++ b/testdata/lvs/ringo_simple_io2.l2n.2 @@ -175,7 +175,7 @@ X(ND2X1 R(l11 (-150 -150) (300 300)) ) N(7 I(SUBSTRATE)) - N(8 + N(8 I($I3) R(l6 (975 1660) (425 950)) R(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_io2.lvsdb.1 b/testdata/lvs/ringo_simple_io2.lvsdb.1 index b5a1aabcb..30cbf6980 100644 --- a/testdata/lvs/ringo_simple_io2.lvsdb.1 +++ b/testdata/lvs/ringo_simple_io2.lvsdb.1 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_io2.lvsdb.2 b/testdata/lvs/ringo_simple_io2.lvsdb.2 index 4bfcca0be..c8c4ded92 100644 --- a/testdata/lvs/ringo_simple_io2.lvsdb.2 +++ b/testdata/lvs/ringo_simple_io2.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvsdb.1 b/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvsdb.1 index f88be0bc7..835c6f61c 100644 --- a/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvsdb.1 +++ b/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvsdb.1 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvsdb.2 b/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvsdb.2 index e500fb5a3..f924852fe 100644 --- a/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvsdb.2 +++ b/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_pin_swapping.lvsdb.1 b/testdata/lvs/ringo_simple_pin_swapping.lvsdb.1 index e383b2490..9a08296ca 100644 --- a/testdata/lvs/ringo_simple_pin_swapping.lvsdb.1 +++ b/testdata/lvs/ringo_simple_pin_swapping.lvsdb.1 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_pin_swapping.lvsdb.2 b/testdata/lvs/ringo_simple_pin_swapping.lvsdb.2 index b06039efe..d8f0bcee8 100644 --- a/testdata/lvs/ringo_simple_pin_swapping.lvsdb.2 +++ b/testdata/lvs/ringo_simple_pin_swapping.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_same_device_classes.lvsdb.1 b/testdata/lvs/ringo_simple_same_device_classes.lvsdb.1 index a6570c9a6..95ce47712 100644 --- a/testdata/lvs/ringo_simple_same_device_classes.lvsdb.1 +++ b/testdata/lvs/ringo_simple_same_device_classes.lvsdb.1 @@ -201,7 +201,7 @@ layout( rect(l17 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l9 (975 1660) (425 950)) rect(l9 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_same_device_classes.lvsdb.2 b/testdata/lvs/ringo_simple_same_device_classes.lvsdb.2 index 55dbc29ba..958d330a8 100644 --- a/testdata/lvs/ringo_simple_same_device_classes.lvsdb.2 +++ b/testdata/lvs/ringo_simple_same_device_classes.lvsdb.2 @@ -201,7 +201,7 @@ layout( rect(l17 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l9 (975 1660) (425 950)) rect(l9 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_simplification.lvsdb.1 b/testdata/lvs/ringo_simple_simplification.lvsdb.1 index cf50d6703..fc1c945d2 100644 --- a/testdata/lvs/ringo_simple_simplification.lvsdb.1 +++ b/testdata/lvs/ringo_simple_simplification.lvsdb.1 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) @@ -560,22 +560,22 @@ layout( rect(l10 (-24850 -800) (500 1500)) rect(l10 (22900 -1500) (500 1500)) ) - net(10 + net(10 name($I22) rect(l11 (7350 2950) (900 300)) ) - net(11 + net(11 name($I18) rect(l11 (16350 2950) (900 300)) ) - net(12 + net(12 name($I36) rect(l11 (9150 2950) (900 300)) ) - net(13 + net(13 name($I37) rect(l11 (10950 2950) (900 300)) ) - net(14 + net(14 name($I38) rect(l11 (12750 2950) (900 300)) ) - net(15 + net(15 name($I39) rect(l11 (14550 2950) (900 300)) ) diff --git a/testdata/lvs/ringo_simple_simplification_with_align.lvsdb.1 b/testdata/lvs/ringo_simple_simplification_with_align.lvsdb.1 index acb6dfc04..2d9fdbac8 100644 --- a/testdata/lvs/ringo_simple_simplification_with_align.lvsdb.1 +++ b/testdata/lvs/ringo_simple_simplification_with_align.lvsdb.1 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) @@ -560,22 +560,22 @@ layout( rect(l10 (-24850 -800) (500 1500)) rect(l10 (22900 -1500) (500 1500)) ) - net(10 + net(10 name($I22) rect(l11 (7350 2950) (900 300)) ) - net(11 + net(11 name($I18) rect(l11 (16350 2950) (900 300)) ) - net(12 + net(12 name($I36) rect(l11 (9150 2950) (900 300)) ) - net(13 + net(13 name($I37) rect(l11 (10950 2950) (900 300)) ) - net(14 + net(14 name($I38) rect(l11 (12750 2950) (900 300)) ) - net(15 + net(15 name($I39) rect(l11 (14550 2950) (900 300)) ) diff --git a/testdata/lvs/ringo_simple_with_tol.lvsdb.1 b/testdata/lvs/ringo_simple_with_tol.lvsdb.1 index 628deb17f..98d8bdee9 100644 --- a/testdata/lvs/ringo_simple_with_tol.lvsdb.1 +++ b/testdata/lvs/ringo_simple_with_tol.lvsdb.1 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_with_tol.lvsdb.2 b/testdata/lvs/ringo_simple_with_tol.lvsdb.2 index 6316f5d39..bded7a1fc 100644 --- a/testdata/lvs/ringo_simple_with_tol.lvsdb.2 +++ b/testdata/lvs/ringo_simple_with_tol.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_with_tol_early.lvsdb.1 b/testdata/lvs/ringo_simple_with_tol_early.lvsdb.1 index 628deb17f..98d8bdee9 100644 --- a/testdata/lvs/ringo_simple_with_tol_early.lvsdb.1 +++ b/testdata/lvs/ringo_simple_with_tol_early.lvsdb.1 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_with_tol_early.lvsdb.2 b/testdata/lvs/ringo_simple_with_tol_early.lvsdb.2 index 6316f5d39..bded7a1fc 100644 --- a/testdata/lvs/ringo_simple_with_tol_early.lvsdb.2 +++ b/testdata/lvs/ringo_simple_with_tol_early.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/soft_connect3.l2n b/testdata/lvs/soft_connect3.l2n index f046069b3..ff7a71bcc 100644 --- a/testdata/lvs/soft_connect3.l2n +++ b/testdata/lvs/soft_connect3.l2n @@ -81,13 +81,13 @@ X(NTRANS R(l12 (-290 -690) (360 760)) R(l6 (-680 -855) (775 950)) ) - N(2 + N(2 I($3) R(l8 (290 -310) (220 220)) R(l8 (-220 180) (220 220)) R(l12 (-290 -690) (360 760)) R(l6 (-455 -855) (775 950)) ) - N(3 + N(3 I($5) R(l4 (-125 -800) (250 1600)) ) N(4 I(SUBSTRATE)) @@ -117,13 +117,13 @@ X(PTRANS R(l12 (-290 -690) (360 760)) R(l2 (-680 -855) (775 950)) ) - N(2 + N(2 I($3) R(l8 (290 -310) (220 220)) R(l8 (-220 180) (220 220)) R(l12 (-290 -690) (360 760)) R(l2 (-455 -855) (775 950)) ) - N(3 + N(3 I($5) R(l4 (-125 -800) (250 1600)) ) N(4) diff --git a/testdata/lvs/soft_connect4.l2n b/testdata/lvs/soft_connect4.l2n index 402240d29..5cc59c0c3 100644 --- a/testdata/lvs/soft_connect4.l2n +++ b/testdata/lvs/soft_connect4.l2n @@ -108,12 +108,12 @@ X(INV R(l10 (-700 -950) (400 1000)) R(l2 (-1875 -975) (775 950)) ) - N(3 + N(3 I($4) R(l4 (-125 -250) (250 2500)) R(l4 (-250 -3050) (250 1600)) R(l4 (-250 1200) (250 1600)) ) - N(4 + N(4 I($5) R(l8 (-510 -310) (220 220)) R(l8 (-220 180) (220 220)) R(l8 (-220 2180) (220 220)) @@ -170,7 +170,7 @@ X(TOP R(l12 (1810 2600) (690 400)) R(l16 (-400 -200) (0 0)) ) - N(4 + N(4 I($5) R(l3 (4000 3400) (2700 2000)) ) N(5 I(SUBSTRATE)) diff --git a/testdata/lvs/soft_connect5.l2n b/testdata/lvs/soft_connect5.l2n index d2a3334b8..bbe544f80 100644 --- a/testdata/lvs/soft_connect5.l2n +++ b/testdata/lvs/soft_connect5.l2n @@ -108,12 +108,12 @@ X(INV R(l10 (-700 -950) (400 1000)) R(l2 (-1875 -975) (775 950)) ) - N(3 + N(3 I($4) R(l4 (-125 -250) (250 2500)) R(l4 (-250 -3050) (250 1600)) R(l4 (-250 1200) (250 1600)) ) - N(4 + N(4 I($5) R(l8 (-510 -310) (220 220)) R(l8 (-220 180) (220 220)) R(l8 (-220 2180) (220 220)) diff --git a/testdata/lvs/soft_connect6.l2n b/testdata/lvs/soft_connect6.l2n index 600f9b0bc..0d3e3a9ad 100644 --- a/testdata/lvs/soft_connect6.l2n +++ b/testdata/lvs/soft_connect6.l2n @@ -104,7 +104,7 @@ X(INV R(l14 (-200 -900) (1000 900)) R(l6 (-2175 -925) (775 950)) ) - N(3 + N(3 I($4) R(l3 (-1500 1800) (3000 2000)) R(l3 (-200 -2000) (1000 2000)) R(l8 (-2010 -1310) (220 220)) @@ -122,7 +122,7 @@ X(INV R(l10 (-700 -950) (400 1000)) R(l2 (-1875 -975) (775 950)) ) - N(4 + N(4 I($6) R(l4 (-125 -250) (250 2500)) R(l4 (-250 -3050) (250 1600)) R(l4 (-250 1200) (250 1600)) diff --git a/testdata/lvs/stray_texts1.l2n b/testdata/lvs/stray_texts1.l2n index 04c1dbed5..312542798 100644 --- a/testdata/lvs/stray_texts1.l2n +++ b/testdata/lvs/stray_texts1.l2n @@ -27,10 +27,10 @@ X(CHILD J(l1 D (1820 510)) R(l2 (-70 -90) (160 160)) ) - N(5 + N(5 I($8) R(l2 (2370 410) (170 200)) ) - N(6 + N(6 I($9) R(l2 (2910 430) (190 200)) ) P(1 I(A)) diff --git a/testdata/lvs/stray_texts2.l2n b/testdata/lvs/stray_texts2.l2n index a014cac50..92dd849ea 100644 --- a/testdata/lvs/stray_texts2.l2n +++ b/testdata/lvs/stray_texts2.l2n @@ -25,10 +25,10 @@ X(CHILD R(l1 (1750 420) (160 160)) R(l1 (-90 -70) (0 0)) ) - N(5 + N(5 I($6) R(l1 (2370 410) (170 200)) ) - N(6 + N(6 I($7) R(l1 (2910 430) (190 200)) ) P(1 I(A)) diff --git a/testdata/lvs/test_22a.lvsdb.1 b/testdata/lvs/test_22a.lvsdb.1 index 3b1267688..6f083ffa0 100644 --- a/testdata/lvs/test_22a.lvsdb.1 +++ b/testdata/lvs/test_22a.lvsdb.1 @@ -832,16 +832,16 @@ layout( rect(l24 (2030 -150) (150 150)) rect(l24 (2030 -150) (150 150)) ) - net(27 + net(27 name($28) polygon(l7 (955 2305) (0 180) (-690 0) (0 150) (1650 0) (0 -150) (-690 0) (0 -180)) ) - net(28 + net(28 name($29) polygon(l7 (7495 2305) (0 180) (-690 0) (0 150) (1650 0) (0 -150) (-690 0) (0 -180)) ) - net(29 + net(29 name($30) polygon(l7 (3135 2305) (0 180) (-690 0) (0 150) (1650 0) (0 -150) (-690 0) (0 -180)) ) - net(30 + net(30 name($31) polygon(l7 (5315 2305) (0 180) (-690 0) (0 150) (1650 0) (0 -150) (-690 0) (0 -180)) ) net(31 name('wl[1]') @@ -893,7 +893,7 @@ layout( rect(l24 (2030 -150) (150 150)) rect(l24 (2030 -150) (150 150)) ) - net(32 + net(32 name($33) polygon(l2 (395 3065) (0 340) (-105 0) (0 420) (525 0) (0 -760)) rect(l2 (-525 1670) (445 420)) polygon(l9 (-210 -1620) (0 1570) (170 0) (0 -920) (245 0) (0 -170) (-245 0) (0 -480)) @@ -904,12 +904,12 @@ layout( rect(l22 (0 -270) (950 150)) rect(l22 (0 -840) (150 2010)) ) - net(33 + net(33 name($34) rect(l7 (940 3965) (950 150)) rect(l7 (-1280 -150) (330 270)) rect(l7 (950 -960) (150 2010)) ) - net(34 + net(34 name($35) polygon(l2 (1365 3065) (0 760) (525 0) (0 -420) (-105 0) (0 -340)) rect(l2 (-340 1670) (445 420)) polygon(l9 (-405 -1620) (0 840) (-245 0) (0 170) (245 0) (0 560) (170 0) (0 -1570)) @@ -920,7 +920,7 @@ layout( rect(l22 (-1100 -1320) (150 2010)) rect(l22 (950 -960) (330 270)) ) - net(35 + net(35 name($36) polygon(l2 (2575 3065) (0 340) (-105 0) (0 420) (525 0) (0 -760)) rect(l2 (-525 1670) (445 420)) polygon(l9 (-210 -1620) (0 1570) (170 0) (0 -920) (245 0) (0 -170) (-245 0) (0 -480)) @@ -931,12 +931,12 @@ layout( rect(l22 (0 -270) (950 150)) rect(l22 (0 -840) (150 2010)) ) - net(36 + net(36 name($37) rect(l7 (3120 3965) (950 150)) rect(l7 (-1280 -150) (330 270)) rect(l7 (950 -960) (150 2010)) ) - net(37 + net(37 name($38) polygon(l2 (4755 3065) (0 340) (-105 0) (0 420) (525 0) (0 -760)) rect(l2 (-525 1670) (445 420)) polygon(l9 (-210 -1620) (0 1570) (170 0) (0 -920) (245 0) (0 -170) (-245 0) (0 -480)) @@ -947,12 +947,12 @@ layout( rect(l22 (0 -270) (950 150)) rect(l22 (0 -840) (150 2010)) ) - net(38 + net(38 name($39) rect(l7 (5300 3965) (950 150)) rect(l7 (-1280 -150) (330 270)) rect(l7 (950 -960) (150 2010)) ) - net(39 + net(39 name($40) polygon(l2 (6935 3065) (0 340) (-105 0) (0 420) (525 0) (0 -760)) rect(l2 (-525 1670) (445 420)) polygon(l9 (-210 -1620) (0 1570) (170 0) (0 -920) (245 0) (0 -170) (-245 0) (0 -480)) @@ -963,34 +963,34 @@ layout( rect(l22 (0 -270) (950 150)) rect(l22 (0 -840) (150 2010)) ) - net(40 + net(40 name($41) rect(l7 (7480 3965) (950 150)) rect(l7 (-1280 -150) (330 270)) rect(l7 (950 -960) (150 2010)) ) - net(41 + net(41 name($42) polygon(l7 (265 2915) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) - net(42 + net(42 name($43) polygon(l7 (6805 2915) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) - net(43 + net(43 name($44) polygon(l7 (2445 2915) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) - net(44 + net(44 name($45) polygon(l7 (4625 2915) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) - net(45 + net(45 name($47) rect(l7 (290 4445) (950 150)) rect(l7 (-1100 -1320) (150 2010)) rect(l7 (950 -960) (330 270)) ) - net(46 + net(46 name($48) rect(l7 (2470 4445) (950 150)) rect(l7 (-1100 -1320) (150 2010)) rect(l7 (950 -960) (330 270)) ) - net(47 + net(47 name($49) polygon(l2 (3545 3065) (0 760) (525 0) (0 -420) (-105 0) (0 -340)) rect(l2 (-340 1670) (445 420)) polygon(l9 (-405 -1620) (0 840) (-245 0) (0 170) (245 0) (0 560) (170 0) (0 -1570)) @@ -1001,12 +1001,12 @@ layout( rect(l22 (-1100 -1320) (150 2010)) rect(l22 (950 -960) (330 270)) ) - net(48 + net(48 name($50) rect(l7 (4650 4445) (950 150)) rect(l7 (-1100 -1320) (150 2010)) rect(l7 (950 -960) (330 270)) ) - net(49 + net(49 name($51) polygon(l2 (5725 3065) (0 760) (525 0) (0 -420) (-105 0) (0 -340)) rect(l2 (-340 1670) (445 420)) polygon(l9 (-405 -1620) (0 840) (-245 0) (0 170) (245 0) (0 560) (170 0) (0 -1570)) @@ -1017,7 +1017,7 @@ layout( rect(l22 (-1100 -1320) (150 2010)) rect(l22 (950 -960) (330 270)) ) - net(50 + net(50 name($52) polygon(l2 (7905 3065) (0 760) (525 0) (0 -420) (-105 0) (0 -340)) rect(l2 (-340 1670) (445 420)) polygon(l9 (-405 -1620) (0 840) (-245 0) (0 170) (245 0) (0 560) (170 0) (0 -1570)) @@ -1028,7 +1028,7 @@ layout( rect(l22 (-1100 -1320) (150 2010)) rect(l22 (950 -960) (330 270)) ) - net(51 + net(51 name($53) rect(l7 (6830 4445) (950 150)) rect(l7 (-1100 -1320) (150 2010)) rect(l7 (950 -960) (330 270)) diff --git a/testdata/lvs/test_22b.lvsdb.1 b/testdata/lvs/test_22b.lvsdb.1 index bac531746..45724cbcf 100644 --- a/testdata/lvs/test_22b.lvsdb.1 +++ b/testdata/lvs/test_22b.lvsdb.1 @@ -832,16 +832,16 @@ layout( rect(l24 (2030 -150) (150 150)) rect(l24 (2030 -150) (150 150)) ) - net(27 + net(27 name($28) polygon(l7 (955 2305) (0 180) (-690 0) (0 150) (1650 0) (0 -150) (-690 0) (0 -180)) ) - net(28 + net(28 name($29) polygon(l7 (7495 2305) (0 180) (-690 0) (0 150) (1650 0) (0 -150) (-690 0) (0 -180)) ) - net(29 + net(29 name($30) polygon(l7 (3135 2305) (0 180) (-690 0) (0 150) (1650 0) (0 -150) (-690 0) (0 -180)) ) - net(30 + net(30 name($31) polygon(l7 (5315 2305) (0 180) (-690 0) (0 150) (1650 0) (0 -150) (-690 0) (0 -180)) ) net(31 name('wl[1]') @@ -893,7 +893,7 @@ layout( rect(l24 (2030 -150) (150 150)) rect(l24 (2030 -150) (150 150)) ) - net(32 + net(32 name($33) polygon(l2 (395 3065) (0 340) (-105 0) (0 420) (525 0) (0 -760)) rect(l2 (-525 1670) (445 420)) polygon(l9 (-210 -1620) (0 1570) (170 0) (0 -920) (245 0) (0 -170) (-245 0) (0 -480)) @@ -904,12 +904,12 @@ layout( rect(l22 (0 -270) (950 150)) rect(l22 (0 -840) (150 2010)) ) - net(33 + net(33 name($34) rect(l7 (940 3965) (950 150)) rect(l7 (-1280 -150) (330 270)) rect(l7 (950 -960) (150 2010)) ) - net(34 + net(34 name($35) polygon(l2 (1365 3065) (0 760) (525 0) (0 -420) (-105 0) (0 -340)) rect(l2 (-340 1670) (445 420)) polygon(l9 (-405 -1620) (0 840) (-245 0) (0 170) (245 0) (0 560) (170 0) (0 -1570)) @@ -920,7 +920,7 @@ layout( rect(l22 (-1100 -1320) (150 2010)) rect(l22 (950 -960) (330 270)) ) - net(35 + net(35 name($36) polygon(l2 (2575 3065) (0 340) (-105 0) (0 420) (525 0) (0 -760)) rect(l2 (-525 1670) (445 420)) polygon(l9 (-210 -1620) (0 1570) (170 0) (0 -920) (245 0) (0 -170) (-245 0) (0 -480)) @@ -931,12 +931,12 @@ layout( rect(l22 (0 -270) (950 150)) rect(l22 (0 -840) (150 2010)) ) - net(36 + net(36 name($37) rect(l7 (3120 3965) (950 150)) rect(l7 (-1280 -150) (330 270)) rect(l7 (950 -960) (150 2010)) ) - net(37 + net(37 name($38) polygon(l2 (4755 3065) (0 340) (-105 0) (0 420) (525 0) (0 -760)) rect(l2 (-525 1670) (445 420)) polygon(l9 (-210 -1620) (0 1570) (170 0) (0 -920) (245 0) (0 -170) (-245 0) (0 -480)) @@ -947,12 +947,12 @@ layout( rect(l22 (0 -270) (950 150)) rect(l22 (0 -840) (150 2010)) ) - net(38 + net(38 name($39) rect(l7 (5300 3965) (950 150)) rect(l7 (-1280 -150) (330 270)) rect(l7 (950 -960) (150 2010)) ) - net(39 + net(39 name($40) polygon(l2 (6935 3065) (0 340) (-105 0) (0 420) (525 0) (0 -760)) rect(l2 (-525 1670) (445 420)) polygon(l9 (-210 -1620) (0 1570) (170 0) (0 -920) (245 0) (0 -170) (-245 0) (0 -480)) @@ -963,34 +963,34 @@ layout( rect(l22 (0 -270) (950 150)) rect(l22 (0 -840) (150 2010)) ) - net(40 + net(40 name($41) rect(l7 (7480 3965) (950 150)) rect(l7 (-1280 -150) (330 270)) rect(l7 (950 -960) (150 2010)) ) - net(41 + net(41 name($42) polygon(l7 (265 2915) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) - net(42 + net(42 name($43) polygon(l7 (6805 2915) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) - net(43 + net(43 name($44) polygon(l7 (2445 2915) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) - net(44 + net(44 name($45) polygon(l7 (4625 2915) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) - net(45 + net(45 name($47) rect(l7 (290 4445) (950 150)) rect(l7 (-1100 -1320) (150 2010)) rect(l7 (950 -960) (330 270)) ) - net(46 + net(46 name($48) rect(l7 (2470 4445) (950 150)) rect(l7 (-1100 -1320) (150 2010)) rect(l7 (950 -960) (330 270)) ) - net(47 + net(47 name($49) polygon(l2 (3545 3065) (0 760) (525 0) (0 -420) (-105 0) (0 -340)) rect(l2 (-340 1670) (445 420)) polygon(l9 (-405 -1620) (0 840) (-245 0) (0 170) (245 0) (0 560) (170 0) (0 -1570)) @@ -1001,12 +1001,12 @@ layout( rect(l22 (-1100 -1320) (150 2010)) rect(l22 (950 -960) (330 270)) ) - net(48 + net(48 name($50) rect(l7 (4650 4445) (950 150)) rect(l7 (-1100 -1320) (150 2010)) rect(l7 (950 -960) (330 270)) ) - net(49 + net(49 name($51) polygon(l2 (5725 3065) (0 760) (525 0) (0 -420) (-105 0) (0 -340)) rect(l2 (-340 1670) (445 420)) polygon(l9 (-405 -1620) (0 840) (-245 0) (0 170) (245 0) (0 560) (170 0) (0 -1570)) @@ -1017,7 +1017,7 @@ layout( rect(l22 (-1100 -1320) (150 2010)) rect(l22 (950 -960) (330 270)) ) - net(50 + net(50 name($52) polygon(l2 (7905 3065) (0 760) (525 0) (0 -420) (-105 0) (0 -340)) rect(l2 (-340 1670) (445 420)) polygon(l9 (-405 -1620) (0 840) (-245 0) (0 170) (245 0) (0 560) (170 0) (0 -1570)) @@ -1028,7 +1028,7 @@ layout( rect(l22 (-1100 -1320) (150 2010)) rect(l22 (950 -960) (330 270)) ) - net(51 + net(51 name($53) rect(l7 (6830 4445) (950 150)) rect(l7 (-1100 -1320) (150 2010)) rect(l7 (950 -960) (330 270)) diff --git a/testdata/lvs/test_22c.lvsdb.1 b/testdata/lvs/test_22c.lvsdb.1 index 029afa97f..0fef41270 100644 --- a/testdata/lvs/test_22c.lvsdb.1 +++ b/testdata/lvs/test_22c.lvsdb.1 @@ -265,7 +265,7 @@ layout( rect(l23 (-110 -170) (170 170)) rect(l2 (-355 -210) (420 265)) ) - net(9 + net(9 name($10) polygon(l7 (265 140) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) net(10 name(vss) diff --git a/testdata/lvs/test_22c.lvsdb.2 b/testdata/lvs/test_22c.lvsdb.2 index 7a023acbf..8438d1de6 100644 --- a/testdata/lvs/test_22c.lvsdb.2 +++ b/testdata/lvs/test_22c.lvsdb.2 @@ -265,7 +265,7 @@ layout( rect(l23 (-110 -170) (170 170)) rect(l2 (-355 -210) (420 265)) ) - net(9 + net(9 name($10) polygon(l7 (265 140) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) net(10 name(vss) diff --git a/testdata/lvs/test_22c.lvsdb.3 b/testdata/lvs/test_22c.lvsdb.3 index 3f52d22f2..e966c57ac 100644 --- a/testdata/lvs/test_22c.lvsdb.3 +++ b/testdata/lvs/test_22c.lvsdb.3 @@ -265,7 +265,7 @@ layout( rect(l23 (-110 -170) (170 170)) rect(l2 (-355 -210) (420 265)) ) - net(9 + net(9 name($10) polygon(l7 (265 140) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) net(10 name(vss) diff --git a/testdata/lvs/test_22d.lvsdb.1 b/testdata/lvs/test_22d.lvsdb.1 index bb827e1c7..73e8005b4 100644 --- a/testdata/lvs/test_22d.lvsdb.1 +++ b/testdata/lvs/test_22d.lvsdb.1 @@ -265,7 +265,7 @@ layout( rect(l23 (-110 -170) (170 170)) rect(l2 (-355 -210) (420 265)) ) - net(9 + net(9 name($10) polygon(l7 (265 140) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) net(10 name(vss) diff --git a/testdata/lvs/test_22d.lvsdb.2 b/testdata/lvs/test_22d.lvsdb.2 index 7b32708a2..1d4e0d82a 100644 --- a/testdata/lvs/test_22d.lvsdb.2 +++ b/testdata/lvs/test_22d.lvsdb.2 @@ -265,7 +265,7 @@ layout( rect(l23 (-110 -170) (170 170)) rect(l2 (-355 -210) (420 265)) ) - net(9 + net(9 name($10) polygon(l7 (265 140) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) net(10 name(vss) diff --git a/testdata/lvs/test_22d.lvsdb.3 b/testdata/lvs/test_22d.lvsdb.3 index d1f5b2a20..10a35d95c 100644 --- a/testdata/lvs/test_22d.lvsdb.3 +++ b/testdata/lvs/test_22d.lvsdb.3 @@ -265,7 +265,7 @@ layout( rect(l23 (-110 -170) (170 170)) rect(l2 (-355 -210) (420 265)) ) - net(9 + net(9 name($10) polygon(l7 (265 140) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) net(10 name(vss) From 271800ab94e1e0ed010d3d981ed9732edcb52302 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 15 May 2025 23:27:03 +0200 Subject: [PATCH 205/392] WIP: first implementation. Builds, but needs testing. --- src/buddies/src/bd/bdReaderOptions.cc | 6 +- .../lefdef/db_plugin/dbLEFDEFImporter.cc | 90 +++++++++++++++---- .../lefdef/db_plugin/dbLEFDEFImporter.h | 56 +++++++++++- .../lefdef/db_plugin/dbLEFDEFPlugin.cc | 84 ++++++++--------- .../lefdef/db_plugin/gsiDeclDbLEFDEF.cc | 4 + .../lefdef/unit_tests/dbLEFDEFImportTests.cc | 4 +- 6 files changed, 177 insertions(+), 67 deletions(-) diff --git a/src/buddies/src/bd/bdReaderOptions.cc b/src/buddies/src/bd/bdReaderOptions.cc index 752d20edd..091412826 100644 --- a/src/buddies/src/bd/bdReaderOptions.cc +++ b/src/buddies/src/bd/bdReaderOptions.cc @@ -846,12 +846,16 @@ static std::vector split_file_list (const std::string &infile) void read_files (db::Layout &layout, const std::string &infile, const db::LoadLayoutOptions &options) { + // enter a LEF caching context for chaining multiple DEF with the same LEF + db::LoadLayoutOptions local_options (options); + local_options.set_option_by_name ("lefdef_config.lef_context_enabled", true); + std::vector files = split_file_list (infile); for (std::vector::const_iterator f = files.begin (); f != files.end (); ++f) { tl::InputStream stream (*f); db::Reader reader (stream); - reader.read (layout, options); + reader.read (layout, local_options); } } diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc index d95144c75..c2d8c253e 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc @@ -598,7 +598,8 @@ LEFDEFReaderOptions::LEFDEFReaderOptions () m_map_file (), m_macro_resolution_mode (0), m_read_lef_with_def (true), - m_paths_relative_to_cwd (false) + m_paths_relative_to_cwd (false), + m_lef_context_enabled (false) { // .. nothing yet .. } @@ -951,26 +952,32 @@ LEFDEFReaderOptions::special_routing_datatype_str () const return get_datatypes (this, &LEFDEFReaderOptions::special_routing_datatype, &LEFDEFReaderOptions::special_routing_datatype_per_mask, max_mask_number ()); } +void +LEFDEFReaderOptions::set_lef_context_enabled (bool f) +{ + if (f != m_lef_context_enabled) { + mp_reader_state.reset (0); + } +} + +db::LEFDEFReaderState * +LEFDEFReaderOptions::reader_state (db::Layout &layout, const std::string &base_path, const db::LoadLayoutOptions &options) const +{ + if (m_lef_context_enabled && ! mp_reader_state.get ()) { + mp_reader_state.reset (new db::LEFDEFReaderState (this)); + mp_reader_state->init (layout, base_path, options); + } + + return mp_reader_state.get (); +} + // ----------------------------------------------------------------------------------- // LEFDEFLayerDelegate implementation -LEFDEFReaderState::LEFDEFReaderState (const LEFDEFReaderOptions *tc, db::Layout &layout, const std::string &base_path) +LEFDEFReaderState::LEFDEFReaderState (const LEFDEFReaderOptions *tc) : mp_importer (0), m_create_layers (true), m_has_explicit_layer_mapping (false), m_laynum (1), mp_tech_comp (tc) { - if (! tc) { - - // use default options - - } else if (! tc->map_file ().empty ()) { - - read_map_file (tc->map_file (), layout, base_path); - - } else { - - m_layer_map = tc->layer_map (); - m_create_layers = tc->read_all_layers (); - - } + // .. nothing yet .. } LEFDEFReaderState::~LEFDEFReaderState () @@ -988,6 +995,57 @@ LEFDEFReaderState::~LEFDEFReaderState () m_macro_generators.clear (); } +void +LEFDEFReaderState::init (Layout &layout, const std::string &base_path, const LoadLayoutOptions &options) +{ + if (! mp_tech_comp) { + + // use default options + + } else if (! mp_tech_comp->map_file ().empty ()) { + + read_map_file (mp_tech_comp->map_file (), layout, base_path); + + } else { + + m_layer_map = mp_tech_comp->layer_map (); + m_create_layers = mp_tech_comp->read_all_layers (); + + } + + if (mp_tech_comp) { + + m_macro_layouts = mp_tech_comp->macro_layouts (); + + // Additionally read the layouts from the given paths + for (std::vector::const_iterator l = mp_tech_comp->begin_macro_layout_files (); l != mp_tech_comp->end_macro_layout_files (); ++l) { + + auto paths = correct_path (*l, layout, base_path, true); + for (auto lp = paths.begin (); lp != paths.end (); ++lp) { + + tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF macro layout file: ")) + *lp); + + tl::InputStream macro_layout_stream (*lp); + tl::log << tl::to_string (tr ("Reading")) << " " << *lp; + db::Layout *new_layout = new db::Layout (false); + m_macro_layout_object_holder.push_back (new_layout); + m_macro_layouts.push_back (new_layout); + + db::Reader reader (macro_layout_stream); + reader.read (*new_layout, options); + + if (fabs (new_layout->dbu () / layout.dbu () - 1.0) > db::epsilon) { + tl::warn << tl::sprintf (tl::to_string (tr ("DBU of macro layout file '%s' does not match reader DBU (layout DBU is %.12g, reader DBU is set to %.12g)")), + *lp, new_layout->dbu (), layout.dbu ()); + } + + } + + } + + } +} + void LEFDEFReaderState::error (const std::string &msg) { diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h index 2a5736f0e..9fa0e6d04 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h @@ -960,6 +960,25 @@ public: m_macro_layout_files = lf; } + /** + * @brief A hidden attribute to enable a LEF context + * LEF context are used for chaining DEF readers. This method must be + * called on local copy of the importer options. With this attribute + * set to "true", the client code can store a LEFDEFReaderState object + * in this object. + * Initially, this attribute is set to false. + * Note that this attribute is not copied. + */ + void set_lef_context_enabled (bool context); + + bool lef_context_enabled () const + { + return m_lef_context_enabled; + } + + // makes the reader state object if required + db::LEFDEFReaderState *reader_state (db::Layout &layout, const std::string &base_path, const LoadLayoutOptions &options) const; + private: bool m_read_all_layers; db::LayerMap m_layer_map; @@ -1028,6 +1047,8 @@ private: tl::weak_collection m_macro_layouts; std::vector m_macro_layout_files; bool m_paths_relative_to_cwd; + bool m_lef_context_enabled; + mutable std::unique_ptr mp_reader_state; }; /** @@ -1247,13 +1268,18 @@ public: /** * @brief Constructor */ - LEFDEFReaderState (const LEFDEFReaderOptions *tc, db::Layout &layout, const std::string &base_path = std::string ()); + LEFDEFReaderState (const LEFDEFReaderOptions *tc); /** * @brief Destructor */ ~LEFDEFReaderState (); + /** + * @brief Initialize with the layout and base path + */ + void init (db::Layout &layout, const std::string &base_path, const LoadLayoutOptions &options); + /** * @brief Attaches to or detaches from an importer */ @@ -1364,6 +1390,31 @@ public: */ void warn (const std::string &msg, int warn_level = 1); + /** + * @brief Gets a value indicating whether the given LEF file was already read + */ + bool lef_file_already_read (const std::string &fn) + { + return m_lef_files_read.find (fn) != m_lef_files_read.end (); + } + + /** + * @brief Registers a LEF file + * After registration, the same file will report "already_read" + */ + void register_lef_file (const std::string &fn) + { + m_lef_files_read.insert (fn); + } + + /** + * @brief Gets the stored macro layouts + */ + const std::vector ¯o_layouts () const + { + return m_macro_layouts; + } + protected: virtual void common_reader_error (const std::string &msg) { error (msg); } virtual void common_reader_warn (const std::string &msg, int warn_level) { warn (msg, warn_level); } @@ -1456,6 +1507,9 @@ private: std::map > m_macro_cells; std::map m_macro_generators; std::map m_foreign_cells; + std::set m_lef_files_read; + std::vector m_macro_layouts; + tl::shared_collection m_macro_layout_object_holder; std::set open_layer_uncached (db::Layout &layout, const std::string &name, LayerPurpose purpose, unsigned int mask); db::cell_index_type foreign_cell(Layout &layout, const std::string &name); diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc index 06e42a0c7..0985b0531 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc @@ -113,23 +113,36 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti init (options); const db::LEFDEFReaderOptions *lefdef_options = dynamic_cast (options.get_options (format ())); + db::LEFDEFReaderOptions effective_options; if (lefdef_options) { effective_options = *lefdef_options; } + layout.dbu (effective_options.dbu ()); + std::string base_path; if (! effective_options.paths_relative_to_cwd ()) { base_path = tl::dirname (m_stream.absolute_file_path ()); } - db::LEFDEFReaderState state (&effective_options, layout, base_path); + // If the LEF reader context (LEF caching) is enabled on the options, + // pull the state from there, otherwise create a local state. + db::LEFDEFReaderState local_state (&effective_options); + db::LEFDEFReaderState *state = 0; + if (lefdef_options) { + state = lefdef_options->reader_state (layout, base_path, options); + } + if (! state) { + local_state.init (layout, base_path, options); + state = &local_state; + } + // Configure the conflict resolution mode db::CommonReaderOptions common_options = options.get_options (); - state.set_conflict_resolution_mode (common_options.cell_conflict_resolution); - - layout.dbu (effective_options.dbu ()); + state->set_conflict_resolution_mode (common_options.cell_conflict_resolution); + // Import LEF if (import_lef) { // Always produce LEF geometry when reading LEF @@ -145,13 +158,13 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti for (auto lp = paths.begin (); lp != paths.end (); ++lp) { tl::InputStream lef_stream (*lp); tl::log << tl::to_string (tr ("Reading")) << " " << *lp; - importer.read (lef_stream, layout, state); + importer.read (lef_stream, layout, *state); } } tl::log << tl::to_string (tr ("Reading")) << " " << m_stream.source (); - importer.read (m_stream, layout, state); + importer.read (m_stream, layout, *state); importer.finish_lef (layout); @@ -161,20 +174,21 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti DEFImporter importer (warn_level ()); - std::set lef_files_read; - for (std::vector::const_iterator l = effective_options.begin_lef_files (); l != effective_options.end_lef_files (); ++l) { auto paths = correct_path (*l, layout, base_path, true); for (auto lp = paths.begin (); lp != paths.end (); ++lp) { - if (lef_files_read.insert (tl::normalize_path (*lp)).second) { + std::string norm_lp = tl::normalize_path (*lp); + if (! state->lef_file_already_read (norm_lp)) { tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF file: ")) + *lp); - tl::InputStream lef_stream (*lp); + tl::InputStream lef_stream (norm_lp); tl::log << tl::to_string (tr ("Reading")) << " " << *lp; - importer.read_lef (lef_stream, layout, state); + importer.read_lef (lef_stream, layout, *state); + + state->register_lef_file (norm_lp); } @@ -198,13 +212,16 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti std::string lp = tl::combine_path (input_dir, *e); // skip if already read (issue-1724) - if (lef_files_read.find (tl::normalize_path (lp)) == lef_files_read.end ()) { + std::string norm_lp = tl::normalize_path (lp); + if (! state->lef_file_already_read (norm_lp)) { tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF file: ")) + lp); - tl::InputStream lef_stream (lp); + tl::InputStream lef_stream (norm_lp); tl::log << tl::to_string (tr ("Reading")) << " " << lp; - importer.read_lef (lef_stream, layout, state); + importer.read_lef (lef_stream, layout, *state); + + state->register_lef_file (norm_lp); } @@ -217,43 +234,14 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti } tl::log << tl::to_string (tr ("Reading")) << " " << m_stream.source (); - importer.read (m_stream, layout, state); + importer.read (m_stream, layout, *state); // Resolve unresolved COMPONENT cells - std::map foreign_cells = state.foreign_cells (); + std::map foreign_cells = state->foreign_cells (); db::cell_index_type seen = std::numeric_limits::max (); - std::vector macro_layouts = effective_options.macro_layouts (); - - // Additionally read the layouts from the given paths - tl::shared_collection macro_layout_object_holder; - for (std::vector::const_iterator l = effective_options.begin_macro_layout_files (); l != effective_options.end_macro_layout_files (); ++l) { - - auto paths = correct_path (*l, layout, base_path, true); - for (auto lp = paths.begin (); lp != paths.end (); ++lp) { - - tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF macro layout file: ")) + *lp); - - tl::InputStream macro_layout_stream (*lp); - tl::log << tl::to_string (tr ("Reading")) << " " << *lp; - db::Layout *new_layout = new db::Layout (false); - macro_layout_object_holder.push_back (new_layout); - macro_layouts.push_back (new_layout); - - db::Reader reader (macro_layout_stream); - reader.read (*new_layout, options); - - if (fabs (new_layout->dbu () / layout.dbu () - 1.0) > db::epsilon) { - importer.warn (tl::sprintf (tl::to_string (tr ("DBU of macro layout file '%s' does not match reader DBU (layout DBU is %.12g, reader DBU is set to %.12g)")), - *lp, new_layout->dbu (), layout.dbu ())); - } - - } - - } - - for (std::vector::const_iterator m = macro_layouts.begin (); m != macro_layouts.end (); ++m) { + for (std::vector::const_iterator m = state->macro_layouts ().begin (); m != state->macro_layouts ().end (); ++m) { std::vector target_cells, source_cells; @@ -286,9 +274,9 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti } - state.finish (layout); + state->finish (layout); - m_layer_map = state.layer_map (); + m_layer_map = state->layer_map (); return m_layer_map; } diff --git a/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc b/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc index 032ad31eb..b2dfae449 100644 --- a/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc +++ b/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc @@ -997,6 +997,10 @@ gsi::Class decl_lefdef_config ("db", "LEFDEFReaderConfi "See \\read_lef_with_def for details about this property.\n" "\n" "This property has been added in version 0.27.\n" + ) + + // special attribute to implement LEF caching + gsi::method ("lef_context_enabled=", &db::LEFDEFReaderOptions::set_lef_context_enabled, + "@hide\n" ), "@brief Detailed LEF/DEF reader options\n" "This class is a aggregate belonging to the \\LoadLayoutOptions class. It provides options for the LEF/DEF reader. " diff --git a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc index 7e128f9c7..a493959b6 100644 --- a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc +++ b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc @@ -59,7 +59,9 @@ static db::LayerMap read (db::Layout &layout, const char *lef_dir, const char *f tl::Extractor ex (filename); - db::LEFDEFReaderState ld (&options, layout, fn_path); + db::LoadLayoutOptions other_options; + db::LEFDEFReaderState ld (&options); + ld.init (layout, fn_path, other_options); ld.set_conflict_resolution_mode (cc_mode); db::DEFImporter imp; From 1b98efd7a854af35c89fcbb115867c24189453b7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 15 May 2025 23:41:23 +0200 Subject: [PATCH 206/392] First bug fixes --- src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc index c2d8c253e..bb93be064 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc @@ -605,7 +605,8 @@ LEFDEFReaderOptions::LEFDEFReaderOptions () } LEFDEFReaderOptions::LEFDEFReaderOptions (const LEFDEFReaderOptions &d) - : db::FormatSpecificReaderOptions () + : db::FormatSpecificReaderOptions (), + m_lef_context_enabled (false) { operator= (d); } From 1c4077449b9dd69d63c98173e4dd1c87245f097f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 16 May 2025 18:55:55 +0200 Subject: [PATCH 207/392] Fixed build issue --- src/pex/pex/gsiDeclRNetExtractor.cc | 8 ++++---- testdata/ruby/pexTests.rb | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/pex/pex/gsiDeclRNetExtractor.cc b/src/pex/pex/gsiDeclRNetExtractor.cc index 3fff2dad5..7ba3c1b54 100644 --- a/src/pex/pex/gsiDeclRNetExtractor.cc +++ b/src/pex/pex/gsiDeclRNetExtractor.cc @@ -264,12 +264,12 @@ static void tech_set_skip_simplify (pex::RExtractorTech *tech, bool f) tech->skip_simplify = f; } -static auto tech_begin_vias (const pex::RExtractorTech *tech) +static std::list::const_iterator tech_begin_vias (const pex::RExtractorTech *tech) { return tech->vias.begin (); } -static auto tech_end_vias (const pex::RExtractorTech *tech) +static std::list::const_iterator tech_end_vias (const pex::RExtractorTech *tech) { return tech->vias.end (); } @@ -284,12 +284,12 @@ static void tech_add_via (pex::RExtractorTech *tech, const pex::RExtractorTechVi tech->vias.push_back (via); } -static auto tech_begin_conductors (const pex::RExtractorTech *tech) +static std::list::const_iterator tech_begin_conductors (const pex::RExtractorTech *tech) { return tech->conductors.begin (); } -static auto tech_end_conductors (const pex::RExtractorTech *tech) +static std::list::const_iterator tech_end_conductors (const pex::RExtractorTech *tech) { return tech->conductors.end (); } diff --git a/testdata/ruby/pexTests.rb b/testdata/ruby/pexTests.rb index 477f07ed3..e5e98a878 100644 --- a/testdata/ruby/pexTests.rb +++ b/testdata/ruby/pexTests.rb @@ -250,6 +250,7 @@ R $2.1(9.3,-5.9;9.9,-5.3) $8.2(10,-3.5;10,-2.7) 13.2813 R $8.2(10,-3.5;10,-2.7) V0.1(5.2,0.4;5.2,0.4) 28.7812 R V0.1(5.2,0.4;5.2,0.4) V0.2(0.3,-5.7;0.5,-5.5) 17.375 END + end end From 113c7013451952113bdbb63eb52e2ff68a37e4cf Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 16 May 2025 23:23:11 +0200 Subject: [PATCH 208/392] Trying explicit member template instantiation to solve linker issue --- src/db/db/dbPLCTriangulation.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/db/db/dbPLCTriangulation.cc b/src/db/db/dbPLCTriangulation.cc index 70f784f3a..c1453f3dc 100644 --- a/src/db/db/dbPLCTriangulation.cc +++ b/src/db/db/dbPLCTriangulation.cc @@ -1379,6 +1379,9 @@ Triangulation::make_contours (const Poly &poly, const Trans &trans, std::vector< } } +template DB_PUBLIC void Triangulation::make_contours (const db::Polygon &, const db::CplxTrans &, std::vector > &); +template DB_PUBLIC void Triangulation::make_contours (const db::DPolygon &, const db::DCplxTrans &, std::vector > &); + void Triangulation::create_constrained_delaunay (const db::Region ®ion, const CplxTrans &trans) { From 5c27fedfe524c743a0e38ae25729e2331f0d0665 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 17 May 2025 00:44:15 +0200 Subject: [PATCH 209/392] Debugging and bug fixes. The 'big example' from issue 2014 now works --- src/db/db/dbCommonReader.cc | 9 ++- src/db/db/dbCommonReader.h | 2 +- .../lefdef/db_plugin/dbDEFImporter.cc | 39 +++------ .../lefdef/db_plugin/dbDEFImporter.h | 16 +--- .../lefdef/db_plugin/dbLEFDEFImporter.cc | 79 ++++++++++++++++++- .../lefdef/db_plugin/dbLEFDEFImporter.h | 31 ++++++++ .../lefdef/db_plugin/dbLEFDEFPlugin.cc | 43 ++-------- .../lefdef/unit_tests/dbLEFDEFImportTests.cc | 7 +- 8 files changed, 139 insertions(+), 87 deletions(-) diff --git a/src/db/db/dbCommonReader.cc b/src/db/db/dbCommonReader.cc index bf21fa5fc..b1becdcfb 100644 --- a/src/db/db/dbCommonReader.cc +++ b/src/db/db/dbCommonReader.cc @@ -314,8 +314,13 @@ CommonReaderBase::merge_cell_without_instances (db::Layout &layout, db::cell_ind } void -CommonReaderBase::init () +CommonReaderBase::start () { + m_id_map.clear (); + m_name_map.clear (); + m_temp_cells.clear (); + m_name_for_id.clear (); + m_layer_map_out.clear (); m_multi_mapping_placeholders.clear (); m_layer_cache.clear (); @@ -621,7 +626,7 @@ void CommonReader::init (const LoadLayoutOptions &options) { ReaderBase::init (options); - CommonReaderBase::init (); + CommonReaderBase::start (); db::CommonReaderOptions common_options = options.get_options (); set_conflict_resolution_mode (common_options.cell_conflict_resolution); diff --git a/src/db/db/dbCommonReader.h b/src/db/db/dbCommonReader.h index 7433cafe3..1a7dafcdf 100644 --- a/src/db/db/dbCommonReader.h +++ b/src/db/db/dbCommonReader.h @@ -207,7 +207,7 @@ public: /** * @brief Re-initialize: clears the tables and caches */ - void init (); + void start (); /** * @brief Sets a value indicating whether to create layers diff --git a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc index b91bd458f..3aa4d79c7 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc @@ -53,24 +53,11 @@ struct DEFImporterGroup }; DEFImporter::DEFImporter (int warn_level) - : LEFDEFImporter (warn_level), - m_lef_importer (warn_level) + : LEFDEFImporter (warn_level) { // .. nothing yet .. } -void -DEFImporter::read_lef (tl::InputStream &stream, db::Layout &layout, LEFDEFReaderState &state) -{ - m_lef_importer.read (stream, layout, state); -} - -void -DEFImporter::finish_lef (db::Layout &layout) -{ - m_lef_importer.finish_lef (layout); -} - void DEFImporter::read_polygon (db::Polygon &poly, double scale) { @@ -112,7 +99,7 @@ DEFImporter::read_rect (db::Polygon &poly, double scale) std::pair DEFImporter::get_wire_width_for_rule (const std::string &rulename, const std::string &ln, double dbu) { - std::pair wxy = m_lef_importer.layer_width (ln, rulename); + std::pair wxy = reader_state ()->lef_importer ().layer_width (ln, rulename); db::Coord wx = db::coord_traits::rounded (wxy.first / dbu); db::Coord wy = db::coord_traits::rounded (wxy.second / dbu); @@ -127,7 +114,7 @@ DEFImporter::get_wire_width_for_rule (const std::string &rulename, const std::st } } - std::pair min_wxy = m_lef_importer.min_layer_width (ln); + std::pair min_wxy = reader_state ()->lef_importer ().min_layer_width (ln); db::Coord min_wx = db::coord_traits::rounded (min_wxy.first / dbu); db::Coord min_wy = db::coord_traits::rounded (min_wxy.second / dbu); @@ -769,7 +756,7 @@ DEFImporter::read_single_net (std::string &nondefaultrule, Layout &layout, db::C unsigned int mask_cut = (mask / 10) % 10; unsigned int mask_bottom = mask % 10; - db::Cell *cell = reader_state ()->via_cell (vn, nondefaultrule, layout, mask_bottom, mask_cut, mask_top, &m_lef_importer); + db::Cell *cell = reader_state ()->via_cell (vn, nondefaultrule, layout, mask_bottom, mask_cut, mask_top, &reader_state ()->lef_importer ()); if (cell) { if (nx <= 1 && ny <= 1) { design.insert (db::CellInstArray (db::CellInst (cell->cell_index ()), db::Trans (ft.rot (), db::Vector (pts.back ())))); @@ -953,7 +940,7 @@ DEFImporter::read_nets (db::Layout &layout, db::Cell &design, double scale, bool std::map::const_iterator vd = m_via_desc.find (vn); if (vd != m_via_desc.end ()) { - db::Cell *cell = reader_state ()->via_cell (vn, nondefaultrule, layout, mask_bottom, mask_cut, mask_top, &m_lef_importer); + db::Cell *cell = reader_state ()->via_cell (vn, nondefaultrule, layout, mask_bottom, mask_cut, mask_top, &reader_state ()->lef_importer ()); if (cell) { design.insert (db::CellInstArray (db::CellInst (cell->cell_index ()), db::Trans (ft.rot (), pt))); } @@ -1130,7 +1117,7 @@ DEFImporter::read_vias (db::Layout &layout, db::Cell & /*design*/, double scale) std::string ln = get (); - if (m_lef_importer.is_routing_layer (ln)) { + if (reader_state ()->lef_importer ().is_routing_layer (ln)) { if (seen_layers.find (ln) == seen_layers.end ()) { @@ -1145,7 +1132,7 @@ DEFImporter::read_vias (db::Layout &layout, db::Cell & /*design*/, double scale) } - } else if (m_lef_importer.is_cut_layer (ln)) { + } else if (reader_state ()->lef_importer ().is_cut_layer (ln)) { geo_based_vg->set_maskshift_layer (1, ln); has_cut_geometry = true; @@ -1336,7 +1323,7 @@ DEFImporter::read_pins (db::Layout &layout, db::Cell &design, double scale) std::map::const_iterator vd = m_via_desc.find (vn); if (vd != m_via_desc.end ()) { std::string nondefaultrule; - db::Cell *cell = reader_state ()->via_cell (vn, nondefaultrule, layout, mask_bottom, mask_cut, mask_top, &m_lef_importer); + db::Cell *cell = reader_state ()->via_cell (vn, nondefaultrule, layout, mask_bottom, mask_cut, mask_top, &reader_state ()->lef_importer ()); if (cell) { design.insert (db::CellInstArray (db::CellInst (cell->cell_index ()), db::Trans (pt))); } @@ -1549,7 +1536,7 @@ DEFImporter::read_fills (db::Layout &layout, db::Cell &design, double scale) std::map::const_iterator vd = m_via_desc.find (vn); if (vd != m_via_desc.end ()) { std::string nondefaultrule; - db::Cell *cell = reader_state ()->via_cell (vn, nondefaultrule, layout, mask_bottom, mask_cut, mask_top, &m_lef_importer); + db::Cell *cell = reader_state ()->via_cell (vn, nondefaultrule, layout, mask_bottom, mask_cut, mask_top, &reader_state ()->lef_importer ()); if (cell) { ensure_fill_cell (layout, design, fill_cell).insert (db::CellInstArray (db::CellInst (cell->cell_index ()), db::Trans (pt))); } @@ -1613,8 +1600,8 @@ DEFImporter::read_components (db::Layout &layout, std::list::const_iterator m = m_lef_importer.macros ().find (model); - if (m == m_lef_importer.macros ().end ()) { + std::map::const_iterator m = reader_state ()->lef_importer ().macros ().find (model); + if (m == reader_state ()->lef_importer ().macros ().end ()) { error (tl::to_string (tr ("Macro not found in LEF file: ")) + model); } @@ -1660,7 +1647,7 @@ DEFImporter::read_components (db::Layout &layout, std::list ct = reader_state ()->macro_cell (model, layout, m_component_maskshift, string2masks (maskshift), m->second, &m_lef_importer); + std::pair ct = reader_state ()->macro_cell (model, layout, m_component_maskshift, string2masks (maskshift), m->second, &reader_state ()->lef_importer ()); if (ct.first) { db::CellInstArray inst (db::CellInst (ct.first->cell_index ()), db::Trans (ft.rot (), d) * ct.second); instances.push_back (std::make_pair (inst_name, inst)); @@ -1684,7 +1671,7 @@ DEFImporter::do_read (db::Layout &layout) std::list groups; std::list > instances; - m_via_desc = m_lef_importer.vias (); + m_via_desc = reader_state ()->lef_importer ().vias (); m_styles.clear (); m_design_name.clear (); diff --git a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.h b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.h index 0b824e403..56e7568f8 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.h +++ b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.h @@ -48,26 +48,12 @@ public: /** * @brief Default constructor */ - DEFImporter (int warn_level = 1); - - /** - * @brief Read the given LEF file prior to the DEF file - * - * This method reads the layout specified into the given layout. - * Multiple LEF files can be read. - */ - void read_lef (tl::InputStream &stream, db::Layout &layout, LEFDEFReaderState &state); - - /** - * @brief Provided for test purposes - */ - void finish_lef (Layout &layout); + DEFImporter (int warn_level); protected: void do_read (db::Layout &layout); private: - LEFImporter m_lef_importer; std::map > m_nondefault_widths; std::map m_via_desc; std::map m_styles; diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc index bb93be064..8776f6827 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc @@ -22,9 +22,11 @@ #include "dbLEFDEFImporter.h" +#include "dbLEFImporter.h" #include "dbLayoutUtils.h" #include "dbTechnology.h" #include "dbShapeProcessor.h" +#include "dbCellMapping.h" #include "tlStream.h" #include "tlProgress.h" @@ -958,6 +960,7 @@ LEFDEFReaderOptions::set_lef_context_enabled (bool f) { if (f != m_lef_context_enabled) { mp_reader_state.reset (0); + m_lef_context_enabled = f; } } @@ -1036,8 +1039,8 @@ LEFDEFReaderState::init (Layout &layout, const std::string &base_path, const Loa reader.read (*new_layout, options); if (fabs (new_layout->dbu () / layout.dbu () - 1.0) > db::epsilon) { - tl::warn << tl::sprintf (tl::to_string (tr ("DBU of macro layout file '%s' does not match reader DBU (layout DBU is %.12g, reader DBU is set to %.12g)")), - *lp, new_layout->dbu (), layout.dbu ()); + warn (tl::sprintf (tl::to_string (tr ("DBU of macro layout file '%s' does not match reader DBU (layout DBU is %.12g, reader DBU is set to %.12g)")), + *lp, new_layout->dbu (), layout.dbu ())); } } @@ -1063,6 +1066,33 @@ LEFDEFReaderState::warn (const std::string &msg, int warn_level) } } +void +LEFDEFReaderState::ensure_lef_importer (int warn_level) +{ + if (! mp_lef_importer.get ()) { + mp_lef_importer.reset (new db::LEFImporter (warn_level)); + } +} + +db::LEFImporter & +LEFDEFReaderState::lef_importer () +{ + tl_assert (mp_lef_importer.get () != 0); + return *mp_lef_importer; +} + +void +LEFDEFReaderState::read_lef (tl::InputStream &stream, db::Layout &layout) +{ + lef_importer ().read (stream, layout, *this); +} + +void +LEFDEFReaderState::finish_lef (db::Layout &layout) +{ + lef_importer ().finish_lef (layout); +} + void LEFDEFReaderState::register_layer (const std::string &ln) { @@ -1774,11 +1804,56 @@ std::set LEFDEFReaderState::open_layer_uncached(db::Layout &layout } } +void +LEFDEFReaderState::start () +{ + CommonReaderBase::start (); + + m_foreign_cells.clear (); +} + void LEFDEFReaderState::finish (db::Layout &layout) { CommonReaderBase::finish (layout); + // Resolve unresolved COMPONENT cells + + db::cell_index_type seen = std::numeric_limits::max (); + + for (std::vector::const_iterator m = macro_layouts ().begin (); m != macro_layouts ().end (); ++m) { + + std::vector target_cells, source_cells; + + // collect the cells to pull in + for (std::map::iterator f = m_foreign_cells.begin (); f != m_foreign_cells.end (); ++f) { + if (f->second != seen) { + std::pair cp = (*m)->cell_by_name (f->first.c_str ()); + if (cp.first) { + target_cells.push_back (f->second); + source_cells.push_back (cp.second); + layout.cell (f->second).set_ghost_cell (false); + f->second = seen; + } + } + } + + db::CellMapping cm; + cm.create_multi_mapping_full (layout, target_cells, **m, source_cells); + layout.copy_tree_shapes (**m, cm); + + } + + // Warn about cells that could not be resolved + for (std::map::iterator f = m_foreign_cells.begin (); f != m_foreign_cells.end (); ++f) { + if (f->second != seen && layout.cell (f->second).is_ghost_cell ()) { + warn (tl::sprintf (tl::to_string (tr ("Could not find a substitution layout for foreign cell '%s'")), + f->first)); + } + } + + // Create the layers + int lnum = 0; std::set used_numbers; diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h index 9fa0e6d04..fc75c5c63 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h @@ -46,6 +46,7 @@ namespace db class LEFDEFReaderState; class LEFDEFImporter; +class LEFImporter; struct MacroDesc; /** @@ -1325,8 +1326,15 @@ public: */ void register_layer (const std::string &l); + /** + * @brief Start reading a file + * After the file is read, "finish" needs to be called. + */ + void start (); + /** * @brief Finish, i.e. assign GDS layer numbers to the layers + * This is the counterpart for "start". */ void finish (db::Layout &layout); @@ -1390,6 +1398,28 @@ public: */ void warn (const std::string &msg, int warn_level = 1); + /** + * @brief Ensures the LEF importer for DEF reading is available + */ + void ensure_lef_importer (int warn_level); + + /** + * @brief Gets the LEF importer for DEF reading + */ + db::LEFImporter &lef_importer (); + + /** + * @brief Reads a LEF file into the LEF importer + * + * Multiple LEF files can be read. + */ + void read_lef (tl::InputStream &stream, db::Layout &layout); + + /** + * @brief Provided for test purposes + */ + void finish_lef (db::Layout &layout); + /** * @brief Gets a value indicating whether the given LEF file was already read */ @@ -1510,6 +1540,7 @@ private: std::set m_lef_files_read; std::vector m_macro_layouts; tl::shared_collection m_macro_layout_object_holder; + std::unique_ptr mp_lef_importer; std::set open_layer_uncached (db::Layout &layout, const std::string &name, LayerPurpose purpose, unsigned int mask); db::cell_index_type foreign_cell(Layout &layout, const std::string &name); diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc index 0985b0531..7dad8a35e 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc @@ -138,6 +138,9 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti state = &local_state; } + state->ensure_lef_importer (warn_level ()); + state->start (); + // Configure the conflict resolution mode db::CommonReaderOptions common_options = options.get_options (); state->set_conflict_resolution_mode (common_options.cell_conflict_resolution); @@ -186,7 +189,7 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti tl::InputStream lef_stream (norm_lp); tl::log << tl::to_string (tr ("Reading")) << " " << *lp; - importer.read_lef (lef_stream, layout, *state); + state->read_lef (lef_stream, layout); state->register_lef_file (norm_lp); @@ -219,7 +222,7 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti tl::InputStream lef_stream (norm_lp); tl::log << tl::to_string (tr ("Reading")) << " " << lp; - importer.read_lef (lef_stream, layout, *state); + state->read_lef (lef_stream, layout); state->register_lef_file (norm_lp); @@ -236,42 +239,6 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti tl::log << tl::to_string (tr ("Reading")) << " " << m_stream.source (); importer.read (m_stream, layout, *state); - // Resolve unresolved COMPONENT cells - - std::map foreign_cells = state->foreign_cells (); - db::cell_index_type seen = std::numeric_limits::max (); - - for (std::vector::const_iterator m = state->macro_layouts ().begin (); m != state->macro_layouts ().end (); ++m) { - - std::vector target_cells, source_cells; - - // collect the cells to pull in - for (std::map::iterator f = foreign_cells.begin (); f != foreign_cells.end (); ++f) { - if (f->second != seen) { - std::pair cp = (*m)->cell_by_name (f->first.c_str ()); - if (cp.first) { - target_cells.push_back (f->second); - source_cells.push_back (cp.second); - layout.cell (f->second).set_ghost_cell (false); - f->second = seen; - } - } - } - - db::CellMapping cm; - cm.create_multi_mapping_full (layout, target_cells, **m, source_cells); - layout.copy_tree_shapes (**m, cm); - - } - - // Warn about cells that could not be resolved - for (std::map::iterator f = foreign_cells.begin (); f != foreign_cells.end (); ++f) { - if (f->second != seen && layout.cell (f->second).is_ghost_cell ()) { - importer.warn (tl::sprintf (tl::to_string (tr ("Could not find a substitution layout for foreign cell '%s'")), - f->first)); - } - } - } state->finish (layout); diff --git a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc index a493959b6..a7448f1e7 100644 --- a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc +++ b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc @@ -61,10 +61,11 @@ static db::LayerMap read (db::Layout &layout, const char *lef_dir, const char *f db::LoadLayoutOptions other_options; db::LEFDEFReaderState ld (&options); + ld.ensure_lef_importer (1); ld.init (layout, fn_path, other_options); ld.set_conflict_resolution_mode (cc_mode); - db::DEFImporter imp; + db::DEFImporter imp (1); bool any_def = false; bool any_lef = false; @@ -95,7 +96,7 @@ static db::LayerMap read (db::Layout &layout, const char *lef_dir, const char *f fn += f; tl::InputStream stream (fn); - imp.read_lef (stream, layout, ld); + ld.read_lef (stream, layout); any_lef = true; @@ -136,7 +137,7 @@ static db::LayerMap read (db::Layout &layout, const char *lef_dir, const char *f } if (! any_def && any_lef) { - imp.finish_lef (layout); + ld.finish_lef (layout); } ld.finish (layout); From baed5bd822ae2816e615c388e1a515fcf3202519 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 17 May 2025 00:57:36 +0200 Subject: [PATCH 210/392] Small refactoring --- .../streamers/lefdef/db_plugin/dbLEFDEFImporter.cc | 5 ++++- .../streamers/lefdef/db_plugin/dbLEFDEFImporter.h | 11 +---------- .../streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc | 10 ++-------- .../lefdef/unit_tests/dbLEFDEFImportTests.cc | 3 +-- 4 files changed, 8 insertions(+), 21 deletions(-) diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc index 8776f6827..3059105da 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc @@ -1082,9 +1082,12 @@ LEFDEFReaderState::lef_importer () } void -LEFDEFReaderState::read_lef (tl::InputStream &stream, db::Layout &layout) +LEFDEFReaderState::read_lef (const std::string &fn, db::Layout &layout) { + tl::InputStream stream (fn); lef_importer ().read (stream, layout, *this); + + m_lef_files_read.insert (fn); } void diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h index fc75c5c63..ddb90415e 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h @@ -1413,7 +1413,7 @@ public: * * Multiple LEF files can be read. */ - void read_lef (tl::InputStream &stream, db::Layout &layout); + void read_lef (const std::string &fn, db::Layout &layout); /** * @brief Provided for test purposes @@ -1428,15 +1428,6 @@ public: return m_lef_files_read.find (fn) != m_lef_files_read.end (); } - /** - * @brief Registers a LEF file - * After registration, the same file will report "already_read" - */ - void register_lef_file (const std::string &fn) - { - m_lef_files_read.insert (fn); - } - /** * @brief Gets the stored macro layouts */ diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc index 7dad8a35e..e4e7dfb80 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc @@ -187,11 +187,8 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF file: ")) + *lp); - tl::InputStream lef_stream (norm_lp); tl::log << tl::to_string (tr ("Reading")) << " " << *lp; - state->read_lef (lef_stream, layout); - - state->register_lef_file (norm_lp); + state->read_lef (norm_lp, layout); } @@ -220,11 +217,8 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF file: ")) + lp); - tl::InputStream lef_stream (norm_lp); tl::log << tl::to_string (tr ("Reading")) << " " << lp; - state->read_lef (lef_stream, layout); - - state->register_lef_file (norm_lp); + state->read_lef (norm_lp, layout); } diff --git a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc index a7448f1e7..9ccb295a4 100644 --- a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc +++ b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc @@ -95,8 +95,7 @@ static db::LayerMap read (db::Layout &layout, const char *lef_dir, const char *f ex.read_word_or_quoted (f); fn += f; - tl::InputStream stream (fn); - ld.read_lef (stream, layout); + ld.read_lef (fn, layout); any_lef = true; From 4206d7ee634151669af79e85b2970cc0553c8b4e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 17 May 2025 01:00:57 +0200 Subject: [PATCH 211/392] Tried a performance improvement experiment, don't dare to actually use it --- src/buddies/src/bd/bdReaderOptions.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/buddies/src/bd/bdReaderOptions.cc b/src/buddies/src/bd/bdReaderOptions.cc index 091412826..958bdb723 100644 --- a/src/buddies/src/bd/bdReaderOptions.cc +++ b/src/buddies/src/bd/bdReaderOptions.cc @@ -846,6 +846,10 @@ static std::vector split_file_list (const std::string &infile) void read_files (db::Layout &layout, const std::string &infile, const db::LoadLayoutOptions &options) { + // We may do this: + // db::LayoutLocker locker (&layout); + // but there are yet unknown side effects + // enter a LEF caching context for chaining multiple DEF with the same LEF db::LoadLayoutOptions local_options (options); local_options.set_option_by_name ("lefdef_config.lef_context_enabled", true); From 8221923ccbbf6de5ed2ee7506411a4504fc58adb Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 17 May 2025 16:50:47 +0200 Subject: [PATCH 212/392] Added a large (private) testcase for the 'read LEF once' enhancement --- src/buddies/unit_tests/bdConverterTests.cc | 324 +++++++++++++++++++++ 1 file changed, 324 insertions(+) diff --git a/src/buddies/unit_tests/bdConverterTests.cc b/src/buddies/unit_tests/bdConverterTests.cc index eb8eef30d..4dfd7d20e 100644 --- a/src/buddies/unit_tests/bdConverterTests.cc +++ b/src/buddies/unit_tests/bdConverterTests.cc @@ -186,3 +186,327 @@ TEST(6) db::compare_layouts (this, layout, input_au, db::WriteGDS2); } + +// Large LEF/DEF to OAS converter test +TEST(10) +{ + test_is_long_runner (); + + std::string input_dir = tl::testdata_private (); + input_dir += "/lefdef/strm2oas/"; + + std::string lef_dir = input_dir + "/lef"; + std::string def_dir = input_dir + "/def"; + std::string gds_dir = input_dir + "/gds"; + + std::string input_au = input_dir + "/strm2oas_au.oas"; + + std::string output = this->tmp_file ("strm2oas.oas"); + std::string map_arg = "--lefdef-map=" + input_dir + "/sky130.map"; + + const char *lef_files[] = { + "sky130_fd_sc_hd.tlef", + "sky130_fd_sc_hd_merged.lef", + "sky130_fd_sc_hs_merged.lef", + "sky130_ef_sc_hd__decap_20_12.lef", + "sky130_ef_sc_hd__decap_80_12.lef", + "sky130_ef_sc_hd__fill_4.lef", + "sky130_ef_sc_hd__decap_40_12.lef", + "sky130_ef_sc_hd__decap_60_12.lef", + "sky130_ef_io__analog_esd_pad.lef", + "sky130_ef_io__analog_noesd_pad.lef", + "sky130_ef_io__analog_pad.lef", + "sky130_ef_io__bare_pad.lef", + "sky130_ef_io__com_bus_slice_10um.lef", + "sky130_ef_io__com_bus_slice_1um.lef", + "sky130_ef_io__com_bus_slice_20um.lef", + "sky130_ef_io__com_bus_slice_5um.lef", + "sky130_ef_io__connect_vcchib_vccd_and_vswitch_vddio_slice_20um.lef", + "sky130_ef_io__corner_pad.lef", + "sky130_ef_io__disconnect_vccd_slice_5um.lef", + "sky130_ef_io__disconnect_vdda_slice_5um.lef", + "sky130_ef_io__gpiov2_pad.lef", + "sky130_ef_io__gpiov2_pad_wrapped.lef", + "sky130_ef_io__top_power_hvc.lef", + "sky130_ef_io__vccd_hvc_pad.lef", + "sky130_ef_io__vccd_lvc_clamped2_pad.lef", + "sky130_ef_io__vccd_lvc_clamped3_pad.lef", + "sky130_ef_io__vccd_lvc_clamped_pad.lef", + "sky130_ef_io__vccd_lvc_pad.lef", + "sky130_ef_io__vdda_hvc_clamped_pad.lef", + "sky130_ef_io__vdda_hvc_pad.lef", + "sky130_ef_io__vdda_lvc_pad.lef", + "sky130_ef_io__vddio_hvc_clamped_pad.lef", + "sky130_ef_io__vddio_hvc_pad.lef", + "sky130_ef_io__vddio_lvc_pad.lef", + "sky130_ef_io__vssa_hvc_clamped_pad.lef", + "sky130_ef_io__vssa_hvc_pad.lef", + "sky130_ef_io__vssa_lvc_pad.lef", + "sky130_ef_io__vssd_hvc_pad.lef", + "sky130_ef_io__vssd_lvc_clamped2_pad.lef", + "sky130_ef_io__vssd_lvc_clamped3_pad.lef", + "sky130_ef_io__vssd_lvc_clamped_pad.lef", + "sky130_ef_io__vssd_lvc_pad.lef", + "sky130_ef_io__vssio_hvc_clamped_pad.lef", + "sky130_ef_io__vssio_hvc_pad.lef", + "sky130_ef_io__vssio_lvc_pad.lef", + "sky130_fd_io__signal_5_sym_hv_local_5term.lef", + "sky130_fd_io__top_gpiov2.lef", + "sky130_fd_io__top_power_hvc_wpadv2.lef", + "sky130_fd_sc_hvl__a21o_1.lef", + "sky130_fd_sc_hvl__a21oi_1.lef", + "sky130_fd_sc_hvl__a22o_1.lef", + "sky130_fd_sc_hvl__a22oi_1.lef", + "sky130_fd_sc_hvl__and2_1.lef", + "sky130_fd_sc_hvl__and3_1.lef", + "sky130_fd_sc_hvl__buf_1.lef", + "sky130_fd_sc_hvl__buf_16.lef", + "sky130_fd_sc_hvl__buf_2.lef", + "sky130_fd_sc_hvl__buf_32.lef", + "sky130_fd_sc_hvl__buf_4.lef", + "sky130_fd_sc_hvl__buf_8.lef", + "sky130_fd_sc_hvl__conb_1.lef", + "sky130_fd_sc_hvl__decap_4.lef", + "sky130_fd_sc_hvl__decap_8.lef", + "sky130_fd_sc_hvl__dfrbp_1.lef", + "sky130_fd_sc_hvl__dfrtp_1.lef", + "sky130_fd_sc_hvl__dfsbp_1.lef", + "sky130_fd_sc_hvl__dfstp_1.lef", + "sky130_fd_sc_hvl__dfxbp_1.lef", + "sky130_fd_sc_hvl__dfxtp_1.lef", + "sky130_fd_sc_hvl__diode_2.lef", + "sky130_fd_sc_hvl__dlclkp_1.lef", + "sky130_fd_sc_hvl__dlrtp_1.lef", + "sky130_fd_sc_hvl__dlxtp_1.lef", + "sky130_fd_sc_hvl__einvn_1.lef", + "sky130_fd_sc_hvl__einvp_1.lef", + "sky130_fd_sc_hvl__fill_1.lef", + "sky130_fd_sc_hvl__fill_2.lef", + "sky130_fd_sc_hvl__fill_4.lef", + "sky130_fd_sc_hvl__fill_8.lef", + "sky130_fd_sc_hvl__inv_1.lef", + "sky130_fd_sc_hvl__inv_16.lef", + "sky130_fd_sc_hvl__inv_2.lef", + "sky130_fd_sc_hvl__inv_4.lef", + "sky130_fd_sc_hvl__inv_8.lef", + "sky130_fd_sc_hvl__lsbufhv2hv_hl_1.lef", + "sky130_fd_sc_hvl__lsbufhv2hv_lh_1.lef", + "sky130_fd_sc_hvl__lsbufhv2lv_1.lef", + "sky130_fd_sc_hvl__lsbufhv2lv_simple_1.lef", + "sky130_fd_sc_hvl__lsbuflv2hv_1.lef", + "sky130_fd_sc_hvl__lsbuflv2hv_clkiso_hlkg_3.lef", + "sky130_fd_sc_hvl__lsbuflv2hv_isosrchvaon_1.lef", + "sky130_fd_sc_hvl__lsbuflv2hv_symmetric_1.lef", + "sky130_fd_sc_hvl__mux2_1.lef", + "sky130_fd_sc_hvl__mux4_1.lef", + "sky130_fd_sc_hvl__nand2_1.lef", + "sky130_fd_sc_hvl__nand3_1.lef", + "sky130_fd_sc_hvl__nor2_1.lef", + "sky130_fd_sc_hvl__nor3_1.lef", + "sky130_fd_sc_hvl__o21a_1.lef", + "sky130_fd_sc_hvl__o21ai_1.lef", + "sky130_fd_sc_hvl__o22a_1.lef", + "sky130_fd_sc_hvl__o22ai_1.lef", + "sky130_fd_sc_hvl__or2_1.lef", + "sky130_fd_sc_hvl__or3_1.lef", + "sky130_fd_sc_hvl__probe_p_8.lef", + "sky130_fd_sc_hvl__probec_p_8.lef", + "sky130_fd_sc_hvl__schmittbuf_1.lef", + "sky130_fd_sc_hvl__sdfrbp_1.lef", + "sky130_fd_sc_hvl__sdfrtp_1.lef", + "sky130_fd_sc_hvl__sdfsbp_1.lef", + "sky130_fd_sc_hvl__sdfstp_1.lef", + "sky130_fd_sc_hvl__sdfxbp_1.lef", + "sky130_fd_sc_hvl__sdfxtp_1.lef", + "sky130_fd_sc_hvl__sdlclkp_1.lef", + "sky130_fd_sc_hvl__sdlxtp_1.lef", + "sky130_fd_sc_hvl__xnor2_1.lef", + "sky130_fd_sc_hvl__xor2_1.lef", + "caravel.lef", + "caravel_clocking.lef", + "caravel_core.lef", + "gpio_defaults_block.lef", + "gpio_logic_high.lef", + "housekeeping.lef", + "mgmt_protect_hv.lef", + "mprj2_logic_high.lef", + "mprj_io_buffer.lef", + "mprj_logic_high.lef", + "spare_logic_block.lef", + "user_project_wrapper.lef", + "xres_buf.lef", + "caravel_logo-stub.lef", + "caravel_motto-stub.lef", + "chip_io.lef", + "copyright_block-stub.lef", + "empty_macro.lef", + "manual_power_connections.lef", + "open_source-stub.lef", + "simple_por.lef", + "user_id_programming.lef", + "user_id_textblock-stub.lef", + "RAM128.lef" + }; + + std::string lefs_arg = "--lefdef-lefs="; + for (size_t i = 0; i < sizeof (lef_files) / sizeof (lef_files[0]); ++i) { + if (i > 0) { + lefs_arg += ","; + } + lefs_arg += lef_dir + "/" + lef_files[i]; + } + + const char *lefdef_layout_files[] = { + "sky130_fd_sc_hd.gds", + "sky130_fd_sc_hvl__sdlxtp_1.gds", + "sky130_fd_sc_hvl__decap_8.gds", + "sky130_fd_sc_hvl__decap_4.gds", + "sky130_fd_sc_hvl__nand3_1.gds", + "sky130_fd_sc_hvl__sdfxbp_1.gds", + "sky130_fd_sc_hvl__lsbufhv2hv_hl_1.gds", + "sky130_fd_sc_hvl__sdfrbp_1.gds", + "sky130_fd_sc_hvl__a21o_1.gds", + "sky130_fd_sc_hvl__inv_2.gds", + "sky130_fd_sc_hvl__inv_16.gds", + "sky130_fd_sc_hvl__inv_1.gds", + "sky130_fd_sc_hvl__inv_4.gds", + "sky130_fd_sc_hvl__inv_8.gds", + "sky130_fd_sc_hvl__nand2_1.gds", + "sky130_fd_sc_hvl__dfstp_1.gds", + "sky130_fd_sc_hvl__a22o_1.gds", + "sky130_fd_sc_hvl__schmittbuf_1.gds", + "sky130_fd_sc_hvl__a22oi_1.gds", + "sky130_fd_sc_hvl__lsbuflv2hv_1.gds", + "sky130_fd_sc_hvl__fill_4.gds", + "sky130_fd_sc_hvl__fill_1.gds", + "sky130_fd_sc_hvl__fill_2.gds", + "sky130_fd_sc_hvl__fill_8.gds", + "sky130_fd_sc_hvl__sdfrtp_1.gds", + "sky130_fd_sc_hvl__sdfxtp_1.gds", + "sky130_fd_sc_hvl__o22a_1.gds", + "sky130_fd_sc_hvl__dfsbp_1.gds", + "sky130_fd_sc_hvl__o21a_1.gds", + "sky130_fd_sc_hvl__a21oi_1.gds", + "sky130_fd_sc_hvl__buf_1.gds", + "sky130_fd_sc_hvl__buf_2.gds", + "sky130_fd_sc_hvl__buf_4.gds", + "sky130_fd_sc_hvl__buf_32.gds", + "sky130_fd_sc_hvl__buf_16.gds", + "sky130_fd_sc_hvl__buf_8.gds", + "sky130_fd_sc_hvl__einvp_1.gds", + "sky130_fd_sc_hvl__conb_1.gds", + "sky130_fd_sc_hvl__and3_1.gds", + "sky130_fd_sc_hvl__lsbufhv2lv_1.gds", + "sky130_fd_sc_hvl__and2_1.gds", + "sky130_fd_sc_hvl__nor3_1.gds", + "sky130_fd_sc_hvl__dlclkp_1.gds", + "sky130_fd_sc_hvl__lsbuflv2hv_symmetric_1.gds", + "sky130_fd_sc_hvl__sdfstp_1.gds", + "sky130_fd_sc_hvl__dfrbp_1.gds", + "sky130_fd_sc_hvl__dfxbp_1.gds", + "sky130_fd_sc_hvl__nor2_1.gds", + "sky130_fd_sc_hvl__diode_2.gds", + "sky130_fd_sc_hvl__dlrtp_1.gds", + "sky130_fd_sc_hvl__dlxtp_1.gds", + "sky130_fd_sc_hvl__lsbufhv2lv_simple_1.gds", + "sky130_fd_sc_hvl__lsbuflv2hv_clkiso_hlkg_3.gds", + "sky130_fd_sc_hvl__sdlclkp_1.gds", + "sky130_fd_sc_hvl__o22ai_1.gds", + "sky130_fd_sc_hvl__or3_1.gds", + "sky130_fd_sc_hvl__sdfsbp_1.gds", + "sky130_fd_sc_hvl__xor2_1.gds", + "sky130_fd_sc_hvl__mux4_1.gds", + "sky130_fd_sc_hvl__or2_1.gds", + "sky130_fd_sc_hvl__probe_p_8.gds", + "sky130_fd_sc_hvl__dfxtp_1.gds", + "sky130_fd_sc_hvl__mux2_1.gds", + "sky130_fd_sc_hvl__dfrtp_1.gds", + "sky130_fd_sc_hvl__lsbuflv2hv_isosrchvaon_1.gds", + "sky130_fd_sc_hvl__probec_p_8.gds", + "sky130_fd_sc_hvl__xnor2_1.gds", + "sky130_fd_sc_hvl__einvn_1.gds", + "sky130_fd_sc_hvl__o21ai_1.gds", + "sky130_fd_sc_hvl__lsbufhv2hv_lh_1.gds", + "sky130_ef_io__analog.gds", + "sky130_ef_io__bare_pad.gds", + "sky130_ef_io__connect_vcchib_vccd_and_vswitch_vddio_slice_20um.gds", + "sky130_ef_io__disconnect_vccd_slice_5um.gds", + "sky130_ef_io__disconnect_vdda_slice_5um.gds", + "sky130_ef_io__gpiov2_pad_wrapped.gds", + "sky130_ef_sc_hd__decap_12.gds", + "sky130_ef_sc_hd__decap_20_12.gds", + "sky130_ef_sc_hd__decap_40_12.gds", + "sky130_ef_sc_hd__decap_60_12.gds", + "sky130_ef_sc_hd__decap_80_12.gds", + "sky130_ef_sc_hd__fill_12.gds", + "sky130_ef_sc_hd__fill_2.gds", + "sky130_ef_sc_hd__fill_4.gds", + "sky130_ef_sc_hd__fill_8.gds", + "sky130_ef_sc_hvl__fill_8.gds", + "caravel_logo.gds.gz", + "caravel_motto.gds.gz", + "chip_io.gds.gz", + "copyright_block.gds.gz", + "empty_macro.gds.gz", + "manual_power_connections.gds.gz", + "open_source.gds.gz", + "simple_por.gds.gz", + "user_id_programming.gds.gz", + "user_id_textblock.gds.gz", + "RAM128.gds.gz" + }; + + std::string lefdef_layouts_arg = "--lefdef-lef-layouts="; + for (size_t i = 0; i < sizeof (lefdef_layout_files) / sizeof (lefdef_layout_files[0]); ++i) { + if (i > 0) { + lefdef_layouts_arg += ","; + } + lefdef_layouts_arg += gds_dir + "/" + lefdef_layout_files[i]; + } + + const char *def_files[] = { + "caravel.def", + "caravel_clocking.def", + "caravel_core.def.gz", + "gpio_defaults_block.def", + "gpio_logic_high.def", + "housekeeping.def", + "mgmt_protect_hv.def", + "mprj2_logic_high.def", + "mprj_io_buffer.def", + "mprj_logic_high.def", + "spare_logic_block.def", + "user_project_wrapper.def", + "xres_buf.def" + }; + + std::string input; + for (size_t i = 0; i < sizeof (def_files) / sizeof (def_files[0]); ++i) { + if (i > 0) { + input += ","; + } + input += def_dir + "/" + def_files[i]; + } + + const char *argv[] = { "x", + "--lefdef-no-implicit-lef", + map_arg.c_str (), + lefs_arg.c_str (), + lefdef_layouts_arg.c_str (), + input.c_str (), + output.c_str () + }; + + EXPECT_EQ (bd::converter_main (sizeof (argv) / sizeof (argv[0]), (char **) argv, bd::GenericWriterOptions::oasis_format_name), 0); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::LoadLayoutOptions options; + db::Reader reader (stream); + reader.read (layout, options); + } + + db::compare_layouts (this, layout, input_au, db::WriteOAS); +} From 135030649b770984bf59d511b981da0c4d623e27 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 17 May 2025 17:15:04 +0200 Subject: [PATCH 213/392] Updating golden test data with new lvsdb files --- testdata/lvs/ringo_device_subcircuits.lvsdb.2 | 2 +- testdata/lvs/ringo_layout_var.lvsdb.2 | 2 +- testdata/lvs/ringo_simple.lvsdb.2 | 2 +- testdata/lvs/ringo_simple_compare2.lvsdb.2 | 2 +- .../lvs/ringo_simple_device_scaling.lvsdb.2 | 2 +- .../lvs/ringo_simple_dummy_device.lvsdb.2 | 24 +- .../ringo_simple_implicit_connections.lvsdb.2 | 2 +- testdata/lvs/ringo_simple_io.lvsdb.2 | 2 +- testdata/lvs/ringo_simple_io2.lvsdb.2 | 908 ------------------ ...simple_net_and_circuit_equivalence.lvsdb.2 | 2 +- .../lvs/ringo_simple_pin_swapping.lvsdb.2 | 2 +- .../ringo_simple_same_device_classes.lvsdb.2 | 2 +- .../lvs/ringo_simple_simplification.lvsdb.2 | 14 +- ...o_simple_simplification_with_align.lvsdb.2 | 14 +- testdata/lvs/ringo_simple_with_tol.lvsdb.2 | 2 +- .../lvs/ringo_simple_with_tol_early.lvsdb.2 | 2 +- testdata/lvs/test_22a.lvsdb.2 | 48 +- testdata/lvs/test_22b.lvsdb.2 | 48 +- 18 files changed, 86 insertions(+), 994 deletions(-) delete mode 100644 testdata/lvs/ringo_simple_io2.lvsdb.2 diff --git a/testdata/lvs/ringo_device_subcircuits.lvsdb.2 b/testdata/lvs/ringo_device_subcircuits.lvsdb.2 index 8eb0b266c..c74e25058 100644 --- a/testdata/lvs/ringo_device_subcircuits.lvsdb.2 +++ b/testdata/lvs/ringo_device_subcircuits.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 name($I3) + net(8 name($I5) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_layout_var.lvsdb.2 b/testdata/lvs/ringo_layout_var.lvsdb.2 index 124c4b7b3..2238cd24b 100644 --- a/testdata/lvs/ringo_layout_var.lvsdb.2 +++ b/testdata/lvs/ringo_layout_var.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 name($I3) + net(8 name($I5) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple.lvsdb.2 b/testdata/lvs/ringo_simple.lvsdb.2 index c8c4ded92..94c1f9904 100644 --- a/testdata/lvs/ringo_simple.lvsdb.2 +++ b/testdata/lvs/ringo_simple.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 name($I3) + net(8 name($I5) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_compare2.lvsdb.2 b/testdata/lvs/ringo_simple_compare2.lvsdb.2 index c8c4ded92..94c1f9904 100644 --- a/testdata/lvs/ringo_simple_compare2.lvsdb.2 +++ b/testdata/lvs/ringo_simple_compare2.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 name($I3) + net(8 name($I5) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_device_scaling.lvsdb.2 b/testdata/lvs/ringo_simple_device_scaling.lvsdb.2 index fb6b39193..6c89b4e06 100644 --- a/testdata/lvs/ringo_simple_device_scaling.lvsdb.2 +++ b/testdata/lvs/ringo_simple_device_scaling.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 name($I3) + net(8 name($I5) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_dummy_device.lvsdb.2 b/testdata/lvs/ringo_simple_dummy_device.lvsdb.2 index a6f463150..4abffb39a 100644 --- a/testdata/lvs/ringo_simple_dummy_device.lvsdb.2 +++ b/testdata/lvs/ringo_simple_dummy_device.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I5) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) @@ -361,41 +361,41 @@ layout( rect((0 350) (27600 7650)) # Nets with their geometries - net(1 + net(1 name($3) rect(l4 (26050 2800) (525 550)) rect(l4 (-525 -300) (300 300)) rect(l4 (-25 -2000) (250 1450)) rect(l8 (-465 310) (180 180)) rect(l11 (-240 -240) (300 300)) ) - net(2 + net(2 name($4) rect(l11 (4040 2950) (610 300)) ) - net(3 + net(3 name($5) rect(l11 (5550 2950) (900 300)) ) - net(4 + net(4 name($6) rect(l11 (7350 2950) (900 300)) ) - net(5 + net(5 name($7) rect(l11 (9150 2950) (900 300)) ) - net(6 + net(6 name($8) rect(l11 (10950 2950) (900 300)) ) - net(7 + net(7 name($9) rect(l11 (12750 2950) (900 300)) ) - net(8 + net(8 name($10) rect(l11 (14550 2950) (900 300)) ) - net(9 + net(9 name($11) rect(l11 (16350 2950) (900 300)) ) - net(10 + net(10 name($12) rect(l11 (18150 2950) (900 300)) ) - net(11 + net(11 name($13) rect(l11 (19950 2950) (900 300)) ) net(12 name(FB) diff --git a/testdata/lvs/ringo_simple_implicit_connections.lvsdb.2 b/testdata/lvs/ringo_simple_implicit_connections.lvsdb.2 index 97bf061e7..0657be4d3 100644 --- a/testdata/lvs/ringo_simple_implicit_connections.lvsdb.2 +++ b/testdata/lvs/ringo_simple_implicit_connections.lvsdb.2 @@ -202,7 +202,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 name($I3) + net(8 name($I5) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_io.lvsdb.2 b/testdata/lvs/ringo_simple_io.lvsdb.2 index c5c4ac6f1..3dd69d78c 100644 --- a/testdata/lvs/ringo_simple_io.lvsdb.2 +++ b/testdata/lvs/ringo_simple_io.lvsdb.2 @@ -176,7 +176,7 @@ J( R(l11 (-150 -150) (300 300)) ) N(7 I(SUBSTRATE)) - N(8 I($I3) + N(8 I($I5) R(l6 (975 1660) (425 950)) R(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_io2.lvsdb.2 b/testdata/lvs/ringo_simple_io2.lvsdb.2 deleted file mode 100644 index c8c4ded92..000000000 --- a/testdata/lvs/ringo_simple_io2.lvsdb.2 +++ /dev/null @@ -1,908 +0,0 @@ -#%lvsdb-klayout - -# Layout -layout( - top(RINGO) - unit(0.001) - - # Layer section - # This section lists the mask layers (drawing or derived) and their connections. - - # Mask layers - layer(l3 '1/0') - layer(l4 '5/0') - layer(l8 '8/0') - layer(l11 '9/0') - layer(l12 '10/0') - layer(l13 '11/0') - layer(l7) - layer(l2) - layer(l9) - layer(l6) - layer(l10) - - # Mask layer connectivity - connect(l3 l3 l9) - connect(l4 l4 l8) - connect(l8 l4 l8 l11 l2 l9 l6 l10) - connect(l11 l8 l11 l12) - connect(l12 l11 l12 l13) - connect(l13 l12 l13) - connect(l7 l7) - connect(l2 l8 l2) - connect(l9 l3 l8 l9) - connect(l6 l8 l6) - connect(l10 l8 l10) - - # Global nets and connectivity - global(l7 SUBSTRATE) - global(l10 SUBSTRATE) - - # Device class section - class(PMOS MOS4) - class(NMOS MOS4) - - # Device abstracts section - # Device abstracts list the pin shapes of the devices. - device(D$PMOS PMOS - terminal(S - rect(l2 (-550 -750) (425 1500)) - ) - terminal(G - rect(l4 (-125 -750) (250 1500)) - ) - terminal(D - rect(l2 (125 -750) (450 1500)) - ) - terminal(B - rect(l3 (-125 -750) (250 1500)) - ) - ) - device(D$PMOS$1 PMOS - terminal(S - rect(l2 (-575 -750) (450 1500)) - ) - terminal(G - rect(l4 (-125 -750) (250 1500)) - ) - terminal(D - rect(l2 (125 -750) (425 1500)) - ) - terminal(B - rect(l3 (-125 -750) (250 1500)) - ) - ) - device(D$PMOS$2 PMOS - terminal(S - rect(l2 (-550 -750) (425 1500)) - ) - terminal(G - rect(l4 (-125 -750) (250 1500)) - ) - terminal(D - rect(l2 (125 -750) (425 1500)) - ) - terminal(B - rect(l3 (-125 -750) (250 1500)) - ) - ) - device(D$NMOS NMOS - terminal(S - rect(l6 (-550 -475) (425 950)) - ) - terminal(G - rect(l4 (-125 -475) (250 950)) - ) - terminal(D - rect(l6 (125 -475) (450 950)) - ) - terminal(B - rect(l7 (-125 -475) (250 950)) - ) - ) - device(D$NMOS$1 NMOS - terminal(S - rect(l6 (-575 -475) (450 950)) - ) - terminal(G - rect(l4 (-125 -475) (250 950)) - ) - terminal(D - rect(l6 (125 -475) (425 950)) - ) - terminal(B - rect(l7 (-125 -475) (250 950)) - ) - ) - device(D$NMOS$2 NMOS - terminal(S - rect(l6 (-550 -475) (425 950)) - ) - terminal(G - rect(l4 (-125 -475) (250 950)) - ) - terminal(D - rect(l6 (125 -475) (425 950)) - ) - terminal(B - rect(l7 (-125 -475) (250 950)) - ) - ) - - # Circuit section - # Circuits are the hierarchical building blocks of the netlist. - circuit(ND2X1 - - # Circuit boundary - rect((-100 400) (2600 7600)) - - # Nets with their geometries - net(1 name(VDD) - rect(l8 (1110 5160) (180 180)) - rect(l8 (-180 920) (180 180)) - rect(l8 (-180 -730) (180 180)) - rect(l11 (-240 -790) (300 1700)) - rect(l11 (-1350 0) (2400 800)) - rect(l11 (-1150 -400) (0 0)) - rect(l2 (-275 -2150) (425 1500)) - rect(l2 (-400 -1500) (425 1500)) - ) - net(2 name(OUT) - rect(l8 (1810 1770) (180 180)) - rect(l8 (-180 370) (180 180)) - rect(l8 (-1580 3760) (180 180)) - rect(l8 (-180 -730) (180 180)) - rect(l8 (-180 -730) (180 180)) - rect(l8 (1220 920) (180 180)) - rect(l8 (-180 -1280) (180 180)) - rect(l8 (-180 370) (180 180)) - polygon(l11 (-240 -4180) (0 1390) (490 0) (0 -300) (-190 0) (0 -1090)) - rect(l11 (-110 1390) (300 1400)) - polygon(l11 (-1890 0) (0 600) (300 0) (0 -300) (1590 0) (0 -300)) - rect(l11 (-140 -500) (0 0)) - rect(l11 (-1750 1100) (300 1400)) - rect(l11 (1100 -1700) (300 300)) - rect(l11 (-300 0) (300 1400)) - rect(l2 (-375 -1450) (425 1500)) - rect(l2 (-1800 -1500) (425 1500)) - rect(l6 (950 -4890) (425 950)) - ) - net(3 name(VSS) - rect(l8 (410 1770) (180 180)) - rect(l8 (-180 370) (180 180)) - rect(l11 (-240 -1300) (300 1360)) - rect(l11 (-650 -2160) (2400 800)) - rect(l11 (-1150 -400) (0 0)) - rect(l6 (-950 860) (425 950)) - ) - net(4 - rect(l3 (-100 4500) (2600 3500)) - ) - net(5 name(B) - rect(l4 (1425 2860) (250 1940)) - rect(l4 (-345 -950) (300 300)) - rect(l4 (-205 650) (250 2000)) - rect(l4 (-250 -2000) (250 2000)) - rect(l4 (-250 -5390) (250 1450)) - rect(l8 (-285 1050) (180 180)) - rect(l11 (-70 -90) (0 0)) - rect(l11 (-170 -150) (300 300)) - ) - net(6 name(A) - rect(l4 (725 2860) (250 1940)) - rect(l4 (-325 -1850) (300 300)) - rect(l4 (-225 1550) (250 2000)) - rect(l4 (-250 -2000) (250 2000)) - rect(l4 (-250 -5390) (250 1450)) - rect(l8 (-265 150) (180 180)) - rect(l11 (-90 -90) (0 0)) - rect(l11 (-150 -150) (300 300)) - ) - net(7 name(SUBSTRATE)) - net(8 name($I3) - rect(l6 (975 1660) (425 950)) - rect(l6 (-400 -950) (425 950)) - ) - - # Outgoing pins and their connections to nets - pin(1 name(VDD)) - pin(2 name(OUT)) - pin(3 name(VSS)) - pin(4) - pin(5 name(B)) - pin(6 name(A)) - pin(7 name(SUBSTRATE)) - - # Devices and their connections - device(1 D$PMOS - location(850 5800) - param(L 0.25) - param(W 1.5) - param(AS 0.6375) - param(AD 0.3375) - param(PS 3.85) - param(PD 1.95) - terminal(S 2) - terminal(G 6) - terminal(D 1) - terminal(B 4) - ) - device(2 D$PMOS$1 - location(1550 5800) - param(L 0.25) - param(W 1.5) - param(AS 0.3375) - param(AD 0.6375) - param(PS 1.95) - param(PD 3.85) - terminal(S 1) - terminal(G 5) - terminal(D 2) - terminal(B 4) - ) - device(3 D$NMOS - location(850 2135) - param(L 0.25) - param(W 0.95) - param(AS 0.40375) - param(AD 0.21375) - param(PS 2.75) - param(PD 1.4) - terminal(S 3) - terminal(G 6) - terminal(D 8) - terminal(B 7) - ) - device(4 D$NMOS$1 - location(1550 2135) - param(L 0.25) - param(W 0.95) - param(AS 0.21375) - param(AD 0.40375) - param(PS 1.4) - param(PD 2.75) - terminal(S 8) - terminal(G 5) - terminal(D 2) - terminal(B 7) - ) - - ) - circuit(INVX1 - - # Circuit boundary - rect((-100 400) (2000 7600)) - - # Nets with their geometries - net(1 name(VDD) - rect(l8 (410 6260) (180 180)) - rect(l8 (-180 -730) (180 180)) - rect(l8 (-180 -730) (180 180)) - rect(l11 (-240 -240) (300 1400)) - rect(l11 (-650 300) (1800 800)) - rect(l11 (-1450 -1100) (300 300)) - rect(l11 (300 400) (0 0)) - rect(l2 (-650 -2150) (425 1500)) - ) - net(2 name(OUT) - rect(l8 (1110 5160) (180 180)) - rect(l8 (-180 920) (180 180)) - rect(l8 (-180 -730) (180 180)) - rect(l8 (-180 -4120) (180 180)) - rect(l8 (-180 370) (180 180)) - rect(l11 (-240 -790) (300 4790)) - rect(l11 (-150 -2500) (0 0)) - rect(l2 (-225 1050) (425 1500)) - rect(l6 (-425 -4890) (425 950)) - ) - net(3 name(VSS) - rect(l8 (410 1770) (180 180)) - rect(l8 (-180 370) (180 180)) - rect(l11 (-240 -1300) (300 1360)) - rect(l11 (-650 -2160) (1800 800)) - rect(l11 (-850 -400) (0 0)) - rect(l6 (-650 860) (425 950)) - ) - net(4 - rect(l3 (-100 4500) (2000 3500)) - ) - net(5 name(IN) - rect(l4 (725 2860) (250 1940)) - rect(l4 (-525 -1850) (300 300)) - rect(l4 (-25 1550) (250 2000)) - rect(l4 (-250 -2000) (250 2000)) - rect(l4 (-250 -5390) (250 1450)) - rect(l8 (-465 150) (180 180)) - rect(l11 (-90 -90) (0 0)) - rect(l11 (-150 -150) (300 300)) - ) - net(6 name(SUBSTRATE)) - - # Outgoing pins and their connections to nets - pin(1 name(VDD)) - pin(2 name(OUT)) - pin(3 name(VSS)) - pin(4) - pin(5 name(IN)) - pin(6 name(SUBSTRATE)) - - # Devices and their connections - device(1 D$PMOS$2 - location(850 5800) - param(L 0.25) - param(W 1.5) - param(AS 0.6375) - param(AD 0.6375) - param(PS 3.85) - param(PD 3.85) - terminal(S 1) - terminal(G 5) - terminal(D 2) - terminal(B 4) - ) - device(2 D$NMOS$2 - location(850 2135) - param(L 0.25) - param(W 0.95) - param(AS 0.40375) - param(AD 0.40375) - param(PS 2.75) - param(PD 2.75) - terminal(S 3) - terminal(G 5) - terminal(D 2) - terminal(B 6) - ) - - ) - circuit(RINGO - - # Circuit boundary - rect((0 350) (25800 7650)) - - # Nets with their geometries - net(1 - rect(l11 (4040 2950) (610 300)) - ) - net(2 - rect(l11 (5550 2950) (900 300)) - ) - net(3 - rect(l11 (7350 2950) (900 300)) - ) - net(4 - rect(l11 (9150 2950) (900 300)) - ) - net(5 - rect(l11 (10950 2950) (900 300)) - ) - net(6 - rect(l11 (12750 2950) (900 300)) - ) - net(7 - rect(l11 (14550 2950) (900 300)) - ) - net(8 - rect(l11 (16350 2950) (900 300)) - ) - net(9 - rect(l11 (18150 2950) (900 300)) - ) - net(10 - rect(l11 (19950 2950) (900 300)) - ) - net(11 name(FB) - rect(l11 (21750 2950) (900 300)) - rect(l11 (-19530 590) (320 320)) - rect(l11 (17820 -320) (320 320)) - rect(l12 (-18400 -260) (200 200)) - rect(l12 (17940 -200) (200 200)) - rect(l13 (-18040 -300) (17740 400)) - rect(l13 (-17920 -200) (0 0)) - rect(l13 (-220 -200) (400 400)) - rect(l13 (17740 -400) (400 400)) - ) - net(12 name(VDD) - rect(l3 (500 4500) (1400 3500)) - rect(l3 (-1900 -3500) (600 3500)) - rect(l3 (23300 -3500) (1400 3500)) - rect(l3 (-100 -3500) (600 3500)) - rect(l8 (-24690 -1240) (180 180)) - rect(l8 (-180 370) (180 180)) - rect(l8 (-180 -1280) (180 180)) - rect(l8 (23220 370) (180 180)) - rect(l8 (-180 370) (180 180)) - rect(l8 (-180 -1280) (180 180)) - rect(l11 (-21740 860) (0 0)) - rect(l11 (-2350 -450) (1200 800)) - rect(l11 (-750 -1450) (300 1400)) - rect(l11 (-100 -350) (0 0)) - rect(l11 (-1250 -400) (600 800)) - rect(l11 (23400 -800) (1200 800)) - rect(l11 (-750 -1450) (300 1400)) - rect(l11 (-100 -350) (0 0)) - rect(l11 (550 -400) (600 800)) - rect(l9 (-24850 -1500) (500 1500)) - rect(l9 (22900 -1500) (500 1500)) - ) - net(13 name(OUT) - rect(l11 (23440 3840) (320 320)) - rect(l12 (-260 -260) (200 200)) - rect(l13 (-100 -100) (0 0)) - rect(l13 (-200 -200) (400 400)) - ) - net(14 name(ENABLE) - rect(l11 (2440 2940) (320 320)) - rect(l12 (-260 -260) (200 200)) - rect(l13 (-100 -100) (0 0)) - rect(l13 (-200 -200) (400 400)) - ) - net(15 name(VSS) - rect(l8 (1110 1610) (180 180)) - rect(l8 (-180 -1280) (180 180)) - rect(l8 (-180 370) (180 180)) - rect(l8 (23220 370) (180 180)) - rect(l8 (-180 -1280) (180 180)) - rect(l8 (-180 370) (180 180)) - rect(l11 (-21740 -390) (0 0)) - rect(l11 (-1900 -400) (300 1400)) - rect(l11 (-750 -1450) (1200 800)) - rect(l11 (-550 -400) (0 0)) - rect(l11 (-1250 -400) (600 800)) - rect(l11 (23850 -750) (300 1400)) - rect(l11 (-750 -1450) (1200 800)) - rect(l11 (-550 -400) (0 0)) - rect(l11 (550 -400) (600 800)) - rect(l10 (-24850 -800) (500 1500)) - rect(l10 (22900 -1500) (500 1500)) - ) - - # Outgoing pins and their connections to nets - pin(11 name(FB)) - pin(12 name(VDD)) - pin(13 name(OUT)) - pin(14 name(ENABLE)) - pin(15 name(VSS)) - - # Subcircuits and their connections - circuit(1 ND2X1 location(1800 0) - pin(0 12) - pin(1 1) - pin(2 15) - pin(3 12) - pin(4 11) - pin(5 14) - pin(6 15) - ) - circuit(2 INVX1 location(4200 0) - pin(0 12) - pin(1 2) - pin(2 15) - pin(3 12) - pin(4 1) - pin(5 15) - ) - circuit(3 INVX1 location(6000 0) - pin(0 12) - pin(1 3) - pin(2 15) - pin(3 12) - pin(4 2) - pin(5 15) - ) - circuit(4 INVX1 location(7800 0) - pin(0 12) - pin(1 4) - pin(2 15) - pin(3 12) - pin(4 3) - pin(5 15) - ) - circuit(5 INVX1 location(9600 0) - pin(0 12) - pin(1 5) - pin(2 15) - pin(3 12) - pin(4 4) - pin(5 15) - ) - circuit(6 INVX1 location(11400 0) - pin(0 12) - pin(1 6) - pin(2 15) - pin(3 12) - pin(4 5) - pin(5 15) - ) - circuit(7 INVX1 location(13200 0) - pin(0 12) - pin(1 7) - pin(2 15) - pin(3 12) - pin(4 6) - pin(5 15) - ) - circuit(8 INVX1 location(15000 0) - pin(0 12) - pin(1 8) - pin(2 15) - pin(3 12) - pin(4 7) - pin(5 15) - ) - circuit(9 INVX1 location(16800 0) - pin(0 12) - pin(1 9) - pin(2 15) - pin(3 12) - pin(4 8) - pin(5 15) - ) - circuit(10 INVX1 location(18600 0) - pin(0 12) - pin(1 10) - pin(2 15) - pin(3 12) - pin(4 9) - pin(5 15) - ) - circuit(11 INVX1 location(20400 0) - pin(0 12) - pin(1 11) - pin(2 15) - pin(3 12) - pin(4 10) - pin(5 15) - ) - circuit(12 INVX1 location(22200 0) - pin(0 12) - pin(1 13) - pin(2 15) - pin(3 12) - pin(4 11) - pin(5 15) - ) - - ) -) - -# Reference netlist -reference( - - # Device class section - class(PMOS MOS4) - class(NMOS MOS4) - - # Circuit section - # Circuits are the hierarchical building blocks of the netlist. - circuit(ND2X1 - - # Nets - net(1 name(VDD)) - net(2 name(OUT)) - net(3 name(VSS)) - net(4 name(NWELL)) - net(5 name(B)) - net(6 name(A)) - net(7 name(BULK)) - net(8 name('1')) - - # Outgoing pins and their connections to nets - pin(1 name(VDD)) - pin(2 name(OUT)) - pin(3 name(VSS)) - pin(4 name(NWELL)) - pin(5 name(B)) - pin(6 name(A)) - pin(7 name(BULK)) - - # Devices and their connections - device(1 PMOS - name($1) - param(L 0.25) - param(W 1.5) - param(AS 0) - param(AD 0) - param(PS 0) - param(PD 0) - terminal(S 1) - terminal(G 6) - terminal(D 2) - terminal(B 4) - ) - device(2 PMOS - name($2) - param(L 0.25) - param(W 1.5) - param(AS 0) - param(AD 0) - param(PS 0) - param(PD 0) - terminal(S 1) - terminal(G 5) - terminal(D 2) - terminal(B 4) - ) - device(3 NMOS - name($3) - param(L 0.25) - param(W 0.95) - param(AS 0) - param(AD 0) - param(PS 0) - param(PD 0) - terminal(S 3) - terminal(G 6) - terminal(D 8) - terminal(B 7) - ) - device(4 NMOS - name($4) - param(L 0.25) - param(W 0.95) - param(AS 0) - param(AD 0) - param(PS 0) - param(PD 0) - terminal(S 8) - terminal(G 5) - terminal(D 2) - terminal(B 7) - ) - - ) - circuit(INVX1 - - # Nets - net(1 name(VDD)) - net(2 name(OUT)) - net(3 name(VSS)) - net(4 name(NWELL)) - net(5 name(IN)) - net(6 name(BULK)) - - # Outgoing pins and their connections to nets - pin(1 name(VDD)) - pin(2 name(OUT)) - pin(3 name(VSS)) - pin(4 name(NWELL)) - pin(5 name(IN)) - pin(6 name(BULK)) - - # Devices and their connections - device(1 PMOS - name($1) - param(L 0.25) - param(W 1.5) - param(AS 0) - param(AD 0) - param(PS 0) - param(PD 0) - terminal(S 1) - terminal(G 5) - terminal(D 2) - terminal(B 4) - ) - device(2 NMOS - name($2) - param(L 0.25) - param(W 0.95) - param(AS 0) - param(AD 0) - param(PS 0) - param(PD 0) - terminal(S 3) - terminal(G 5) - terminal(D 2) - terminal(B 6) - ) - - ) - circuit(RINGO - - # Nets - net(1 name(VSS)) - net(2 name(VDD)) - net(3 name(FB)) - net(4 name(ENABLE)) - net(5 name(OUT)) - net(6 name('1')) - net(7 name('2')) - net(8 name('3')) - net(9 name('4')) - net(10 name('5')) - net(11 name('6')) - net(12 name('7')) - net(13 name('8')) - net(14 name('9')) - net(15 name('10')) - - # Outgoing pins and their connections to nets - pin(1 name(VSS)) - pin(2 name(VDD)) - pin(3 name(FB)) - pin(4 name(ENABLE)) - pin(5 name(OUT)) - - # Subcircuits and their connections - circuit(1 ND2X1 name($1) - pin(0 2) - pin(1 6) - pin(2 1) - pin(3 2) - pin(4 3) - pin(5 4) - pin(6 1) - ) - circuit(2 INVX1 name($2) - pin(0 2) - pin(1 7) - pin(2 1) - pin(3 2) - pin(4 6) - pin(5 1) - ) - circuit(3 INVX1 name($3) - pin(0 2) - pin(1 8) - pin(2 1) - pin(3 2) - pin(4 7) - pin(5 1) - ) - circuit(4 INVX1 name($4) - pin(0 2) - pin(1 9) - pin(2 1) - pin(3 2) - pin(4 8) - pin(5 1) - ) - circuit(5 INVX1 name($5) - pin(0 2) - pin(1 10) - pin(2 1) - pin(3 2) - pin(4 9) - pin(5 1) - ) - circuit(6 INVX1 name($6) - pin(0 2) - pin(1 11) - pin(2 1) - pin(3 2) - pin(4 10) - pin(5 1) - ) - circuit(7 INVX1 name($7) - pin(0 2) - pin(1 12) - pin(2 1) - pin(3 2) - pin(4 11) - pin(5 1) - ) - circuit(8 INVX1 name($8) - pin(0 2) - pin(1 13) - pin(2 1) - pin(3 2) - pin(4 12) - pin(5 1) - ) - circuit(9 INVX1 name($9) - pin(0 2) - pin(1 14) - pin(2 1) - pin(3 2) - pin(4 13) - pin(5 1) - ) - circuit(10 INVX1 name($10) - pin(0 2) - pin(1 15) - pin(2 1) - pin(3 2) - pin(4 14) - pin(5 1) - ) - circuit(11 INVX1 name($11) - pin(0 2) - pin(1 3) - pin(2 1) - pin(3 2) - pin(4 15) - pin(5 1) - ) - circuit(12 INVX1 name($12) - pin(0 2) - pin(1 5) - pin(2 1) - pin(3 2) - pin(4 3) - pin(5 1) - ) - - ) -) - -# Cross reference -xref( - circuit(INVX1 INVX1 match - xref( - net(4 4 match) - net(5 5 match) - net(2 2 match) - net(6 6 match) - net(1 1 match) - net(3 3 match) - pin(3 3 match) - pin(4 4 match) - pin(1 1 match) - pin(5 5 match) - pin(0 0 match) - pin(2 2 match) - device(2 2 match) - device(1 1 match) - ) - ) - circuit(ND2X1 ND2X1 match - xref( - net(8 8 match) - net(4 4 match) - net(6 6 match) - net(5 5 match) - net(2 2 match) - net(7 7 match) - net(1 1 match) - net(3 3 match) - pin(3 3 match) - pin(5 5 match) - pin(4 4 match) - pin(1 1 match) - pin(6 6 match) - pin(0 0 match) - pin(2 2 match) - device(3 3 match) - device(4 4 match) - device(1 1 match) - device(2 2 match) - ) - ) - circuit(RINGO RINGO match - xref( - net(1 6 match) - net(10 15 match) - net(2 7 match) - net(3 8 match) - net(4 9 match) - net(5 10 match) - net(6 11 match) - net(7 12 match) - net(8 13 match) - net(9 14 match) - net(14 4 match) - net(11 3 match) - net(13 5 match) - net(12 2 match) - net(15 1 match) - pin(3 3 match) - pin(0 2 match) - pin(2 4 match) - pin(1 1 match) - pin(4 0 match) - circuit(2 2 match) - circuit(3 3 match) - circuit(4 4 match) - circuit(5 5 match) - circuit(6 6 match) - circuit(7 7 match) - circuit(8 8 match) - circuit(9 9 match) - circuit(10 10 match) - circuit(11 11 match) - circuit(12 12 match) - circuit(1 1 match) - ) - ) -) diff --git a/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvsdb.2 b/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvsdb.2 index f924852fe..6e73c3fdf 100644 --- a/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvsdb.2 +++ b/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 name($I3) + net(8 name($I5) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_pin_swapping.lvsdb.2 b/testdata/lvs/ringo_simple_pin_swapping.lvsdb.2 index d8f0bcee8..dbeab1f69 100644 --- a/testdata/lvs/ringo_simple_pin_swapping.lvsdb.2 +++ b/testdata/lvs/ringo_simple_pin_swapping.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 name($I3) + net(8 name($I5) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_same_device_classes.lvsdb.2 b/testdata/lvs/ringo_simple_same_device_classes.lvsdb.2 index 958d330a8..08ae6e339 100644 --- a/testdata/lvs/ringo_simple_same_device_classes.lvsdb.2 +++ b/testdata/lvs/ringo_simple_same_device_classes.lvsdb.2 @@ -201,7 +201,7 @@ layout( rect(l17 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 name($I3) + net(8 name($I5) rect(l9 (975 1660) (425 950)) rect(l9 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_simplification.lvsdb.2 b/testdata/lvs/ringo_simple_simplification.lvsdb.2 index 475506d1f..494dbfa62 100644 --- a/testdata/lvs/ringo_simple_simplification.lvsdb.2 +++ b/testdata/lvs/ringo_simple_simplification.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I5) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) @@ -560,22 +560,22 @@ layout( rect(l10 (-24850 -800) (500 1500)) rect(l10 (22900 -1500) (500 1500)) ) - net(10 + net(10 name($I22) rect(l11 (7350 2950) (900 300)) ) - net(11 + net(11 name($I18) rect(l11 (16350 2950) (900 300)) ) - net(12 + net(12 name($I36) rect(l11 (9150 2950) (900 300)) ) - net(13 + net(13 name($I37) rect(l11 (10950 2950) (900 300)) ) - net(14 + net(14 name($I38) rect(l11 (12750 2950) (900 300)) ) - net(15 + net(15 name($I39) rect(l11 (14550 2950) (900 300)) ) diff --git a/testdata/lvs/ringo_simple_simplification_with_align.lvsdb.2 b/testdata/lvs/ringo_simple_simplification_with_align.lvsdb.2 index 650cb1b5a..e374d9100 100644 --- a/testdata/lvs/ringo_simple_simplification_with_align.lvsdb.2 +++ b/testdata/lvs/ringo_simple_simplification_with_align.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I5) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) @@ -560,22 +560,22 @@ layout( rect(l10 (-24850 -800) (500 1500)) rect(l10 (22900 -1500) (500 1500)) ) - net(10 + net(10 name($I22) rect(l11 (7350 2950) (900 300)) ) - net(11 + net(11 name($I18) rect(l11 (16350 2950) (900 300)) ) - net(12 + net(12 name($I36) rect(l11 (9150 2950) (900 300)) ) - net(13 + net(13 name($I37) rect(l11 (10950 2950) (900 300)) ) - net(14 + net(14 name($I38) rect(l11 (12750 2950) (900 300)) ) - net(15 + net(15 name($I39) rect(l11 (14550 2950) (900 300)) ) diff --git a/testdata/lvs/ringo_simple_with_tol.lvsdb.2 b/testdata/lvs/ringo_simple_with_tol.lvsdb.2 index bded7a1fc..1ea753c38 100644 --- a/testdata/lvs/ringo_simple_with_tol.lvsdb.2 +++ b/testdata/lvs/ringo_simple_with_tol.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 name($I3) + net(8 name($I5) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/ringo_simple_with_tol_early.lvsdb.2 b/testdata/lvs/ringo_simple_with_tol_early.lvsdb.2 index bded7a1fc..1ea753c38 100644 --- a/testdata/lvs/ringo_simple_with_tol_early.lvsdb.2 +++ b/testdata/lvs/ringo_simple_with_tol_early.lvsdb.2 @@ -199,7 +199,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 name($I3) + net(8 name($I5) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) diff --git a/testdata/lvs/test_22a.lvsdb.2 b/testdata/lvs/test_22a.lvsdb.2 index bd5c980af..b6914388f 100644 --- a/testdata/lvs/test_22a.lvsdb.2 +++ b/testdata/lvs/test_22a.lvsdb.2 @@ -832,16 +832,16 @@ layout( rect(l24 (2030 -150) (150 150)) rect(l24 (2030 -150) (150 150)) ) - net(27 + net(27 name($28) polygon(l7 (955 2305) (0 180) (-690 0) (0 150) (1650 0) (0 -150) (-690 0) (0 -180)) ) - net(28 + net(28 name($29) polygon(l7 (7495 2305) (0 180) (-690 0) (0 150) (1650 0) (0 -150) (-690 0) (0 -180)) ) - net(29 + net(29 name($30) polygon(l7 (3135 2305) (0 180) (-690 0) (0 150) (1650 0) (0 -150) (-690 0) (0 -180)) ) - net(30 + net(30 name($31) polygon(l7 (5315 2305) (0 180) (-690 0) (0 150) (1650 0) (0 -150) (-690 0) (0 -180)) ) net(31 name('wl[1]') @@ -893,7 +893,7 @@ layout( rect(l24 (2030 -150) (150 150)) rect(l24 (2030 -150) (150 150)) ) - net(32 + net(32 name($33) polygon(l2 (395 3065) (0 340) (-105 0) (0 420) (525 0) (0 -760)) rect(l2 (-525 1670) (445 420)) polygon(l9 (-210 -1620) (0 1570) (170 0) (0 -920) (245 0) (0 -170) (-245 0) (0 -480)) @@ -904,12 +904,12 @@ layout( rect(l22 (0 -270) (950 150)) rect(l22 (0 -840) (150 2010)) ) - net(33 + net(33 name($34) rect(l7 (940 3965) (950 150)) rect(l7 (-1280 -150) (330 270)) rect(l7 (950 -960) (150 2010)) ) - net(34 + net(34 name($35) polygon(l2 (1365 3065) (0 760) (525 0) (0 -420) (-105 0) (0 -340)) rect(l2 (-340 1670) (445 420)) polygon(l9 (-405 -1620) (0 840) (-245 0) (0 170) (245 0) (0 560) (170 0) (0 -1570)) @@ -920,7 +920,7 @@ layout( rect(l22 (-1100 -1320) (150 2010)) rect(l22 (950 -960) (330 270)) ) - net(35 + net(35 name($36) polygon(l2 (2575 3065) (0 340) (-105 0) (0 420) (525 0) (0 -760)) rect(l2 (-525 1670) (445 420)) polygon(l9 (-210 -1620) (0 1570) (170 0) (0 -920) (245 0) (0 -170) (-245 0) (0 -480)) @@ -931,12 +931,12 @@ layout( rect(l22 (0 -270) (950 150)) rect(l22 (0 -840) (150 2010)) ) - net(36 + net(36 name($37) rect(l7 (3120 3965) (950 150)) rect(l7 (-1280 -150) (330 270)) rect(l7 (950 -960) (150 2010)) ) - net(37 + net(37 name($38) polygon(l2 (4755 3065) (0 340) (-105 0) (0 420) (525 0) (0 -760)) rect(l2 (-525 1670) (445 420)) polygon(l9 (-210 -1620) (0 1570) (170 0) (0 -920) (245 0) (0 -170) (-245 0) (0 -480)) @@ -947,12 +947,12 @@ layout( rect(l22 (0 -270) (950 150)) rect(l22 (0 -840) (150 2010)) ) - net(38 + net(38 name($39) rect(l7 (5300 3965) (950 150)) rect(l7 (-1280 -150) (330 270)) rect(l7 (950 -960) (150 2010)) ) - net(39 + net(39 name($40) polygon(l2 (5725 3065) (0 760) (525 0) (0 -420) (-105 0) (0 -340)) rect(l2 (-340 1670) (445 420)) polygon(l9 (-405 -1620) (0 840) (-245 0) (0 170) (245 0) (0 560) (170 0) (0 -1570)) @@ -963,7 +963,7 @@ layout( rect(l22 (-1100 -1320) (150 2010)) rect(l22 (950 -960) (330 270)) ) - net(40 + net(40 name($41) polygon(l2 (6935 3065) (0 340) (-105 0) (0 420) (525 0) (0 -760)) rect(l2 (-525 1670) (445 420)) polygon(l9 (-210 -1620) (0 1570) (170 0) (0 -920) (245 0) (0 -170) (-245 0) (0 -480)) @@ -974,12 +974,12 @@ layout( rect(l22 (0 -270) (950 150)) rect(l22 (0 -840) (150 2010)) ) - net(41 + net(41 name($42) rect(l7 (7480 3965) (950 150)) rect(l7 (-1280 -150) (330 270)) rect(l7 (950 -960) (150 2010)) ) - net(42 + net(42 name($43) polygon(l2 (7905 3065) (0 760) (525 0) (0 -420) (-105 0) (0 -340)) rect(l2 (-340 1670) (445 420)) polygon(l9 (-405 -1620) (0 840) (-245 0) (0 170) (245 0) (0 560) (170 0) (0 -1570)) @@ -990,29 +990,29 @@ layout( rect(l22 (-1100 -1320) (150 2010)) rect(l22 (950 -960) (330 270)) ) - net(43 + net(43 name($44) polygon(l7 (265 2915) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) - net(44 + net(44 name($45) polygon(l7 (6805 2915) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) - net(45 + net(45 name($46) polygon(l7 (2445 2915) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) - net(46 + net(46 name($47) polygon(l7 (4625 2915) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) - net(47 + net(47 name($49) rect(l7 (290 4445) (950 150)) rect(l7 (-1100 -1320) (150 2010)) rect(l7 (950 -960) (330 270)) ) - net(48 + net(48 name($50) rect(l7 (2470 4445) (950 150)) rect(l7 (-1100 -1320) (150 2010)) rect(l7 (950 -960) (330 270)) ) - net(49 + net(49 name($51) polygon(l2 (3545 3065) (0 760) (525 0) (0 -420) (-105 0) (0 -340)) rect(l2 (-340 1670) (445 420)) polygon(l9 (-405 -1620) (0 840) (-245 0) (0 170) (245 0) (0 560) (170 0) (0 -1570)) @@ -1023,12 +1023,12 @@ layout( rect(l22 (-1100 -1320) (150 2010)) rect(l22 (950 -960) (330 270)) ) - net(50 + net(50 name($52) rect(l7 (4650 4445) (950 150)) rect(l7 (-1100 -1320) (150 2010)) rect(l7 (950 -960) (330 270)) ) - net(51 + net(51 name($53) rect(l7 (6830 4445) (950 150)) rect(l7 (-1100 -1320) (150 2010)) rect(l7 (950 -960) (330 270)) diff --git a/testdata/lvs/test_22b.lvsdb.2 b/testdata/lvs/test_22b.lvsdb.2 index cac410e2b..7c718f649 100644 --- a/testdata/lvs/test_22b.lvsdb.2 +++ b/testdata/lvs/test_22b.lvsdb.2 @@ -832,16 +832,16 @@ layout( rect(l24 (2030 -150) (150 150)) rect(l24 (2030 -150) (150 150)) ) - net(27 + net(27 name($28) polygon(l7 (955 2305) (0 180) (-690 0) (0 150) (1650 0) (0 -150) (-690 0) (0 -180)) ) - net(28 + net(28 name($29) polygon(l7 (7495 2305) (0 180) (-690 0) (0 150) (1650 0) (0 -150) (-690 0) (0 -180)) ) - net(29 + net(29 name($30) polygon(l7 (3135 2305) (0 180) (-690 0) (0 150) (1650 0) (0 -150) (-690 0) (0 -180)) ) - net(30 + net(30 name($31) polygon(l7 (5315 2305) (0 180) (-690 0) (0 150) (1650 0) (0 -150) (-690 0) (0 -180)) ) net(31 name('wl[1]') @@ -893,7 +893,7 @@ layout( rect(l24 (2030 -150) (150 150)) rect(l24 (2030 -150) (150 150)) ) - net(32 + net(32 name($33) polygon(l2 (395 3065) (0 340) (-105 0) (0 420) (525 0) (0 -760)) rect(l2 (-525 1670) (445 420)) polygon(l9 (-210 -1620) (0 1570) (170 0) (0 -920) (245 0) (0 -170) (-245 0) (0 -480)) @@ -904,12 +904,12 @@ layout( rect(l22 (0 -270) (950 150)) rect(l22 (0 -840) (150 2010)) ) - net(33 + net(33 name($34) rect(l7 (940 3965) (950 150)) rect(l7 (-1280 -150) (330 270)) rect(l7 (950 -960) (150 2010)) ) - net(34 + net(34 name($35) polygon(l2 (1365 3065) (0 760) (525 0) (0 -420) (-105 0) (0 -340)) rect(l2 (-340 1670) (445 420)) polygon(l9 (-405 -1620) (0 840) (-245 0) (0 170) (245 0) (0 560) (170 0) (0 -1570)) @@ -920,7 +920,7 @@ layout( rect(l22 (-1100 -1320) (150 2010)) rect(l22 (950 -960) (330 270)) ) - net(35 + net(35 name($36) polygon(l2 (2575 3065) (0 340) (-105 0) (0 420) (525 0) (0 -760)) rect(l2 (-525 1670) (445 420)) polygon(l9 (-210 -1620) (0 1570) (170 0) (0 -920) (245 0) (0 -170) (-245 0) (0 -480)) @@ -931,12 +931,12 @@ layout( rect(l22 (0 -270) (950 150)) rect(l22 (0 -840) (150 2010)) ) - net(36 + net(36 name($37) rect(l7 (3120 3965) (950 150)) rect(l7 (-1280 -150) (330 270)) rect(l7 (950 -960) (150 2010)) ) - net(37 + net(37 name($38) polygon(l2 (4755 3065) (0 340) (-105 0) (0 420) (525 0) (0 -760)) rect(l2 (-525 1670) (445 420)) polygon(l9 (-210 -1620) (0 1570) (170 0) (0 -920) (245 0) (0 -170) (-245 0) (0 -480)) @@ -947,12 +947,12 @@ layout( rect(l22 (0 -270) (950 150)) rect(l22 (0 -840) (150 2010)) ) - net(38 + net(38 name($39) rect(l7 (5300 3965) (950 150)) rect(l7 (-1280 -150) (330 270)) rect(l7 (950 -960) (150 2010)) ) - net(39 + net(39 name($40) polygon(l2 (5725 3065) (0 760) (525 0) (0 -420) (-105 0) (0 -340)) rect(l2 (-340 1670) (445 420)) polygon(l9 (-405 -1620) (0 840) (-245 0) (0 170) (245 0) (0 560) (170 0) (0 -1570)) @@ -963,7 +963,7 @@ layout( rect(l22 (-1100 -1320) (150 2010)) rect(l22 (950 -960) (330 270)) ) - net(40 + net(40 name($41) polygon(l2 (6935 3065) (0 340) (-105 0) (0 420) (525 0) (0 -760)) rect(l2 (-525 1670) (445 420)) polygon(l9 (-210 -1620) (0 1570) (170 0) (0 -920) (245 0) (0 -170) (-245 0) (0 -480)) @@ -974,12 +974,12 @@ layout( rect(l22 (0 -270) (950 150)) rect(l22 (0 -840) (150 2010)) ) - net(41 + net(41 name($42) rect(l7 (7480 3965) (950 150)) rect(l7 (-1280 -150) (330 270)) rect(l7 (950 -960) (150 2010)) ) - net(42 + net(42 name($43) polygon(l2 (7905 3065) (0 760) (525 0) (0 -420) (-105 0) (0 -340)) rect(l2 (-340 1670) (445 420)) polygon(l9 (-405 -1620) (0 840) (-245 0) (0 170) (245 0) (0 560) (170 0) (0 -1570)) @@ -990,29 +990,29 @@ layout( rect(l22 (-1100 -1320) (150 2010)) rect(l22 (950 -960) (330 270)) ) - net(43 + net(43 name($44) polygon(l7 (265 2915) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) - net(44 + net(44 name($45) polygon(l7 (6805 2915) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) - net(45 + net(45 name($46) polygon(l7 (2445 2915) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) - net(46 + net(46 name($47) polygon(l7 (4625 2915) (0 150) (690 0) (0 180) (270 0) (0 -180) (690 0) (0 -150)) ) - net(47 + net(47 name($49) rect(l7 (290 4445) (950 150)) rect(l7 (-1100 -1320) (150 2010)) rect(l7 (950 -960) (330 270)) ) - net(48 + net(48 name($50) rect(l7 (2470 4445) (950 150)) rect(l7 (-1100 -1320) (150 2010)) rect(l7 (950 -960) (330 270)) ) - net(49 + net(49 name($51) polygon(l2 (3545 3065) (0 760) (525 0) (0 -420) (-105 0) (0 -340)) rect(l2 (-340 1670) (445 420)) polygon(l9 (-405 -1620) (0 840) (-245 0) (0 170) (245 0) (0 560) (170 0) (0 -1570)) @@ -1023,12 +1023,12 @@ layout( rect(l22 (-1100 -1320) (150 2010)) rect(l22 (950 -960) (330 270)) ) - net(50 + net(50 name($52) rect(l7 (4650 4445) (950 150)) rect(l7 (-1100 -1320) (150 2010)) rect(l7 (950 -960) (330 270)) ) - net(51 + net(51 name($53) rect(l7 (6830 4445) (950 150)) rect(l7 (-1100 -1320) (150 2010)) rect(l7 (950 -960) (330 270)) From ca53d8718b81091301c60b4c40c2a12728db5dd8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 17 May 2025 17:26:59 +0200 Subject: [PATCH 214/392] Fixed some build problems --- src/pex/pex/pexRNetExtractor.cc | 10 +++++----- src/pex/pex/pexRNetwork.h | 5 +++-- src/pex/pex/pexSquareCountingRExtractor.cc | 6 +++--- src/pex/pex/pexTriangulationRExtractor.cc | 12 ++++++------ 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/pex/pex/pexRNetExtractor.cc b/src/pex/pex/pexRNetExtractor.cc index e93d6e745..977524dcc 100644 --- a/src/pex/pex/pexRNetExtractor.cc +++ b/src/pex/pex/pexRNetExtractor.cc @@ -229,19 +229,19 @@ RNetExtractor::create_via_ports (const RExtractorTech &tech, } } -static inline size_t make_id (size_t index, unsigned int type) +static inline size_t make_id (unsigned int index, unsigned int type) { - return (index << 2) + type; + return (size_t (index) << 2) + type; } -static inline size_t index_from_id (size_t id) +static inline unsigned int index_from_id (size_t id) { - return id >> 2; + return (unsigned int) (id >> 2); } static inline unsigned int type_from_id (size_t id) { - return id & 3; + return (unsigned int) (id & 3); } namespace diff --git a/src/pex/pex/pexRNetwork.h b/src/pex/pex/pexRNetwork.h index b9438b0d9..8de7dcb49 100644 --- a/src/pex/pex/pexRNetwork.h +++ b/src/pex/pex/pexRNetwork.h @@ -50,7 +50,7 @@ class RNetwork; * RNode object cannot be created directly. Use "create_node" * from RNetwork. */ -struct PEX_PUBLIC RNode +class PEX_PUBLIC RNode : public tl::list_node { public: @@ -139,9 +139,10 @@ private: * RElement objects cannot be created directly. Use "create_element" * from RNetwork. */ -struct PEX_PUBLIC RElement +class PEX_PUBLIC RElement : public tl::list_node { +public: /** * @brief The conductance value */ diff --git a/src/pex/pex/pexSquareCountingRExtractor.cc b/src/pex/pex/pexSquareCountingRExtractor.cc index cd956a703..08b324d13 100644 --- a/src/pex/pex/pexSquareCountingRExtractor.cc +++ b/src/pex/pex/pexSquareCountingRExtractor.cc @@ -226,7 +226,7 @@ SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector for (size_t j = 0; j < p->size (); ++j) { - const db::plc::Edge *e = p->edge (j); + const db::plc::Edge *e = p->edge (int (j)); if (e->left () && e->right ()) { auto ip = internal_ports.find (e); @@ -262,7 +262,7 @@ SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector // 1. internal ports for (auto i = ip_indexes.begin (); i != ip_indexes.end (); ++i) { db::Box loc = (inv_trans * internal_port_edges [*i]->edge ()).bbox (); - ports.push_back (std::make_pair (PortDefinition (pex::RNode::Internal, loc, *i), (pex::RNode *) 0)); + ports.push_back (std::make_pair (PortDefinition (pex::RNode::Internal, loc, (unsigned int) *i), (pex::RNode *) 0)); } // 2. vertex ports @@ -278,7 +278,7 @@ SquareCountingRExtractor::extract (const db::Polygon &polygon, const std::vector // (NOTE: here we only take the center of the bounding box) for (auto i = pp_indexes.begin (); i != pp_indexes.end (); ++i) { db::Box loc = polygon_ports [*i].box (); - ports.push_back (std::make_pair (PortDefinition (pex::RNode::PolygonPort, loc, *i), (pex::RNode *) 0)); + ports.push_back (std::make_pair (PortDefinition (pex::RNode::PolygonPort, loc, (unsigned int) *i), (pex::RNode *) 0)); } // create nodes for the ports diff --git a/src/pex/pex/pexTriangulationRExtractor.cc b/src/pex/pex/pexTriangulationRExtractor.cc index 6a867ef43..500a5c179 100644 --- a/src/pex/pex/pexTriangulationRExtractor.cc +++ b/src/pex/pex/pexTriangulationRExtractor.cc @@ -133,7 +133,7 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector< for (size_t iv = 0; iv < p->size (); ++iv) { - const db::plc::Vertex *vertex = p->vertex (iv); + const db::plc::Vertex *vertex = p->vertex (int (iv)); if (vertex2node.find (vertex) != vertex2node.end ()) { continue; } @@ -148,7 +148,7 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector< if (pn != pport_nodes.end ()) { n = pn->second; } else { - n = rnetwork.create_node (pex::RNode::PolygonPort, port_index, 0); + n = rnetwork.create_node (pex::RNode::PolygonPort, (unsigned int) port_index, 0); pport_nodes.insert (std::make_pair (port_index, n)); n->location = trans * polygon_ports [port_index].box (); } @@ -158,7 +158,7 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector< for (auto pi = vertex->ids ().begin (); pi != vertex->ids ().end (); ++pi) { size_t port_index = size_t (*pi); if (port_index < vertex_ports.size ()) { - RNode *nn = rnetwork.create_node (pex::RNode::VertexPort, port_index, 0); + RNode *nn = rnetwork.create_node (pex::RNode::VertexPort, (unsigned int) port_index, 0); nn->location = db::DBox (*vertex, *vertex); if (n) { // in case of multiple vertexes on the same spot, short them @@ -172,7 +172,7 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector< } else { - n = rnetwork.create_node (pex::RNode::Internal, internal_node_id++, 0); + n = rnetwork.create_node (pex::RNode::Internal, (unsigned int) internal_node_id++, 0); n->location = db::DBox (*vertex, *vertex); } @@ -204,7 +204,7 @@ TriangulationRExtractor::extract (const db::Polygon &polygon, const std::vector< if (ip != pport_nodes.end ()) { // create a new vertex port and short it to the polygon port - auto n = rnetwork.create_node (pex::RNode::VertexPort, iv, 0); + auto n = rnetwork.create_node (pex::RNode::VertexPort, (unsigned int) iv, 0); n->location = db::DBox (trans * vp, trans * vp); rnetwork.create_element (pex::RElement::short_value (), n, ip->second); @@ -283,7 +283,7 @@ TriangulationRExtractor::eliminate_all (RNetwork &rnetwork) size_t nn = n->elements ().size (); if (nn <= nmax) { to_eliminate.push_back (const_cast (n.operator-> ())); - } else if (nmax_next == 0 or nn < nmax_next) { + } else if (nmax_next == 0 || nn < nmax_next) { nmax_next = nn; } } From 5dd189d413223fa38f5d416c50eea3fe5cf9be44 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 17 May 2025 18:39:11 +0200 Subject: [PATCH 215/392] Including pex dependencies in build --- src/klayout.pro | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/klayout.pro b/src/klayout.pro index 3e211798d..47891e836 100644 --- a/src/klayout.pro +++ b/src/klayout.pro @@ -69,7 +69,7 @@ lib.depends += db lym.depends += gsi $$LANG_DEPENDS -laybasic.depends += rdb +laybasic.depends += rdb pex layview.depends += laybasic ant.depends += layview @@ -117,7 +117,7 @@ equals(HAVE_RUBY, "1") { } else { - plugins.depends += layview ant img edt + plugins.depends += layview ant img edt } From 91cb8826c701675773d95f169a92ec1d5193f751 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 17 May 2025 18:41:22 +0200 Subject: [PATCH 216/392] Including more dependencies in build --- src/klayout.pro | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/klayout.pro b/src/klayout.pro index 47891e836..a7dde2251 100644 --- a/src/klayout.pro +++ b/src/klayout.pro @@ -121,8 +121,8 @@ equals(HAVE_RUBY, "1") { } -buddies.depends += plugins lym $$LANG_DEPENDS -unit_tests.depends += plugins lym $$MAIN_DEPENDS $$LANG_DEPENDS +buddies.depends += plugins pex lym $$LANG_DEPENDS +unit_tests.depends += plugins pex lym $$MAIN_DEPENDS $$LANG_DEPENDS !equals(HAVE_QT, "0") { From 1fb0f318dc84d2b7a0055c6bf45fc61250e28988 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 17 May 2025 18:49:32 +0200 Subject: [PATCH 217/392] Added pex lib to bd tools. --- src/buddies/src/buddy_app.pri | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/buddies/src/buddy_app.pri b/src/buddies/src/buddy_app.pri index da47c2fa2..58da418bf 100644 --- a/src/buddies/src/buddy_app.pri +++ b/src/buddies/src/buddy_app.pri @@ -19,7 +19,7 @@ SOURCES = $$PWD/bd/main.cc INCLUDEPATH += $$BD_INC $$TL_INC $$GSI_INC DEPENDPATH += $$BD_INC $$TL_INC $$GSI_INC -LIBS += -L$$DESTDIR -lklayout_bd -lklayout_db -lklayout_tl -lklayout_gsi -lklayout_lib -lklayout_rdb -lklayout_lym +LIBS += -L$$DESTDIR -lklayout_bd -lklayout_db -lklayout_pex -lklayout_tl -lklayout_gsi -lklayout_lib -lklayout_rdb -lklayout_lym INCLUDEPATH += $$RBA_INC DEPENDPATH += $$RBA_INC From 3e3dcb757e68b0769439d86754de45937097312c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 17 May 2025 18:55:51 +0200 Subject: [PATCH 218/392] New golden test data --- testdata/lvs/ringo_simple_io2.lvsdb.2 | 908 ++++++++++++++++++++++++++ 1 file changed, 908 insertions(+) create mode 100644 testdata/lvs/ringo_simple_io2.lvsdb.2 diff --git a/testdata/lvs/ringo_simple_io2.lvsdb.2 b/testdata/lvs/ringo_simple_io2.lvsdb.2 new file mode 100644 index 000000000..94c1f9904 --- /dev/null +++ b/testdata/lvs/ringo_simple_io2.lvsdb.2 @@ -0,0 +1,908 @@ +#%lvsdb-klayout + +# Layout +layout( + top(RINGO) + unit(0.001) + + # Layer section + # This section lists the mask layers (drawing or derived) and their connections. + + # Mask layers + layer(l3 '1/0') + layer(l4 '5/0') + layer(l8 '8/0') + layer(l11 '9/0') + layer(l12 '10/0') + layer(l13 '11/0') + layer(l7) + layer(l2) + layer(l9) + layer(l6) + layer(l10) + + # Mask layer connectivity + connect(l3 l3 l9) + connect(l4 l4 l8) + connect(l8 l4 l8 l11 l2 l9 l6 l10) + connect(l11 l8 l11 l12) + connect(l12 l11 l12 l13) + connect(l13 l12 l13) + connect(l7 l7) + connect(l2 l8 l2) + connect(l9 l3 l8 l9) + connect(l6 l8 l6) + connect(l10 l8 l10) + + # Global nets and connectivity + global(l7 SUBSTRATE) + global(l10 SUBSTRATE) + + # Device class section + class(PMOS MOS4) + class(NMOS MOS4) + + # Device abstracts section + # Device abstracts list the pin shapes of the devices. + device(D$PMOS PMOS + terminal(S + rect(l2 (-550 -750) (425 1500)) + ) + terminal(G + rect(l4 (-125 -750) (250 1500)) + ) + terminal(D + rect(l2 (125 -750) (450 1500)) + ) + terminal(B + rect(l3 (-125 -750) (250 1500)) + ) + ) + device(D$PMOS$1 PMOS + terminal(S + rect(l2 (-575 -750) (450 1500)) + ) + terminal(G + rect(l4 (-125 -750) (250 1500)) + ) + terminal(D + rect(l2 (125 -750) (425 1500)) + ) + terminal(B + rect(l3 (-125 -750) (250 1500)) + ) + ) + device(D$PMOS$2 PMOS + terminal(S + rect(l2 (-550 -750) (425 1500)) + ) + terminal(G + rect(l4 (-125 -750) (250 1500)) + ) + terminal(D + rect(l2 (125 -750) (425 1500)) + ) + terminal(B + rect(l3 (-125 -750) (250 1500)) + ) + ) + device(D$NMOS NMOS + terminal(S + rect(l6 (-550 -475) (425 950)) + ) + terminal(G + rect(l4 (-125 -475) (250 950)) + ) + terminal(D + rect(l6 (125 -475) (450 950)) + ) + terminal(B + rect(l7 (-125 -475) (250 950)) + ) + ) + device(D$NMOS$1 NMOS + terminal(S + rect(l6 (-575 -475) (450 950)) + ) + terminal(G + rect(l4 (-125 -475) (250 950)) + ) + terminal(D + rect(l6 (125 -475) (425 950)) + ) + terminal(B + rect(l7 (-125 -475) (250 950)) + ) + ) + device(D$NMOS$2 NMOS + terminal(S + rect(l6 (-550 -475) (425 950)) + ) + terminal(G + rect(l4 (-125 -475) (250 950)) + ) + terminal(D + rect(l6 (125 -475) (425 950)) + ) + terminal(B + rect(l7 (-125 -475) (250 950)) + ) + ) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(ND2X1 + + # Circuit boundary + rect((-100 400) (2600 7600)) + + # Nets with their geometries + net(1 name(VDD) + rect(l8 (1110 5160) (180 180)) + rect(l8 (-180 920) (180 180)) + rect(l8 (-180 -730) (180 180)) + rect(l11 (-240 -790) (300 1700)) + rect(l11 (-1350 0) (2400 800)) + rect(l11 (-1150 -400) (0 0)) + rect(l2 (-275 -2150) (425 1500)) + rect(l2 (-400 -1500) (425 1500)) + ) + net(2 name(OUT) + rect(l8 (1810 1770) (180 180)) + rect(l8 (-180 370) (180 180)) + rect(l8 (-1580 3760) (180 180)) + rect(l8 (-180 -730) (180 180)) + rect(l8 (-180 -730) (180 180)) + rect(l8 (1220 920) (180 180)) + rect(l8 (-180 -1280) (180 180)) + rect(l8 (-180 370) (180 180)) + polygon(l11 (-240 -4180) (0 1390) (490 0) (0 -300) (-190 0) (0 -1090)) + rect(l11 (-110 1390) (300 1400)) + polygon(l11 (-1890 0) (0 600) (300 0) (0 -300) (1590 0) (0 -300)) + rect(l11 (-140 -500) (0 0)) + rect(l11 (-1750 1100) (300 1400)) + rect(l11 (1100 -1700) (300 300)) + rect(l11 (-300 0) (300 1400)) + rect(l2 (-375 -1450) (425 1500)) + rect(l2 (-1800 -1500) (425 1500)) + rect(l6 (950 -4890) (425 950)) + ) + net(3 name(VSS) + rect(l8 (410 1770) (180 180)) + rect(l8 (-180 370) (180 180)) + rect(l11 (-240 -1300) (300 1360)) + rect(l11 (-650 -2160) (2400 800)) + rect(l11 (-1150 -400) (0 0)) + rect(l6 (-950 860) (425 950)) + ) + net(4 + rect(l3 (-100 4500) (2600 3500)) + ) + net(5 name(B) + rect(l4 (1425 2860) (250 1940)) + rect(l4 (-345 -950) (300 300)) + rect(l4 (-205 650) (250 2000)) + rect(l4 (-250 -2000) (250 2000)) + rect(l4 (-250 -5390) (250 1450)) + rect(l8 (-285 1050) (180 180)) + rect(l11 (-70 -90) (0 0)) + rect(l11 (-170 -150) (300 300)) + ) + net(6 name(A) + rect(l4 (725 2860) (250 1940)) + rect(l4 (-325 -1850) (300 300)) + rect(l4 (-225 1550) (250 2000)) + rect(l4 (-250 -2000) (250 2000)) + rect(l4 (-250 -5390) (250 1450)) + rect(l8 (-265 150) (180 180)) + rect(l11 (-90 -90) (0 0)) + rect(l11 (-150 -150) (300 300)) + ) + net(7 name(SUBSTRATE)) + net(8 name($I5) + rect(l6 (975 1660) (425 950)) + rect(l6 (-400 -950) (425 950)) + ) + + # Outgoing pins and their connections to nets + pin(1 name(VDD)) + pin(2 name(OUT)) + pin(3 name(VSS)) + pin(4) + pin(5 name(B)) + pin(6 name(A)) + pin(7 name(SUBSTRATE)) + + # Devices and their connections + device(1 D$PMOS + location(850 5800) + param(L 0.25) + param(W 1.5) + param(AS 0.6375) + param(AD 0.3375) + param(PS 3.85) + param(PD 1.95) + terminal(S 2) + terminal(G 6) + terminal(D 1) + terminal(B 4) + ) + device(2 D$PMOS$1 + location(1550 5800) + param(L 0.25) + param(W 1.5) + param(AS 0.3375) + param(AD 0.6375) + param(PS 1.95) + param(PD 3.85) + terminal(S 1) + terminal(G 5) + terminal(D 2) + terminal(B 4) + ) + device(3 D$NMOS + location(850 2135) + param(L 0.25) + param(W 0.95) + param(AS 0.40375) + param(AD 0.21375) + param(PS 2.75) + param(PD 1.4) + terminal(S 3) + terminal(G 6) + terminal(D 8) + terminal(B 7) + ) + device(4 D$NMOS$1 + location(1550 2135) + param(L 0.25) + param(W 0.95) + param(AS 0.21375) + param(AD 0.40375) + param(PS 1.4) + param(PD 2.75) + terminal(S 8) + terminal(G 5) + terminal(D 2) + terminal(B 7) + ) + + ) + circuit(INVX1 + + # Circuit boundary + rect((-100 400) (2000 7600)) + + # Nets with their geometries + net(1 name(VDD) + rect(l8 (410 6260) (180 180)) + rect(l8 (-180 -730) (180 180)) + rect(l8 (-180 -730) (180 180)) + rect(l11 (-240 -240) (300 1400)) + rect(l11 (-650 300) (1800 800)) + rect(l11 (-1450 -1100) (300 300)) + rect(l11 (300 400) (0 0)) + rect(l2 (-650 -2150) (425 1500)) + ) + net(2 name(OUT) + rect(l8 (1110 5160) (180 180)) + rect(l8 (-180 920) (180 180)) + rect(l8 (-180 -730) (180 180)) + rect(l8 (-180 -4120) (180 180)) + rect(l8 (-180 370) (180 180)) + rect(l11 (-240 -790) (300 4790)) + rect(l11 (-150 -2500) (0 0)) + rect(l2 (-225 1050) (425 1500)) + rect(l6 (-425 -4890) (425 950)) + ) + net(3 name(VSS) + rect(l8 (410 1770) (180 180)) + rect(l8 (-180 370) (180 180)) + rect(l11 (-240 -1300) (300 1360)) + rect(l11 (-650 -2160) (1800 800)) + rect(l11 (-850 -400) (0 0)) + rect(l6 (-650 860) (425 950)) + ) + net(4 + rect(l3 (-100 4500) (2000 3500)) + ) + net(5 name(IN) + rect(l4 (725 2860) (250 1940)) + rect(l4 (-525 -1850) (300 300)) + rect(l4 (-25 1550) (250 2000)) + rect(l4 (-250 -2000) (250 2000)) + rect(l4 (-250 -5390) (250 1450)) + rect(l8 (-465 150) (180 180)) + rect(l11 (-90 -90) (0 0)) + rect(l11 (-150 -150) (300 300)) + ) + net(6 name(SUBSTRATE)) + + # Outgoing pins and their connections to nets + pin(1 name(VDD)) + pin(2 name(OUT)) + pin(3 name(VSS)) + pin(4) + pin(5 name(IN)) + pin(6 name(SUBSTRATE)) + + # Devices and their connections + device(1 D$PMOS$2 + location(850 5800) + param(L 0.25) + param(W 1.5) + param(AS 0.6375) + param(AD 0.6375) + param(PS 3.85) + param(PD 3.85) + terminal(S 1) + terminal(G 5) + terminal(D 2) + terminal(B 4) + ) + device(2 D$NMOS$2 + location(850 2135) + param(L 0.25) + param(W 0.95) + param(AS 0.40375) + param(AD 0.40375) + param(PS 2.75) + param(PD 2.75) + terminal(S 3) + terminal(G 5) + terminal(D 2) + terminal(B 6) + ) + + ) + circuit(RINGO + + # Circuit boundary + rect((0 350) (25800 7650)) + + # Nets with their geometries + net(1 + rect(l11 (4040 2950) (610 300)) + ) + net(2 + rect(l11 (5550 2950) (900 300)) + ) + net(3 + rect(l11 (7350 2950) (900 300)) + ) + net(4 + rect(l11 (9150 2950) (900 300)) + ) + net(5 + rect(l11 (10950 2950) (900 300)) + ) + net(6 + rect(l11 (12750 2950) (900 300)) + ) + net(7 + rect(l11 (14550 2950) (900 300)) + ) + net(8 + rect(l11 (16350 2950) (900 300)) + ) + net(9 + rect(l11 (18150 2950) (900 300)) + ) + net(10 + rect(l11 (19950 2950) (900 300)) + ) + net(11 name(FB) + rect(l11 (21750 2950) (900 300)) + rect(l11 (-19530 590) (320 320)) + rect(l11 (17820 -320) (320 320)) + rect(l12 (-18400 -260) (200 200)) + rect(l12 (17940 -200) (200 200)) + rect(l13 (-18040 -300) (17740 400)) + rect(l13 (-17920 -200) (0 0)) + rect(l13 (-220 -200) (400 400)) + rect(l13 (17740 -400) (400 400)) + ) + net(12 name(VDD) + rect(l3 (500 4500) (1400 3500)) + rect(l3 (-1900 -3500) (600 3500)) + rect(l3 (23300 -3500) (1400 3500)) + rect(l3 (-100 -3500) (600 3500)) + rect(l8 (-24690 -1240) (180 180)) + rect(l8 (-180 370) (180 180)) + rect(l8 (-180 -1280) (180 180)) + rect(l8 (23220 370) (180 180)) + rect(l8 (-180 370) (180 180)) + rect(l8 (-180 -1280) (180 180)) + rect(l11 (-21740 860) (0 0)) + rect(l11 (-2350 -450) (1200 800)) + rect(l11 (-750 -1450) (300 1400)) + rect(l11 (-100 -350) (0 0)) + rect(l11 (-1250 -400) (600 800)) + rect(l11 (23400 -800) (1200 800)) + rect(l11 (-750 -1450) (300 1400)) + rect(l11 (-100 -350) (0 0)) + rect(l11 (550 -400) (600 800)) + rect(l9 (-24850 -1500) (500 1500)) + rect(l9 (22900 -1500) (500 1500)) + ) + net(13 name(OUT) + rect(l11 (23440 3840) (320 320)) + rect(l12 (-260 -260) (200 200)) + rect(l13 (-100 -100) (0 0)) + rect(l13 (-200 -200) (400 400)) + ) + net(14 name(ENABLE) + rect(l11 (2440 2940) (320 320)) + rect(l12 (-260 -260) (200 200)) + rect(l13 (-100 -100) (0 0)) + rect(l13 (-200 -200) (400 400)) + ) + net(15 name(VSS) + rect(l8 (1110 1610) (180 180)) + rect(l8 (-180 -1280) (180 180)) + rect(l8 (-180 370) (180 180)) + rect(l8 (23220 370) (180 180)) + rect(l8 (-180 -1280) (180 180)) + rect(l8 (-180 370) (180 180)) + rect(l11 (-21740 -390) (0 0)) + rect(l11 (-1900 -400) (300 1400)) + rect(l11 (-750 -1450) (1200 800)) + rect(l11 (-550 -400) (0 0)) + rect(l11 (-1250 -400) (600 800)) + rect(l11 (23850 -750) (300 1400)) + rect(l11 (-750 -1450) (1200 800)) + rect(l11 (-550 -400) (0 0)) + rect(l11 (550 -400) (600 800)) + rect(l10 (-24850 -800) (500 1500)) + rect(l10 (22900 -1500) (500 1500)) + ) + + # Outgoing pins and their connections to nets + pin(11 name(FB)) + pin(12 name(VDD)) + pin(13 name(OUT)) + pin(14 name(ENABLE)) + pin(15 name(VSS)) + + # Subcircuits and their connections + circuit(1 ND2X1 location(1800 0) + pin(0 12) + pin(1 1) + pin(2 15) + pin(3 12) + pin(4 11) + pin(5 14) + pin(6 15) + ) + circuit(2 INVX1 location(4200 0) + pin(0 12) + pin(1 2) + pin(2 15) + pin(3 12) + pin(4 1) + pin(5 15) + ) + circuit(3 INVX1 location(6000 0) + pin(0 12) + pin(1 3) + pin(2 15) + pin(3 12) + pin(4 2) + pin(5 15) + ) + circuit(4 INVX1 location(7800 0) + pin(0 12) + pin(1 4) + pin(2 15) + pin(3 12) + pin(4 3) + pin(5 15) + ) + circuit(5 INVX1 location(9600 0) + pin(0 12) + pin(1 5) + pin(2 15) + pin(3 12) + pin(4 4) + pin(5 15) + ) + circuit(6 INVX1 location(11400 0) + pin(0 12) + pin(1 6) + pin(2 15) + pin(3 12) + pin(4 5) + pin(5 15) + ) + circuit(7 INVX1 location(13200 0) + pin(0 12) + pin(1 7) + pin(2 15) + pin(3 12) + pin(4 6) + pin(5 15) + ) + circuit(8 INVX1 location(15000 0) + pin(0 12) + pin(1 8) + pin(2 15) + pin(3 12) + pin(4 7) + pin(5 15) + ) + circuit(9 INVX1 location(16800 0) + pin(0 12) + pin(1 9) + pin(2 15) + pin(3 12) + pin(4 8) + pin(5 15) + ) + circuit(10 INVX1 location(18600 0) + pin(0 12) + pin(1 10) + pin(2 15) + pin(3 12) + pin(4 9) + pin(5 15) + ) + circuit(11 INVX1 location(20400 0) + pin(0 12) + pin(1 11) + pin(2 15) + pin(3 12) + pin(4 10) + pin(5 15) + ) + circuit(12 INVX1 location(22200 0) + pin(0 12) + pin(1 13) + pin(2 15) + pin(3 12) + pin(4 11) + pin(5 15) + ) + + ) +) + +# Reference netlist +reference( + + # Device class section + class(PMOS MOS4) + class(NMOS MOS4) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(ND2X1 + + # Nets + net(1 name(VDD)) + net(2 name(OUT)) + net(3 name(VSS)) + net(4 name(NWELL)) + net(5 name(B)) + net(6 name(A)) + net(7 name(BULK)) + net(8 name('1')) + + # Outgoing pins and their connections to nets + pin(1 name(VDD)) + pin(2 name(OUT)) + pin(3 name(VSS)) + pin(4 name(NWELL)) + pin(5 name(B)) + pin(6 name(A)) + pin(7 name(BULK)) + + # Devices and their connections + device(1 PMOS + name($1) + param(L 0.25) + param(W 1.5) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 1) + terminal(G 6) + terminal(D 2) + terminal(B 4) + ) + device(2 PMOS + name($2) + param(L 0.25) + param(W 1.5) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 1) + terminal(G 5) + terminal(D 2) + terminal(B 4) + ) + device(3 NMOS + name($3) + param(L 0.25) + param(W 0.95) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 3) + terminal(G 6) + terminal(D 8) + terminal(B 7) + ) + device(4 NMOS + name($4) + param(L 0.25) + param(W 0.95) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 8) + terminal(G 5) + terminal(D 2) + terminal(B 7) + ) + + ) + circuit(INVX1 + + # Nets + net(1 name(VDD)) + net(2 name(OUT)) + net(3 name(VSS)) + net(4 name(NWELL)) + net(5 name(IN)) + net(6 name(BULK)) + + # Outgoing pins and their connections to nets + pin(1 name(VDD)) + pin(2 name(OUT)) + pin(3 name(VSS)) + pin(4 name(NWELL)) + pin(5 name(IN)) + pin(6 name(BULK)) + + # Devices and their connections + device(1 PMOS + name($1) + param(L 0.25) + param(W 1.5) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 1) + terminal(G 5) + terminal(D 2) + terminal(B 4) + ) + device(2 NMOS + name($2) + param(L 0.25) + param(W 0.95) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 3) + terminal(G 5) + terminal(D 2) + terminal(B 6) + ) + + ) + circuit(RINGO + + # Nets + net(1 name(VSS)) + net(2 name(VDD)) + net(3 name(FB)) + net(4 name(ENABLE)) + net(5 name(OUT)) + net(6 name('1')) + net(7 name('2')) + net(8 name('3')) + net(9 name('4')) + net(10 name('5')) + net(11 name('6')) + net(12 name('7')) + net(13 name('8')) + net(14 name('9')) + net(15 name('10')) + + # Outgoing pins and their connections to nets + pin(1 name(VSS)) + pin(2 name(VDD)) + pin(3 name(FB)) + pin(4 name(ENABLE)) + pin(5 name(OUT)) + + # Subcircuits and their connections + circuit(1 ND2X1 name($1) + pin(0 2) + pin(1 6) + pin(2 1) + pin(3 2) + pin(4 3) + pin(5 4) + pin(6 1) + ) + circuit(2 INVX1 name($2) + pin(0 2) + pin(1 7) + pin(2 1) + pin(3 2) + pin(4 6) + pin(5 1) + ) + circuit(3 INVX1 name($3) + pin(0 2) + pin(1 8) + pin(2 1) + pin(3 2) + pin(4 7) + pin(5 1) + ) + circuit(4 INVX1 name($4) + pin(0 2) + pin(1 9) + pin(2 1) + pin(3 2) + pin(4 8) + pin(5 1) + ) + circuit(5 INVX1 name($5) + pin(0 2) + pin(1 10) + pin(2 1) + pin(3 2) + pin(4 9) + pin(5 1) + ) + circuit(6 INVX1 name($6) + pin(0 2) + pin(1 11) + pin(2 1) + pin(3 2) + pin(4 10) + pin(5 1) + ) + circuit(7 INVX1 name($7) + pin(0 2) + pin(1 12) + pin(2 1) + pin(3 2) + pin(4 11) + pin(5 1) + ) + circuit(8 INVX1 name($8) + pin(0 2) + pin(1 13) + pin(2 1) + pin(3 2) + pin(4 12) + pin(5 1) + ) + circuit(9 INVX1 name($9) + pin(0 2) + pin(1 14) + pin(2 1) + pin(3 2) + pin(4 13) + pin(5 1) + ) + circuit(10 INVX1 name($10) + pin(0 2) + pin(1 15) + pin(2 1) + pin(3 2) + pin(4 14) + pin(5 1) + ) + circuit(11 INVX1 name($11) + pin(0 2) + pin(1 3) + pin(2 1) + pin(3 2) + pin(4 15) + pin(5 1) + ) + circuit(12 INVX1 name($12) + pin(0 2) + pin(1 5) + pin(2 1) + pin(3 2) + pin(4 3) + pin(5 1) + ) + + ) +) + +# Cross reference +xref( + circuit(INVX1 INVX1 match + xref( + net(4 4 match) + net(5 5 match) + net(2 2 match) + net(6 6 match) + net(1 1 match) + net(3 3 match) + pin(3 3 match) + pin(4 4 match) + pin(1 1 match) + pin(5 5 match) + pin(0 0 match) + pin(2 2 match) + device(2 2 match) + device(1 1 match) + ) + ) + circuit(ND2X1 ND2X1 match + xref( + net(8 8 match) + net(4 4 match) + net(6 6 match) + net(5 5 match) + net(2 2 match) + net(7 7 match) + net(1 1 match) + net(3 3 match) + pin(3 3 match) + pin(5 5 match) + pin(4 4 match) + pin(1 1 match) + pin(6 6 match) + pin(0 0 match) + pin(2 2 match) + device(3 3 match) + device(4 4 match) + device(1 1 match) + device(2 2 match) + ) + ) + circuit(RINGO RINGO match + xref( + net(1 6 match) + net(10 15 match) + net(2 7 match) + net(3 8 match) + net(4 9 match) + net(5 10 match) + net(6 11 match) + net(7 12 match) + net(8 13 match) + net(9 14 match) + net(14 4 match) + net(11 3 match) + net(13 5 match) + net(12 2 match) + net(15 1 match) + pin(3 3 match) + pin(0 2 match) + pin(2 4 match) + pin(1 1 match) + pin(4 0 match) + circuit(2 2 match) + circuit(3 3 match) + circuit(4 4 match) + circuit(5 5 match) + circuit(6 6 match) + circuit(7 7 match) + circuit(8 8 match) + circuit(9 9 match) + circuit(10 10 match) + circuit(11 11 match) + circuit(12 12 match) + circuit(1 1 match) + ) + ) +) From a07d742bee3ee50de74f65b0ac11806b5516e0e5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 18 May 2025 00:48:10 +0200 Subject: [PATCH 219/392] Fixed a problem with via uniquification - via definitions may be DEF local, so we need to clean them between different DEF reads. --- .../lefdef/db_plugin/dbDEFImporter.cc | 2 ++ .../lefdef/db_plugin/dbLEFDEFImporter.cc | 20 +++++++++++++++++++ .../lefdef/db_plugin/dbLEFDEFImporter.h | 4 +++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc index 3aa4d79c7..a47f99d32 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc @@ -1182,8 +1182,10 @@ DEFImporter::read_vias (db::Layout &layout, db::Cell & /*design*/, double scale) if (rule_based_vg.get () && geo_based_vg.get ()) { error (tl::to_string (tr ("A via can only be defined through a VIARULE or geometry, not both ways"))); } else if (rule_based_vg.get ()) { + rule_based_vg->def_local = true; reader_state ()->register_via_cell (n, std::string (), rule_based_vg.release ()); } else if (geo_based_vg.get ()) { + geo_based_vg->def_local = true; reader_state ()->register_via_cell (n, std::string (), geo_based_vg.release ()); } else { error (tl::to_string (tr ("Too little information to generate a via"))); diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc index 3059105da..4d1ecba24 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc @@ -1810,9 +1810,29 @@ std::set LEFDEFReaderState::open_layer_uncached(db::Layout &layout void LEFDEFReaderState::start () { + // Start over for a new DEF file - this function is used in LEF context mode + // i.e. when LEFs are cached during multiple DEF reads. It is called when a new DEF is read. + CommonReaderBase::start (); m_foreign_cells.clear (); + + // Remove the via generators that were added by DEF + // TODO: there is no concept for "local LEFs" currently. Even LEFs stored along + // with DEFs are considered "global". + for (auto vg = m_via_generators.begin (); vg != m_via_generators.end (); ) { + auto vg_here = vg; + ++vg; + if (vg_here->second->def_local) { + delete vg_here->second; + m_via_generators.erase (vg_here); + } + } + + // We always create fresh via cells for different DEFs to avoid potential + // content conflicts. Problem is: vias can be generated by both LEF (global) + // and DEF (local) + m_via_cells.clear (); } void diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h index ddb90415e..5c2a2a346 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h @@ -1141,12 +1141,14 @@ public: class DB_PLUGIN_PUBLIC LEFDEFLayoutGenerator { public: - LEFDEFLayoutGenerator () { } + LEFDEFLayoutGenerator () : def_local (false) { } virtual ~LEFDEFLayoutGenerator () { } virtual void create_cell (LEFDEFReaderState &reader, db::Layout &layout, db::Cell &cell, const std::vector *maskshift_layers, const std::vector &masks, const LEFDEFNumberOfMasks *nm) = 0; virtual std::vector maskshift_layers () const = 0; virtual bool is_fixedmask () const = 0; + + bool def_local; }; /** From 4306b24b4a6bca7a9f41978269db3a801c3d7098 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 18 May 2025 09:27:10 +0200 Subject: [PATCH 220/392] Normalizing test case for RNetExtractor to reduce jitter --- src/pex/unit_tests/pexRNetExtractorTests.cc | 91 +++++++++++++-------- 1 file changed, 59 insertions(+), 32 deletions(-) diff --git a/src/pex/unit_tests/pexRNetExtractorTests.cc b/src/pex/unit_tests/pexRNetExtractorTests.cc index 9b4d0bd49..2b6f9f083 100644 --- a/src/pex/unit_tests/pexRNetExtractorTests.cc +++ b/src/pex/unit_tests/pexRNetExtractorTests.cc @@ -37,6 +37,33 @@ public: using pex::RNetExtractor::create_via_ports; }; +static std::string network2s (const pex::RNetwork &network) +{ + std::vector r; + + for (auto e = network.begin_elements (); e != network.end_elements (); ++e) { + + const pex::RElement &element = *e; + + std::string na = (element.a ()->type != pex::RNode::Internal ? element.a ()->to_string () : "") + + element.a ()->location.to_string (); + std::string nb = (element.b ()->type != pex::RNode::Internal ? element.b ()->to_string () : "") + + element.b ()->location.to_string (); + + if (nb < na) { + std::swap (na, nb); + } + + std::string s = "R " + na + " " + nb + " " + tl::to_string (element.resistance ()); + r.push_back (s); + + } + + std::sort (r.begin (), r.end ()); + + return tl::join (r, "\n"); +} + TEST(netex_viagen1) { db::Layout ly; @@ -78,11 +105,11 @@ TEST(netex_viagen1) EXPECT_EQ (via_ports [l2].size (), size_t (0)); EXPECT_EQ (via_ports [l3].size (), size_t (4)); - EXPECT_EQ (network.to_string (true), - "R $0.1(1.7,0.1;1.9,0.3) $1.2(1.7,0.1;1.9,0.3) 50\n" - "R $2.1(0.4,0.5;0.6,0.7) $3.2(0.4,0.5;0.6,0.7) 50\n" - "R $4.1(0.8,0.5;1,0.7) $5.2(0.8,0.5;1,0.7) 50\n" - "R $6.1(2.9,0.5;3.1,0.7) $7.2(2.9,0.5;3.1,0.7) 50" + EXPECT_EQ (network2s (network), + "R (0.4,0.5;0.6,0.7) (0.4,0.5;0.6,0.7) 50\n" + "R (0.8,0.5;1,0.7) (0.8,0.5;1,0.7) 50\n" + "R (1.7,0.1;1.9,0.3) (1.7,0.1;1.9,0.3) 50\n" + "R (2.9,0.5;3.1,0.7) (2.9,0.5;3.1,0.7) 50" ); } @@ -128,13 +155,13 @@ TEST(netex_viagen2) EXPECT_EQ (via_ports [l2].size (), size_t (0)); EXPECT_EQ (via_ports [l3].size (), size_t (6)); - EXPECT_EQ (network.to_string (true), - "R $0.1(4.6,2.8;4.8,3) $1.2(4.6,2.8;4.8,3) 50\n" - "R $2.1(2.5,3.7;2.7,3.9) $3.2(2.5,3.7;2.7,3.9) 50\n" - "R $4.1(3,3.7;3.2,3.9) $5.2(3,3.7;3.2,3.9) 50\n" - "R $6.1(2.2,1.2;3.4,3.4) $7.2(2.2,1.2;3.4,3.4) 2.77778\n" - "R $8.1(0.4,0.4;2.2,4.2) $9.2(0.4,0.4;2.2,4.2) 1\n" - "R $10.1(0.6,4.9;1.2,5.1) $11.2(0.6,4.9;1.2,5.1) 25" + EXPECT_EQ (network2s (network), + "R (0.4,0.4;2.2,4.2) (0.4,0.4;2.2,4.2) 1\n" + "R (0.6,4.9;1.2,5.1) (0.6,4.9;1.2,5.1) 25\n" + "R (2.2,1.2;3.4,3.4) (2.2,1.2;3.4,3.4) 2.77777777778\n" + "R (2.5,3.7;2.7,3.9) (2.5,3.7;2.7,3.9) 50\n" + "R (3,3.7;3.2,3.9) (3,3.7;3.2,3.9) 50\n" + "R (4.6,2.8;4.8,3) (4.6,2.8;4.8,3) 50" ); } @@ -220,32 +247,32 @@ TEST(netex_2layer) rex.extract (tech, geo, vertex_ports, polygon_ports, network); - EXPECT_EQ (network.to_string (true), - "R $0.1(0.3,-5.7;0.5,-5.5) $1.2(0.3,-5.7;0.5,-5.5) 50\n" - "R $2.1(9.3,-5.9;9.9,-5.3) $3.2(9.3,-5.9;9.9,-5.3) 12.5\n" - "R $4.1(9.3,0.1;9.9,0.3) $5.2(9.3,0.1;9.9,0.3) 25\n" - "R $6.1(0.1,0.1;0.7,0.7) $7.2(0.1,0.1;0.7,0.7) 12.5\n" - "R $0.1(0.3,-5.7;0.5,-5.5) $2.1(9.3,-5.9;9.9,-5.3) 5.75\n" - "R $2.1(9.3,-5.9;9.9,-5.3) P0.1(12.9,-5.9;13.5,-5.3) 2.25\n" - "R $6.1(0.1,0.1;0.7,0.7) V0.1(5.2,0.4;5.2,0.4) 3\n" - "R $4.1(9.3,0.1;9.9,0.3) V0.1(5.2,0.4;5.2,0.4) 2.75\n" - "R $5.2(9.3,0.1;9.9,0.3) $8.2(10,-3.5;10,-2.7) 1.03125\n" - "R $3.2(9.3,-5.9;9.9,-5.3) $8.2(10,-3.5;10,-2.7) 0.78125\n" - "R $8.2(10,-3.5;10,-2.7) P0.2(12.9,-3.4;13.5,-2.8) 1\n" - "R $7.2(0.1,0.1;0.7,0.7) V0.2(0.4,-5.6;0.4,-5.6) 1.875\n" - "R $1.2(0.3,-5.7;0.5,-5.5) V0.2(0.4,-5.6;0.4,-5.6) 0" + EXPECT_EQ (network2s (network), + "R (0.1,0.1;0.7,0.7) (0.1,0.1;0.7,0.7) 12.5\n" + "R (0.1,0.1;0.7,0.7) V0.1(5.2,0.4;5.2,0.4) 3\n" + "R (0.1,0.1;0.7,0.7) V0.2(0.4,-5.6;0.4,-5.6) 1.875\n" + "R (0.3,-5.7;0.5,-5.5) (0.3,-5.7;0.5,-5.5) 50\n" + "R (0.3,-5.7;0.5,-5.5) (9.3,-5.9;9.9,-5.3) 5.75\n" + "R (0.3,-5.7;0.5,-5.5) V0.2(0.4,-5.6;0.4,-5.6) 0\n" + "R (10,-3.5;10,-2.7) (9.3,-5.9;9.9,-5.3) 0.78125\n" + "R (10,-3.5;10,-2.7) (9.3,0.1;9.9,0.3) 1.03125\n" + "R (10,-3.5;10,-2.7) P0.2(12.9,-3.4;13.5,-2.8) 1\n" + "R (9.3,-5.9;9.9,-5.3) (9.3,-5.9;9.9,-5.3) 12.5\n" + "R (9.3,-5.9;9.9,-5.3) P0.1(12.9,-5.9;13.5,-5.3) 2.25\n" + "R (9.3,0.1;9.9,0.3) (9.3,0.1;9.9,0.3) 25\n" + "R (9.3,0.1;9.9,0.3) V0.1(5.2,0.4;5.2,0.4) 2.75" ); tech.skip_simplify = false; rex.extract (tech, geo, vertex_ports, polygon_ports, network); - EXPECT_EQ (network.to_string (true), - "R $2.1(9.3,-5.9;9.9,-5.3) P0.1(12.9,-5.9;13.5,-5.3) 2.25\n" - "R $8.2(10,-3.5;10,-2.7) P0.2(12.9,-3.4;13.5,-2.8) 1\n" - "R $2.1(9.3,-5.9;9.9,-5.3) V0.2(0.3,-5.7;0.5,-5.5) 55.75\n" - "R $2.1(9.3,-5.9;9.9,-5.3) $8.2(10,-3.5;10,-2.7) 13.2813\n" - "R $8.2(10,-3.5;10,-2.7) V0.1(5.2,0.4;5.2,0.4) 28.7812\n" + EXPECT_EQ (network2s (network), + "R (10,-3.5;10,-2.7) (9.3,-5.9;9.9,-5.3) 13.28125\n" + "R (10,-3.5;10,-2.7) P0.2(12.9,-3.4;13.5,-2.8) 1\n" + "R (10,-3.5;10,-2.7) V0.1(5.2,0.4;5.2,0.4) 28.78125\n" + "R (9.3,-5.9;9.9,-5.3) P0.1(12.9,-5.9;13.5,-5.3) 2.25\n" + "R (9.3,-5.9;9.9,-5.3) V0.2(0.3,-5.7;0.5,-5.5) 55.75\n" "R V0.1(5.2,0.4;5.2,0.4) V0.2(0.3,-5.7;0.5,-5.5) 17.375" ); } From 72c716f38d6b614de4a35f7af2c1f43326ed1264 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 18 May 2025 17:04:21 +0200 Subject: [PATCH 221/392] More robust tests. --- testdata/ruby/pexTests.rb | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/testdata/ruby/pexTests.rb b/testdata/ruby/pexTests.rb index e5e98a878..2e11f42de 100644 --- a/testdata/ruby/pexTests.rb +++ b/testdata/ruby/pexTests.rb @@ -242,12 +242,15 @@ class PEX_TestClass < TestBase network = rex.extract(tech, geo, vertex_ports, polygon_ports) - assert_equal(network.to_s(true) + "\n", <<"END") -R $2.1(9.3,-5.9;9.9,-5.3) P0.1(12.9,-5.9;13.5,-5.3) 2.25 -R $8.2(10,-3.5;10,-2.7) P0.2(12.9,-3.4;13.5,-2.8) 1 -R $2.1(9.3,-5.9;9.9,-5.3) V0.2(0.3,-5.7;0.5,-5.5) 55.75 -R $2.1(9.3,-5.9;9.9,-5.3) $8.2(10,-3.5;10,-2.7) 13.2813 -R $8.2(10,-3.5;10,-2.7) V0.1(5.2,0.4;5.2,0.4) 28.7812 + n = network.to_s(true) + n = n.gsub(/ \$\d+\./, " $x.") + n = n.split("\n").sort.join("\n") + "\n" + assert_equal(n, <<"END") +R $x.1(9.3,-5.9;9.9,-5.3) $x.2(10,-3.5;10,-2.7) 13.2813 +R $x.1(9.3,-5.9;9.9,-5.3) P0.1(12.9,-5.9;13.5,-5.3) 2.25 +R $x.1(9.3,-5.9;9.9,-5.3) V0.2(0.3,-5.7;0.5,-5.5) 55.75 +R $x.2(10,-3.5;10,-2.7) P0.2(12.9,-3.4;13.5,-2.8) 1 +R $x.2(10,-3.5;10,-2.7) V0.1(5.2,0.4;5.2,0.4) 28.7812 R V0.1(5.2,0.4;5.2,0.4) V0.2(0.3,-5.7;0.5,-5.5) 17.375 END From 8222085efe155d4cff6393f925e1b21fa3d9a9ed Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 18 May 2025 17:08:35 +0200 Subject: [PATCH 222/392] Fixing tests on Windows --- testdata/lvs/ringo_simple_io2.l2n.2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testdata/lvs/ringo_simple_io2.l2n.2 b/testdata/lvs/ringo_simple_io2.l2n.2 index d0a07ab94..871a19bb3 100644 --- a/testdata/lvs/ringo_simple_io2.l2n.2 +++ b/testdata/lvs/ringo_simple_io2.l2n.2 @@ -175,7 +175,7 @@ X(ND2X1 R(l11 (-150 -150) (300 300)) ) N(7 I(SUBSTRATE)) - N(8 I($I3) + N(8 I($I5) R(l6 (975 1660) (425 950)) R(l6 (-400 -950) (425 950)) ) From e27e24ff4f6b727e5c7c8fe0c156ed9dc34e6866 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 18 May 2025 22:00:39 +0200 Subject: [PATCH 223/392] Bugfixing multi-DEF reader with LEF cache, adding a testcase. Making unknown vias an error. --- .../lefdef/db_plugin/dbLEFDEFImporter.cc | 56 ++++++----- .../lefdef/db_plugin/dbLEFDEFImporter.h | 3 +- .../lefdef/unit_tests/dbLEFDEFImportTests.cc | 33 +++++++ testdata/lefdef/multi_def/au.oas | Bin 0 -> 980 bytes testdata/lefdef/multi_def/comp_a.def | 20 ++++ testdata/lefdef/multi_def/comp_a.lef | 12 +++ testdata/lefdef/multi_def/comp_b.def | 20 ++++ testdata/lefdef/multi_def/comp_b.lef | 12 +++ testdata/lefdef/multi_def/comp_c.def | 16 ++++ testdata/lefdef/multi_def/comp_c.lef | 12 +++ testdata/lefdef/multi_def/layers.map | 7 ++ testdata/lefdef/multi_def/main.def | 13 +++ testdata/lefdef/multi_def/out.oas | Bin 0 -> 980 bytes testdata/lefdef/multi_def/tech.lef | 89 ++++++++++++++++++ 14 files changed, 270 insertions(+), 23 deletions(-) create mode 100644 testdata/lefdef/multi_def/au.oas create mode 100644 testdata/lefdef/multi_def/comp_a.def create mode 100644 testdata/lefdef/multi_def/comp_a.lef create mode 100644 testdata/lefdef/multi_def/comp_b.def create mode 100644 testdata/lefdef/multi_def/comp_b.lef create mode 100644 testdata/lefdef/multi_def/comp_c.def create mode 100644 testdata/lefdef/multi_def/comp_c.lef create mode 100644 testdata/lefdef/multi_def/layers.map create mode 100644 testdata/lefdef/multi_def/main.def create mode 100644 testdata/lefdef/multi_def/out.oas create mode 100644 testdata/lefdef/multi_def/tech.lef diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc index 4d1ecba24..dbbe55cf1 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc @@ -1947,48 +1947,52 @@ LEFDEFReaderState::finish (db::Layout &layout) void LEFDEFReaderState::register_via_cell (const std::string &vn, const std::string &nondefaultrule, LEFDEFLayoutGenerator *generator) { - if (m_via_generators.find (std::make_pair (vn, nondefaultrule)) != m_via_generators.end ()) { - delete m_via_generators [std::make_pair (vn, nondefaultrule)]; - } - m_via_generators [std::make_pair (vn, nondefaultrule)] = generator; + // inserts at the end of the range + m_via_generators.insert (std::make_pair (std::make_pair (vn, nondefaultrule), generator)); } LEFDEFLayoutGenerator * LEFDEFReaderState::via_generator (const std::string &vn, const std::string &nondefaultrule) { - std::map, LEFDEFLayoutGenerator *>::const_iterator g = m_via_generators.find (std::make_pair (vn, nondefaultrule)); - if (g == m_via_generators.end () && ! nondefaultrule.empty ()) { - // default rule is fallback - g = m_via_generators.find (std::make_pair (vn, std::string ())); + return via_generator_and_rule (vn, nondefaultrule).first; +} + +std::pair +LEFDEFReaderState::via_generator_and_rule (const std::string &vn, const std::string &nondefaultrule) +{ + auto key = std::make_pair (vn, nondefaultrule); + + auto g = m_via_generators.upper_bound (key); + if (g != m_via_generators.begin ()) { + --g; } - if (g != m_via_generators.end ()) { - return g->second; + + if (g == m_via_generators.end () || g->first != key) { + if (nondefaultrule.empty ()) { + return std::pair (0, std::string ()); + } else { + // default rule is fallback + return via_generator_and_rule (vn, std::string ()); + } } else { - return 0; + return std::make_pair (g->second, nondefaultrule); } } db::Cell * LEFDEFReaderState::via_cell (const std::string &vn, const std::string &nondefaultrule, db::Layout &layout, unsigned int mask_bottom, unsigned int mask_cut, unsigned int mask_top, const LEFDEFNumberOfMasks *nm) { - ViaKey vk (vn, nondefaultrule, mask_bottom, mask_cut, mask_top); + auto gr = via_generator_and_rule (vn, nondefaultrule); + LEFDEFLayoutGenerator *vg = gr.first; - std::map, LEFDEFLayoutGenerator *>::const_iterator g = m_via_generators.find (std::make_pair (vn, nondefaultrule)); - - if (g == m_via_generators.end () && ! vk.nondefaultrule.empty ()) { - // default rule is fallback - g = m_via_generators.find (std::make_pair (vn, std::string ())); - vk.nondefaultrule.clear (); - } + ViaKey vk (vn, gr.second, mask_bottom, mask_cut, mask_top); std::map::const_iterator i = m_via_cells.find (vk); if (i == m_via_cells.end ()) { db::Cell *cell = 0; - if (g != m_via_generators.end ()) { - - LEFDEFLayoutGenerator *vg = g->second; + if (vg) { std::string n = vn; @@ -2017,6 +2021,14 @@ LEFDEFReaderState::via_cell (const std::string &vn, const std::string &nondefaul vg->create_cell (*this, layout, *cell, 0, masks, nm); + } else { + + std::string details; + if (! nondefaultrule.empty () && nondefaultrule != vk.nondefaultrule) { + details = tl::sprintf (tl::to_string (tr (" (trying with NONDEFAULTRULE '%s' and without)")), nondefaultrule); + } + error (tl::sprintf (tl::to_string (tr ("Could not find a via specification with name '%s'")) + details, vn)); + } m_via_cells[vk] = cell; diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h index 5c2a2a346..d801c04b2 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h @@ -1526,7 +1526,7 @@ private: std::map m_default_number; const LEFDEFReaderOptions *mp_tech_comp; std::map m_via_cells; - std::map, LEFDEFLayoutGenerator *> m_via_generators; + std::multimap, LEFDEFLayoutGenerator *> m_via_generators; std::map > m_macro_cells; std::map m_macro_generators; std::map m_foreign_cells; @@ -1538,6 +1538,7 @@ private: std::set open_layer_uncached (db::Layout &layout, const std::string &name, LayerPurpose purpose, unsigned int mask); db::cell_index_type foreign_cell(Layout &layout, const std::string &name); void read_single_map_file (const std::string &path, std::map, std::vector > &layer_map); + std::pair via_generator_and_rule (const std::string &vn, const std::string &nondefaultrule); }; /** diff --git a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc index 9ccb295a4..fb786413f 100644 --- a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc +++ b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc @@ -25,6 +25,7 @@ #include "dbWriter.h" #include "dbDEFImporter.h" #include "dbLEFImporter.h" +#include "dbCommonReader.h" #include "tlUnitTest.h" #include "dbTestSupport.h" @@ -1102,3 +1103,35 @@ TEST(214_issue1877) db::compare_layouts (_this, ly, fn_path + "au.oas", db::WriteOAS); } +// multi-DEF reader support (issue-2014) +TEST(215_multiDEF) +{ + std::string fn_path (tl::testdata ()); + fn_path += "/lefdef/multi_def/"; + + db::Layout ly; + + db::LoadLayoutOptions opt; + // anything else will not make much sense + opt.get_options ().cell_conflict_resolution = db::CellConflictResolution::RenameCell; + + // Test "set_option_by_name" + opt.set_option_by_name ("lefdef_config.lef_context_enabled", true); + opt.set_option_by_name ("lefdef_config.map_file", "layers.map"); + opt.set_option_by_name ("lefdef_config.read_lef_with_def", true); + + const char *files[] = { + "main.def", + "comp_a.def", + "comp_b.def", + "comp_c.def" + }; + + for (const char **fn = files; fn != files + sizeof (files) / sizeof (files[0]); ++fn) { + tl::InputStream is (fn_path + *fn); + db::Reader reader (is); + reader.read (ly, opt); + } + + db::compare_layouts (_this, ly, fn_path + "au.oas", db::WriteOAS); +} diff --git a/testdata/lefdef/multi_def/au.oas b/testdata/lefdef/multi_def/au.oas new file mode 100644 index 0000000000000000000000000000000000000000..3b6e663675cd55e565e8efcce65c6a068de4517d GIT binary patch literal 980 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKC?n3q!6L)YEF;ds&!EJR>XUoMnybM;fb~F; z;|1o9>4KX(8~>b$4qRRSKbqrK6n{_8gJqfe+C@(E01b3CypJk0~AJy4vpC7rjNt zSg>0}I6yj2GtY2=h}rZF4LOH(*%^3wM6H<_7@3v`eGu5mF_}};l9iF^ir^K&*Bq}o zL~U6ZnIY2a#PUC1VC$;`;qz}3Ndl%p31V3;>lyTrw#whrK2cnD{pE3r)3(N=X1TQcxkQcnb$RKhUWUUgzWX6Q# zM1MaWFCFi*i4z3VAM7}J=+Lw|^P>gkpWt|u*Hk!phQpma3G5R?JvSWYq@K~qQIHgM<`ejCEj%N8Nzw_+;R4CduxWH!_@V0gjKC?n3q!6L)YEF;ds&!EJR>XUoMnybM;fb~F; z;|1o9>4KX(8~>b$4qRRSKbqrK6n{_8gJqfe+C@(E01b3CypJk0~AJy4vpC7rjNt zSg>0}I6yj2GtY2=h}rZF4LOH(*%^3wM6H<_7@3v`eGu5mF_}};l9iF^ir^K&*Bq}o zL~U6ZnIY2a#PUC1VC$;`;qz}3Ndl%p31V3;>lyTrw#whrK2cnD{pE3r)3(N=X1TQcxkQcnb$RKhUWUUgzWX6Q# zM1MaWFCFi*i4z3VAM7}J=+Lw|^P>gkpWt|u*Hk!phQpma3G5R?JvSWYq@K~qQIHgM<`ejCEj%N8Nzw_ Date: Sun, 18 May 2025 22:16:13 +0200 Subject: [PATCH 224/392] Postpone decision about "invalid vias are errors." --- .../lefdef/db_plugin/dbDEFImporter.cc | 7 +- .../lefdef/unit_tests/dbLEFDEFImportTests.cc | 15 +++ .../lefdef/invalid_via/comp_invalid_via.def | 16 ++++ testdata/lefdef/invalid_via/tech.lef | 89 ++++++++++++++++++ testdata/lefdef/multi_def/au.oas | Bin 980 -> 1054 bytes 5 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 testdata/lefdef/invalid_via/comp_invalid_via.def create mode 100644 testdata/lefdef/invalid_via/tech.lef diff --git a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc index a47f99d32..b07a347b0 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc @@ -749,7 +749,12 @@ DEFImporter::read_single_net (std::string &nondefaultrule, Layout &layout, db::C } std::map::const_iterator vd = m_via_desc.find (vn); - if (vd != m_via_desc.end () && ! pts.empty ()) { + + if (vd == m_via_desc.end ()) { + + warn (tl::to_string (tr ("Invalid via name: ")) + vn); + + } else if (! pts.empty ()) { // For the via, the masks are encoded in a three-digit number ( ) unsigned int mask_top = (mask / 100) % 10; diff --git a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc index fb786413f..f430e4829 100644 --- a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc +++ b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc @@ -898,6 +898,21 @@ TEST(132_issue1307_pin_names) run_test (_this, "issue-1307c", "lef:in.lef+def:in.def", "au.oas", opt, false); } +/* +TODO: need to clarify first, if invalid via specs should be errors +TEST(133_unknown_vias_are_errors) +{ + db::LEFDEFReaderOptions opt = default_options (); + + try { + run_test (_this, "invalid_via", "lef:tech.lef+def:comp_invalid_via.def", "au.oas", opt, false); + EXPECT_EQ (true, false); + } catch (db::LEFDEFReaderException &ex) { + EXPECT_EQ (ex.msg ().find ("Invalid via name"), size_t (0)); + } +} +*/ + TEST(200_lefdef_plugin) { db::Layout ly; diff --git a/testdata/lefdef/invalid_via/comp_invalid_via.def b/testdata/lefdef/invalid_via/comp_invalid_via.def new file mode 100644 index 000000000..1eb475daf --- /dev/null +++ b/testdata/lefdef/invalid_via/comp_invalid_via.def @@ -0,0 +1,16 @@ +VERSION 5.8 ; +DIVIDERCHAR "/" ; +BUSBITCHARS "[]" ; +DESIGN comp_invalid_via ; +UNITS DISTANCE MICRONS 1000 ; +DIEAREA ( 0 0 ) ( 10000 2000 ) ; + +SPECIALNETS 2 ; + - VGND ( PIN VGND ) ( * VNB ) ( * VGND ) + USE GROUND + + ROUTED met1 480 + SHAPE FOLLOWPIN ( 0 1000 ) ( 2000 1000 ) + NEW met1 0 + SHAPE STRIPE ( 2000 1000 ) via1 + + ROUTED met2 480 + SHAPE FOLLOWPIN ( 2000 1000 ) ( 4000 1000 ) + NEW met2 0 + SHAPE STRIPE ( 4000 1000 ) invalid_via ; +END SPECIALNETS +END DESIGN + diff --git a/testdata/lefdef/invalid_via/tech.lef b/testdata/lefdef/invalid_via/tech.lef new file mode 100644 index 000000000..cae5bcd54 --- /dev/null +++ b/testdata/lefdef/invalid_via/tech.lef @@ -0,0 +1,89 @@ + +VERSION 5.7 ; + +BUSBITCHARS "[]" ; +DIVIDERCHAR "/" ; +USEMINSPACING OBS OFF ; + +UNITS + DATABASE MICRONS 1000 ; +END UNITS + +MANUFACTURINGGRID 0.005 ; + +LAYER met1 + TYPE ROUTING ; + DIRECTION HORIZONTAL ; + PITCH 0.34 ; + OFFSET 0.17 ; + WIDTH 0.14 ; +END met1 + +LAYER via + TYPE CUT ; + WIDTH 0.15 ; + SPACING 0.17 ; +END via + +LAYER met2 + TYPE ROUTING ; + DIRECTION VERTICAL ; + PITCH 0.46 ; + OFFSET 0.23 ; + WIDTH 0.14 ; +END met2 + +LAYER via2 + TYPE CUT ; + WIDTH 0.2 ; + SPACING 0.2 ; +END via2 + +LAYER met3 + TYPE ROUTING ; + DIRECTION HORIZONTAL ; + PITCH 0.68 ; + OFFSET 0.34 ; + WIDTH 0.3 ; +END met3 + +VIA via1 DEFAULT + LAYER via ; + RECT -0.075 -0.075 0.075 0.075 ; + LAYER met1 ; + RECT -0.16 -0.24 0.16 0.24 ; + LAYER met2 ; + RECT -0.13 -0.24 0.13 0.24 ; +END via1 + +VIARULE M1M2_PR GENERATE + LAYER met1 ; + ENCLOSURE 0.085 0.055 ; + LAYER met2 ; + ENCLOSURE 0.055 0.085 ; + LAYER via ; + RECT -0.075 -0.075 0.075 0.075 ; + SPACING 0.32 BY 0.32 ; +END M1M2_PR + +VIA via2 DEFAULT + LAYER via2 ; + RECT -0.1 -0.1 0.1 0.1 ; + LAYER met2 ; + RECT -0.14 -0.24 0.14 0.24 ; + LAYER met3 ; + RECT -0.165 -0.165 0.165 0.165 ; +END via2 + +VIARULE M2M3_PR GENERATE + LAYER met2 ; + ENCLOSURE 0.04 0.085 ; + LAYER met3 ; + ENCLOSURE 0.065 0.065 ; + LAYER via2 ; + RECT -0.1 -0.1 0.1 0.1 ; + SPACING 0.4 BY 0.4 ; +END M2M3_PR + +END LIBRARY + diff --git a/testdata/lefdef/multi_def/au.oas b/testdata/lefdef/multi_def/au.oas index 3b6e663675cd55e565e8efcce65c6a068de4517d..4f1002177a8d410edd5fe2bc9136b840a45c8f47 100644 GIT binary patch delta 304 zcmcb@K96HUv62#ly4>Rwe?J{B9q+RbQ#gyi7e7q#K6hTX!@uQ{XMmL?!^E1z$txJ; z8M!B)V3eKg%9uRyt~n#?WKl)~Zf3y`JO}J9@C!cRp8SDPj8{gTk%N(+QB06|fxPTw zZYCvu6cJv=$QTiQ1TQcju!D%P2wq@XATOf`G=y330wV*M#Uye#g<*o962of7 zwB$snOA{vurayR*l$r=}Y6?qX{$_`ro<0iiq$JE|N*?)p`phB0XD=nF@jUKUOyM(14VHWyzh=YNNaWg9; U1EVryG2>Lm$q$%5GBGd!0A&1IEC2ui delta 249 zcmbQoafN+CF()T`a$-?pS!&M2sfm-PF`6@SPd>+JFxi+fdEz&Dc~+5XMur1S510=! zJ!BRMWNu+vgO5b`olj%89{6%%A$AU|1$Nr@fI;$@utpHZP6!e$V>ze3?he97$*2BF-&GmNKW+k)A7>rKASi}F#W-flZOsXn=?OJVEze?M|n+! zlV>>G$&)ajDS4#!(3wL)(`IIz;Cdv}T+nmFVNU89og4*8QD;7Z@7BUIvX>;C;Cv*P w*mO#&uzAPi#7%BC3h#2=_!*cO-?K0>Fe)<^GuALNOg3bGJ=uYom5G4?0HlUf(*OVf From 9b03a1ba642ff57a207cf72681d983a12638dc2e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 18 May 2025 22:41:46 +0200 Subject: [PATCH 225/392] More robust tests --- .../pexSquareCountingRExtractorTests.cc | 39 ++++++++++++++++--- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc b/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc index e3b6e057e..5b566152d 100644 --- a/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc +++ b/src/pex/unit_tests/pexSquareCountingRExtractorTests.cc @@ -41,6 +41,33 @@ public: } +static std::string network2s (const pex::RNetwork &network) +{ + std::vector r; + + for (auto e = network.begin_elements (); e != network.end_elements (); ++e) { + + const pex::RElement &element = *e; + + std::string na = (element.a ()->type != pex::RNode::Internal ? element.a ()->to_string () : "") + + element.a ()->location.to_string (); + std::string nb = (element.b ()->type != pex::RNode::Internal ? element.b ()->to_string () : "") + + element.b ()->location.to_string (); + + if (nb < na) { + std::swap (na, nb); + } + + std::string s = "R " + na + " " + nb + " " + tl::to_string (element.resistance ()); + r.push_back (s); + + } + + std::sort (r.begin (), r.end ()); + + return tl::join (r, "\n"); +} + TEST(basic) { db::Point contour[] = { @@ -134,10 +161,10 @@ TEST(extraction) rex.extract (poly, vertex_ports, polygon_ports, rn); - EXPECT_EQ (rn.to_string (), - "R $0 V0 10.5\n" - "R $0 V1 6\n" - "R $0 P0 8.5" + EXPECT_EQ (network2s (rn), + "R (1,0.1;1.1,0.1) P0(1,0.9;1.1,1) 8.5\n" + "R (1,0.1;1.1,0.1) V0(0,0.05;0,0.05) 10.5\n" + "R (1,0.1;1.1,0.1) V1(1.65,0.05;1.65,0.05) 6" ) } @@ -182,7 +209,7 @@ TEST(extraction_meander) rex.extract (poly, vertex_ports, polygon_ports, rn); - EXPECT_EQ (rn.to_string (), - "R V0 V1 10.0544" // that is pretty much the length of the center line / width :) + EXPECT_EQ (network2s (rn), + "R V0(0.3,0;0.3,0) V1(4.3,1;4.3,1) 10.0543767445" // that is pretty much the length of the center line / width :) ) } From f9e2a9257fd11f2a67ff725c8c0c1eb548a23b76 Mon Sep 17 00:00:00 2001 From: Henner Zeller Date: Mon, 19 May 2025 14:15:31 +0200 Subject: [PATCH 226/392] Fix logic error in hex2int. This was probably never noticed as it was the last branch and would behave benign on valid input. Signed-off-by: Henner Zeller --- src/tl/tl/tlUri.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tl/tl/tlUri.cc b/src/tl/tl/tlUri.cc index d0a6aade3..8032a2cc2 100644 --- a/src/tl/tl/tlUri.cc +++ b/src/tl/tl/tlUri.cc @@ -41,7 +41,7 @@ static char hex2int (char c) return c - '0'; } else if (c >= 'A' && c <= 'F') { return (c - 'A') + 10; - } else if (c >= 'a' || c <= 'f') { + } else if (c >= 'a' && c <= 'f') { return (c - 'a') + 10; } else { return 0; From 6ee824cb3cd6cf529e73ae72fa91527a29d3c66b Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Wed, 21 May 2025 16:02:33 +0900 Subject: [PATCH 227/392] adding arm linux to os matrix --- .github/workflows/build.yml | 52 ++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d9f6a3b9b..a49910d91 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,6 +34,10 @@ jobs: - os: "ubuntu-latest" cibuild: "*musllinux*" cibw_arch: "musllinux" + - os: "ubuntu-24.04-arm" # aarch64 manylinux on ARM runner + cibuild: "*manylinux*" + cibw_arch: "manylinux" + steps: - name: Free Disk Space (Ubuntu) if: matrix.os == 'ubuntu-latest' @@ -90,43 +94,43 @@ jobs: name: Make SDist runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Build SDist - run: pipx run build --sdist + - name: Build SDist + run: pipx run build --sdist - - uses: actions/upload-artifact@v4 - with: - name: artifact-sdist - path: dist/*.tar.gz + - uses: actions/upload-artifact@v4 + with: + name: artifact-sdist + path: dist/*.tar.gz upload_to_test_pypy: needs: [build, make_sdist] runs-on: ubuntu-latest steps: - - uses: actions/download-artifact@v4 - with: - merge-multiple: true - path: dist + - uses: actions/download-artifact@v4 + with: + merge-multiple: true + path: dist - - uses: pypa/gh-action-pypi-publish@v1.12.4 + - uses: pypa/gh-action-pypi-publish@v1.12.4 continue-on-error: true # might fail if we don't bump the version - with: - user: __token__ - password: ${{ secrets.test_pypi_password }} - repository-url: https://test.pypi.org/legacy/ + with: + user: __token__ + password: ${{ secrets.test_pypi_password }} + repository-url: https://test.pypi.org/legacy/ upload_to_pypi: needs: [build, make_sdist] runs-on: ubuntu-latest if: github.event_name == 'release' && github.event.action == 'published' steps: - - uses: actions/download-artifact@v4 - with: - merge-multiple: true - path: dist + - uses: actions/download-artifact@v4 + with: + merge-multiple: true + path: dist - - uses: pypa/gh-action-pypi-publish@v1.12.4 - with: - user: __token__ - password: ${{ secrets.pypi_password }} + - uses: pypa/gh-action-pypi-publish@v1.12.4 + with: + user: __token__ + password: ${{ secrets.pypi_password }} From 6b8c79c488dff179208bd093a20165560ff4847b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 22 May 2025 18:50:25 +0200 Subject: [PATCH 228/392] Fixed a segfault (thanks, Martin\!) --- src/db/db/dbPLC.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/db/db/dbPLC.cc b/src/db/db/dbPLC.cc index 8f7c650a5..07b7b8fa5 100644 --- a/src/db/db/dbPLC.cc +++ b/src/db/db/dbPLC.cc @@ -459,10 +459,11 @@ Polygon::init () auto i = v2e.find (v); tl_assert (i != v2e.end () && i->first == v && i->second != mp_e.back ()); - v2e.erase (i); mp_e.push_back (i->second); - v = i->second->other (v); + + v2e.erase (i); + i = v2e.find (v); while (i != v2e.end () && i->first == v) { if (i->second == mp_e.back ()) { From 4dd4524da9dc3decdd810fca45ea2162f3aac579 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 22 May 2025 19:47:39 +0200 Subject: [PATCH 229/392] Fixed typo --- src/db/db/gsiDeclDbLayoutToNetlist.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index c3a565c50..065677906 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -1033,7 +1033,7 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "'lmap' can also be left nil, in which case, a layer mapping will be provided based on the layer info attributes of " "the layers (see \\layer_info).\n" "\n" - "'cmap' specifies the cell mapping. Use \\create_cell_mapping or \\const_create_cell_mapping to " + "'cmap' specifies the cell mapping. Use \\cell_mapping_into or \\const_cell_mapping_into to " "define the target cells in the target layout and to derive a cell mapping.\n" "\n" "The method has three net annotation modes:\n" From 90c4f654b612714846f382d187acf041d4bb8f14 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 22 May 2025 19:57:06 +0200 Subject: [PATCH 230/392] API for adding log entries to LVS and L2N databases --- src/db/db/gsiDeclDbLayoutToNetlist.cc | 8 ++++++++ testdata/ruby/dbLayoutToNetlist.rb | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index c3a565c50..b30450327 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -1118,6 +1118,14 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "@brief Reads the extracted netlist from the file.\n" "This method employs the native format of KLayout.\n" ) + + gsi::method ("clear_log_entries", &db::LayoutToNetlist::clear_log_entries, + "@brief Clears the log entries.\n" + "This method has been introduced in version 0.30.2" + ) + + gsi::method ("add_log_entry", &db::LayoutToNetlist::log_entry, gsi::arg ("entry"), + "@brief Adds a log entry.\n" + "This method has been introduced in version 0.30.2" + ) + gsi::iterator ("each_log_entry|#each_error", &db::LayoutToNetlist::begin_log_entries, &db::LayoutToNetlist::end_log_entries, "@brief Iterates over all log entries collected during device and netlist extraction.\n" "This method has been introduced in version 0.28.13." diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb index 30408b611..4ecf8765f 100644 --- a/testdata/ruby/dbLayoutToNetlist.rb +++ b/testdata/ruby/dbLayoutToNetlist.rb @@ -1121,6 +1121,23 @@ END assert_equal(le[2].to_s, "warning") assert_equal(le[3].to_s, "error") + le = RBA::LogEntryData::new + le.severity = RBA::LogEntryData::Error + le.message = "A new entry" + le.geometry = RBA::DPolygon::new(RBA::DBox::new(0, 1, 2, 3)) + le.cell_name = "MYCELL" + le.category_name = "CAT" + le.category_description = "Cat Desc" + l2n.add_log_entry(le) + + le = l2n.each_log_entry.collect { |s| s.to_s } + assert_equal(le.size, 5) + assert_equal(le[4].to_s, "[Cat Desc] In cell MYCELL: A new entry, shape: (0,1;0,3;2,3;2,1)") + + l2n.clear_log_entries + le = l2n.each_log_entry.collect { |s| s.to_s } + assert_equal(le.size, 0) + end def test_22_Layers From 57c2add00a06c56f44a8ed2c874144ffa22b29ab Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 22 May 2025 20:54:21 +0200 Subject: [PATCH 231/392] API enhancement for NetlistCrossReference: obtaining information from one object only (net, circuit), does not need a pair anymore --- src/db/db/dbNetlistCrossReference.cc | 18 +++ src/db/db/dbNetlistCrossReference.h | 1 + src/db/db/gsiDeclDbNetlistCrossReference.cc | 133 ++++++++++++++++++++ testdata/ruby/dbNetlistCrossReference.rb | 84 +++++++++++++ 4 files changed, 236 insertions(+) diff --git a/src/db/db/dbNetlistCrossReference.cc b/src/db/db/dbNetlistCrossReference.cc index fb22eeb85..eb3f82a6d 100644 --- a/src/db/db/dbNetlistCrossReference.cc +++ b/src/db/db/dbNetlistCrossReference.cc @@ -111,6 +111,24 @@ NetlistCrossReference::other_net_for (const db::Net *net) const } } +const NetlistCrossReference::PerNetData * +NetlistCrossReference::per_net_data_for_net (const db::Net *net) const +{ + const db::Net *other_net = other_net_for (net); + + std::map, PerNetData>::iterator i = m_per_net_data.find (std::make_pair (net, other_net)); + if (i == m_per_net_data.end ()) { + i = m_per_net_data.find (std::make_pair (other_net, net)); + } + + if (i == m_per_net_data.end ()) { + static const NetlistCrossReference::PerNetData empty_net_data; + return &empty_net_data; + } else { + return &i->second; + } +} + const NetlistCrossReference::PerNetData * NetlistCrossReference::per_net_data_for (const std::pair &nets) const { diff --git a/src/db/db/dbNetlistCrossReference.h b/src/db/db/dbNetlistCrossReference.h index c06c43ba9..17138ca15 100644 --- a/src/db/db/dbNetlistCrossReference.h +++ b/src/db/db/dbNetlistCrossReference.h @@ -280,6 +280,7 @@ public: const db::SubCircuit *other_subcircuit_for (const db::SubCircuit *subcircuit) const; const db::Circuit *other_circuit_for (const db::Circuit *circuit) const; const db::Net *other_net_for (const db::Net *net) const; + const PerNetData *per_net_data_for_net (const db::Net *net) const; const PerNetData *per_net_data_for (const std::pair &nets) const; const db::Netlist *netlist_a () const diff --git a/src/db/db/gsiDeclDbNetlistCrossReference.cc b/src/db/db/gsiDeclDbNetlistCrossReference.cc index bc847f833..467b2c828 100644 --- a/src/db/db/gsiDeclDbNetlistCrossReference.cc +++ b/src/db/db/gsiDeclDbNetlistCrossReference.cc @@ -287,6 +287,19 @@ static pair_data_iterator each_net_pair1 (db::NetlistCrossReference *xref, const db::Circuit *circuit) +{ + tl_assert (xref->netlist_a () != 0 && xref->netlist_b () != 0); + typedef pair_data_iterator iter_type; + + const db::NetlistCrossReference::PerCircuitData *data = xref->per_circuit_data_for (std::make_pair (circuit, circuit)); + if (! data) { + return iter_type (); + } else { + return iter_type (xref, data->nets.begin (), data->nets.end ()); + } +} + static pair_data_iterator each_device_pair (db::NetlistCrossReference *xref, const CircuitPairData &circuit_pair) { tl_assert (xref->netlist_a () != 0 && xref->netlist_b () != 0); @@ -300,6 +313,19 @@ static pair_data_iterator each_device_pair1 (db::NetlistCrossReference *xref, const db::Circuit *circuit) +{ + tl_assert (xref->netlist_a () != 0 && xref->netlist_b () != 0); + typedef pair_data_iterator iter_type; + + const db::NetlistCrossReference::PerCircuitData *data = xref->per_circuit_data_for (std::make_pair (circuit, circuit)); + if (! data) { + return iter_type (); + } else { + return iter_type (xref, data->devices.begin (), data->devices.end ()); + } +} + static pair_data_iterator each_pin_pair (db::NetlistCrossReference *xref, const CircuitPairData &circuit_pair) { tl_assert (xref->netlist_a () != 0 && xref->netlist_b () != 0); @@ -313,6 +339,19 @@ static pair_data_iterator each_pin_pair1 (db::NetlistCrossReference *xref, const db::Circuit *circuit) +{ + tl_assert (xref->netlist_a () != 0 && xref->netlist_b () != 0); + typedef pair_data_iterator iter_type; + + const db::NetlistCrossReference::PerCircuitData *data = xref->per_circuit_data_for (std::make_pair (circuit, circuit)); + if (! data) { + return iter_type (); + } else { + return iter_type (xref, data->pins.begin (), data->pins.end ()); + } +} + static pair_data_iterator each_subcircuit_pair (db::NetlistCrossReference *xref, const CircuitPairData &circuit_pair) { tl_assert (xref->netlist_a () != 0 && xref->netlist_b () != 0); @@ -326,6 +365,19 @@ static pair_data_iterator each_subcircuit_pair1 (db::NetlistCrossReference *xref, const db::Circuit *circuit) +{ + tl_assert (xref->netlist_a () != 0 && xref->netlist_b () != 0); + typedef pair_data_iterator iter_type; + + const db::NetlistCrossReference::PerCircuitData *data = xref->per_circuit_data_for (std::make_pair (circuit, circuit)); + if (! data) { + return iter_type (); + } else { + return iter_type (xref, data->subcircuits.begin (), data->subcircuits.end ()); + } +} + static pair_data_iterator, db::NetlistCrossReference::PerNetData::terminal_pairs_const_iterator> each_net_terminal_pair (db::NetlistCrossReference *xref, const db::NetlistCrossReference::NetPairData &net_pair) { tl_assert (xref->netlist_a () != 0 && xref->netlist_b () != 0); @@ -339,6 +391,19 @@ static pair_data_iterator, db::NetlistCrossReference::PerNetData::terminal_pairs_const_iterator> each_net_terminal_pair1 (db::NetlistCrossReference *xref, const db::Net *net) +{ + tl_assert (xref->netlist_a () != 0 && xref->netlist_b () != 0); + typedef pair_data_iterator, db::NetlistCrossReference::PerNetData::terminal_pairs_const_iterator> iter_type; + + const db::NetlistCrossReference::PerNetData *data = xref->per_net_data_for_net (net); + if (! data) { + return iter_type (); + } else { + return iter_type (xref, data->terminals.begin (), data->terminals.end ()); + } +} + static pair_data_iterator, db::NetlistCrossReference::PerNetData::pin_pairs_const_iterator> each_net_pin_pair (db::NetlistCrossReference *xref, const db::NetlistCrossReference::NetPairData &net_pair) { tl_assert (xref->netlist_a () != 0 && xref->netlist_b () != 0); @@ -352,6 +417,19 @@ static pair_data_iterator, db::NetlistCrossReference::PerNetData::pin_pairs_const_iterator> each_net_pin_pair1 (db::NetlistCrossReference *xref, const db::Net *net) +{ + tl_assert (xref->netlist_a () != 0 && xref->netlist_b () != 0); + typedef pair_data_iterator, db::NetlistCrossReference::PerNetData::pin_pairs_const_iterator> iter_type; + + const db::NetlistCrossReference::PerNetData *data = xref->per_net_data_for_net (net); + if (! data) { + return iter_type (); + } else { + return iter_type (xref, data->pins.begin (), data->pins.end ()); + } +} + static pair_data_iterator, db::NetlistCrossReference::PerNetData::subcircuit_pin_pairs_const_iterator> each_net_subcircuit_pin_pair (db::NetlistCrossReference *xref, const db::NetlistCrossReference::NetPairData &net_pair) { tl_assert (xref->netlist_a () != 0 && xref->netlist_b () != 0); @@ -365,6 +443,19 @@ static pair_data_iterator, db::NetlistCrossReference::PerNetData::subcircuit_pin_pairs_const_iterator> each_net_subcircuit_pin_pair1 (db::NetlistCrossReference *xref, const db::Net *net) +{ + tl_assert (xref->netlist_a () != 0 && xref->netlist_b () != 0); + typedef pair_data_iterator, db::NetlistCrossReference::PerNetData::subcircuit_pin_pairs_const_iterator> iter_type; + + const db::NetlistCrossReference::PerNetData *data = xref->per_net_data_for_net (net); + if (! data) { + return iter_type (); + } else { + return iter_type (xref, data->subcircuit_pins.begin (), data->subcircuit_pins.end ()); + } +} + Class decl_dbNetlistCrossReference (decl_dbNetlistCompareLogger, "db", "NetlistCrossReference", gsi::iterator_ext ("each_circuit_pair", &each_circuit_pair, "@brief Delivers the circuit pairs and their status.\n" @@ -374,30 +465,72 @@ Class decl_dbNetlistCrossReference (decl_dbNetlistCom "@brief Delivers the net pairs and their status for the given circuit pair.\n" "See the class description for details." ) + + gsi::iterator_ext ("each_net_pair", &each_net_pair1, gsi::arg ("circuit"), + "@brief Delivers the net pairs and their status for the given circuit.\n" + "This convenience method looks up the circuit pair from the given circuit. This circuit can be " + "a schematic or layout circuit.\n" + "This method has been added in version 0.30.2.\n" + ) + gsi::iterator_ext ("each_device_pair", &each_device_pair, gsi::arg ("circuit_pair"), "@brief Delivers the device pairs and their status for the given circuit pair.\n" "See the class description for details." ) + + gsi::iterator_ext ("each_device_pair", &each_device_pair1, gsi::arg ("circuit"), + "@brief Delivers the device pairs and their status for the given circuit pair.\n" + "This convenience method looks up the circuit pair from the given circuit. This circuit can be " + "a schematic or layout circuit.\n" + "This method has been added in version 0.30.2.\n" + ) + gsi::iterator_ext ("each_pin_pair", &each_pin_pair, gsi::arg ("circuit_pair"), "@brief Delivers the pin pairs and their status for the given circuit pair.\n" "See the class description for details." ) + + gsi::iterator_ext ("each_pin_pair", &each_pin_pair1, gsi::arg ("circuit"), + "@brief Delivers the pin pairs and their status for the given circuit pair.\n" + "This convenience method looks up the circuit pair from the given circuit. This circuit can be " + "a schematic or layout circuit.\n" + "This method has been added in version 0.30.2.\n" + ) + gsi::iterator_ext ("each_subcircuit_pair", &each_subcircuit_pair, gsi::arg ("circuit_pair"), "@brief Delivers the subcircuit pairs and their status for the given circuit pair.\n" "See the class description for details." ) + + gsi::iterator_ext ("each_subcircuit_pair", &each_subcircuit_pair1, gsi::arg ("circuit"), + "@brief Delivers the subcircuit pairs and their status for the given circuit pair.\n" + "This convenience method looks up the circuit pair from the given circuit. This circuit can be " + "a schematic or layout circuit.\n" + "This method has been added in version 0.30.2.\n" + ) + gsi::iterator_ext ("each_net_terminal_pair", &each_net_terminal_pair, gsi::arg ("net_pair"), "@brief Delivers the device terminal pairs for the given net pair.\n" "For the net pair, lists the device terminal pairs identified on this net." ) + + gsi::iterator_ext ("each_net_terminal_pair", &each_net_terminal_pair1, gsi::arg ("net"), + "@brief Delivers the device terminal pairs for the given net pair.\n" + "This convenience method looks up the net pair from the given net. This net can be " + "a schematic or layout net.\n" + "This method has been added in version 0.30.2.\n" + ) + gsi::iterator_ext ("each_net_pin_pair", &each_net_pin_pair, gsi::arg ("net_pair"), "@brief Delivers the pin pairs for the given net pair.\n" "For the net pair, lists the pin pairs identified on this net." ) + + gsi::iterator_ext ("each_net_pin_pair", &each_net_pin_pair1, gsi::arg ("net"), + "@brief Delivers the pin pairs for the given net pair.\n" + "This convenience method looks up the net pair from the given net. This net can be " + "a schematic or layout net.\n" + "This method has been added in version 0.30.2.\n" + ) + gsi::iterator_ext ("each_net_subcircuit_pin_pair", &each_net_subcircuit_pin_pair, gsi::arg ("net_pair"), "@brief Delivers the subcircuit pin pairs for the given net pair.\n" "For the net pair, lists the subcircuit pin pairs identified on this net." ) + + gsi::iterator_ext ("each_net_subcircuit_pin_pair", &each_net_subcircuit_pin_pair1, gsi::arg ("net"), + "@brief Delivers the subcircuit pin pairs for the given net pair.\n" + "This convenience method looks up the net pair from the given net. This net can be " + "a schematic or layout net.\n" + "This method has been added in version 0.30.2.\n" + ) + gsi::method ("other_net_for", &db::NetlistCrossReference::other_net_for, gsi::arg ("net"), "@brief Gets the matching other net for a given primary net.\n" "The return value will be nil if no match is found. " diff --git a/testdata/ruby/dbNetlistCrossReference.rb b/testdata/ruby/dbNetlistCrossReference.rb index 7da53e295..22de8dd35 100644 --- a/testdata/ruby/dbNetlistCrossReference.rb +++ b/testdata/ruby/dbNetlistCrossReference.rb @@ -81,12 +81,36 @@ class DBNetlistCrossReference_TestClass < TestBase end assert_equal(info.join(","), "/1:Match,BULK/6:Match,IN/2:Match,OUT/3:Match,VDD/5:Match,VSS/4:Match") + info = [] + xref.each_pin_pair(cp_inv2.first) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.name : "(nil)" }.join("/") + ":" + p.status.to_s + end + assert_equal(info.join(","), "/1:Match,BULK/6:Match,IN/2:Match,OUT/3:Match,VDD/5:Match,VSS/4:Match") + + info = [] + xref.each_pin_pair(cp_inv2.second) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.name : "(nil)" }.join("/") + ":" + p.status.to_s + end + assert_equal(info.join(","), "/1:Match,BULK/6:Match,IN/2:Match,OUT/3:Match,VDD/5:Match,VSS/4:Match") + info = [] xref.each_net_pair(cp_inv2) do |p| info << [ p.first, p.second ].collect { |s| s ? s.name : "(nil)" }.join("/") + ":" + p.status.to_s end assert_equal(info.join(","), "/1:Match,BULK/6:Match,IN/2:Match,OUT/3:Match,VDD/5:Match,VSS/4:Match") + info = [] + xref.each_net_pair(cp_inv2.first) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.name : "(nil)" }.join("/") + ":" + p.status.to_s + end + assert_equal(info.join(","), "/1:Match,BULK/6:Match,IN/2:Match,OUT/3:Match,VDD/5:Match,VSS/4:Match") + + info = [] + xref.each_net_pair(cp_inv2.second) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.name : "(nil)" }.join("/") + ":" + p.status.to_s + end + assert_equal(info.join(","), "/1:Match,BULK/6:Match,IN/2:Match,OUT/3:Match,VDD/5:Match,VSS/4:Match") + netp_bulk = nil xref.each_net_pair(cp_inv2) do |p| if p.first.name == "BULK" @@ -100,30 +124,90 @@ class DBNetlistCrossReference_TestClass < TestBase end assert_equal(info.join(","), "B/B") + info = [] + xref.each_net_terminal_pair(netp_bulk.first) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.terminal_def.name : "(nil)" }.join("/") + end + assert_equal(info.join(","), "B/B") + + info = [] + xref.each_net_terminal_pair(netp_bulk.second) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.terminal_def.name : "(nil)" }.join("/") + end + assert_equal(info.join(","), "B/B") + info = [] xref.each_net_pin_pair(netp_bulk) do |p| info << [ p.first, p.second ].collect { |s| s ? s.pin.name : "(nil)" }.join("/") end assert_equal(info.join(","), "BULK/6") + info = [] + xref.each_net_pin_pair(netp_bulk.first) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.pin.name : "(nil)" }.join("/") + end + assert_equal(info.join(","), "BULK/6") + + info = [] + xref.each_net_pin_pair(netp_bulk.second) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.pin.name : "(nil)" }.join("/") + end + assert_equal(info.join(","), "BULK/6") + info = [] xref.each_net_subcircuit_pin_pair(netp_bulk) do |p| info << [ p.first, p.second ].collect { |s| s ? s.pin.name : "(nil)" }.join("/") end assert_equal(info.join(","), "") + info = [] + xref.each_net_subcircuit_pin_pair(netp_bulk.first) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.pin.name : "(nil)" }.join("/") + end + assert_equal(info.join(","), "") + + info = [] + xref.each_net_subcircuit_pin_pair(netp_bulk.second) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.pin.name : "(nil)" }.join("/") + end + assert_equal(info.join(","), "") + info = [] xref.each_device_pair(cp_inv2) do |p| info << [ p.first, p.second ].collect { |s| s ? s.name : "(nil)" }.join("/") + ":" + p.status.to_s end assert_equal(info.join(","), "/$1:Match,/$3:Match") + info = [] + xref.each_device_pair(cp_inv2.first) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.name : "(nil)" }.join("/") + ":" + p.status.to_s + end + assert_equal(info.join(","), "/$1:Match,/$3:Match") + + info = [] + xref.each_device_pair(cp_inv2.second) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.name : "(nil)" }.join("/") + ":" + p.status.to_s + end + assert_equal(info.join(","), "/$1:Match,/$3:Match") + info = [] xref.each_subcircuit_pair(cp_inv2) do |p| info << [ p.first, p.second ].collect { |s| s ? s.name : "(nil)" }.join("/") + ":" + p.status.to_s end assert_equal(info.join(","), "") + info = [] + xref.each_subcircuit_pair(cp_inv2.first) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.name : "(nil)" }.join("/") + ":" + p.status.to_s + end + assert_equal(info.join(","), "") + + info = [] + xref.each_subcircuit_pair(cp_inv2.second) do |p| + info << [ p.first, p.second ].collect { |s| s ? s.name : "(nil)" }.join("/") + ":" + p.status.to_s + end + assert_equal(info.join(","), "") + cp_inv2pair = nil xref.each_circuit_pair do |cp| if cp.first && cp.first.name == "INV2PAIR" From e96f20a1064840146f06c45dfe4abe3782607e40 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 22 May 2025 22:25:49 +0200 Subject: [PATCH 232/392] Introducing case sensitivity in circuit and netlist queries, makeing 'same_nets' more case sensitivity-aware in case of mixed sensitivity --- src/db/db/gsiDeclDbNetlist.cc | 78 ++++++++++++++-------- src/lvs/lvs/built-in-macros/_lvs_netter.rb | 8 +-- testdata/ruby/dbNetlist.rb | 32 +++++++++ 3 files changed, 88 insertions(+), 30 deletions(-) diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 05e2c85bc..792d65d15 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -1488,7 +1488,7 @@ nets_non_const (const std::vector &nc) } static std::vector -nets_by_name_const (const db::Circuit *circuit, const std::string &name_pattern) +nets_by_name_const (const db::Circuit *circuit, const std::string &name_pattern, const tl::Variant &cs) { std::vector res; if (! circuit) { @@ -1496,7 +1496,9 @@ nets_by_name_const (const db::Circuit *circuit, const std::string &name_pattern) } tl::GlobPattern glob (name_pattern); - if (circuit->netlist ()) { + if (! cs.is_nil ()) { + glob.set_case_sensitive (cs.to_bool ()); + } else if (circuit->netlist ()) { glob.set_case_sensitive (circuit->netlist ()->is_case_sensitive ()); } for (db::Circuit::const_net_iterator n = circuit->begin_nets (); n != circuit->end_nets (); ++n) { @@ -1510,13 +1512,13 @@ nets_by_name_const (const db::Circuit *circuit, const std::string &name_pattern) } static std::vector -nets_by_name (db::Circuit *circuit, const std::string &name_pattern) +nets_by_name (db::Circuit *circuit, const std::string &name_pattern, const tl::Variant &cs) { - return nets_non_const (nets_by_name_const (circuit, name_pattern)); + return nets_non_const (nets_by_name_const (circuit, name_pattern, cs)); } static std::vector -nets_by_name_const_from_netlist (const db::Netlist *netlist, const std::string &name_pattern) +nets_by_name_const_from_netlist (const db::Netlist *netlist, const std::string &name_pattern, const tl::Variant &cs) { std::vector res; if (! netlist) { @@ -1524,7 +1526,7 @@ nets_by_name_const_from_netlist (const db::Netlist *netlist, const std::string & } tl::GlobPattern glob (name_pattern); - glob.set_case_sensitive (netlist->is_case_sensitive ()); + glob.set_case_sensitive (cs.is_nil () ? netlist->is_case_sensitive () : cs.to_bool ()); for (auto c = netlist->begin_circuits (); c != netlist->end_circuits (); ++c) { bool is_top = (c->begin_parents () == c->end_parents ()); for (auto n = c->begin_nets (); n != c->end_nets (); ++n) { @@ -1540,9 +1542,9 @@ nets_by_name_const_from_netlist (const db::Netlist *netlist, const std::string & } static std::vector -nets_by_name_from_netlist (db::Netlist *netlist, const std::string &name_pattern) +nets_by_name_from_netlist (db::Netlist *netlist, const std::string &name_pattern, const tl::Variant &cs) { - return nets_non_const (nets_by_name_const_from_netlist (netlist, name_pattern)); + return nets_non_const (nets_by_name_const_from_netlist (netlist, name_pattern, cs)); } Class decl_dbCircuit (decl_dbNetlistObject, "db", "Circuit", @@ -1658,17 +1660,25 @@ Class decl_dbCircuit (decl_dbNetlistObject, "db", "Circuit", "\n\n" "This constness variant has been introduced in version 0.26.8" ) + - gsi::method_ext ("nets_by_name", &nets_by_name, gsi::arg ("name_pattern"), + gsi::method_ext ("nets_by_name", &nets_by_name, gsi::arg ("name_pattern"), gsi::arg ("case_sensitive", tl::Variant (), "default"), "@brief Gets the net objects for a given name filter.\n" "The name filter is a glob pattern. This method will return all \\Net objects matching the glob pattern.\n" + "The 'case_sensitive' argument will control whether the name is looked up in a case sensitive way or not. Note that " + "with case insensitive search on a netlist that is case sensitive, the same name may render more than one hit. By " + "default, case sensitivity is taken from the netlist.\n" "\n" "This method has been introduced in version 0.27.3.\n" + "The 'case_sensitive' argument has been added in version 0.30.2." ) + - gsi::method_ext ("nets_by_name", &nets_by_name_const, gsi::arg ("name_pattern"), + gsi::method_ext ("nets_by_name", &nets_by_name_const, gsi::arg ("name_pattern"), gsi::arg ("case_sensitive", tl::Variant (), "default"), "@brief Gets the net objects for a given name filter (const version).\n" "The name filter is a glob pattern. This method will return all \\Net objects matching the glob pattern.\n" - "\n\n" - "This constness variant has been introduced in version 0.27.3" + "The 'case_sensitive' argument will control whether the name is looked up in a case sensitive way or not. Note that " + "with case insensitive search on a netlist that is case sensitive, the same name may render more than one hit. By " + "default, case sensitivity is taken from the netlist.\n" + "\n" + "This constness variant has been introduced in version 0.27.3.\n" + "The 'case_sensitive' argument has been added in version 0.30.2." ) + gsi::method ("pin_by_id", (db::Pin *(db::Circuit::*) (size_t)) &db::Circuit::pin_by_id, gsi::arg ("id"), "@brief Gets the \\Pin object corresponding to a specific ID\n" @@ -1964,7 +1974,7 @@ static void blank_circuit_by_name (db::Netlist *nl, const std::string &name_patt } static std::vector -circuits_by_name (db::Netlist *netlist, const std::string &name_pattern) +circuits_by_name (db::Netlist *netlist, const std::string &name_pattern, const tl::Variant &cs) { std::vector res; if (! netlist) { @@ -1972,7 +1982,7 @@ circuits_by_name (db::Netlist *netlist, const std::string &name_pattern) } tl::GlobPattern glob (name_pattern); - glob.set_case_sensitive (netlist->is_case_sensitive ()); + glob.set_case_sensitive (cs.is_nil () ? netlist->is_case_sensitive () : cs.to_bool ()); for (db::Netlist::circuit_iterator c = netlist->begin_circuits (); c != netlist->end_circuits (); ++c) { db::Circuit *circuit = c.operator-> (); @@ -1985,7 +1995,7 @@ circuits_by_name (db::Netlist *netlist, const std::string &name_pattern) } static std::vector -circuits_by_name_const (const db::Netlist *netlist, const std::string &name_pattern) +circuits_by_name_const (const db::Netlist *netlist, const std::string &name_pattern, const tl::Variant &cs) { std::vector res; if (! netlist) { @@ -1993,7 +2003,7 @@ circuits_by_name_const (const db::Netlist *netlist, const std::string &name_patt } tl::GlobPattern glob (name_pattern); - glob.set_case_sensitive (netlist->is_case_sensitive ()); + glob.set_case_sensitive (cs.is_nil () ? netlist->is_case_sensitive () : cs.to_bool ()); for (db::Netlist::const_circuit_iterator c = netlist->begin_circuits (); c != netlist->end_circuits (); ++c) { const db::Circuit *circuit = c.operator-> (); @@ -2082,23 +2092,45 @@ Class decl_dbNetlist ("db", "Netlist", "\n\n" "This constness variant has been introduced in version 0.26.8." ) + - gsi::method_ext ("circuits_by_name", &circuits_by_name, gsi::arg ("name_pattern"), + gsi::method_ext ("circuits_by_name", &circuits_by_name, gsi::arg ("name_pattern"), gsi::arg ("case_sensitive", tl::Variant (), "default"), "@brief Gets the circuit objects for a given name filter.\n" "The name filter is a glob pattern. This method will return all \\Circuit objects matching the glob pattern.\n" + "The 'case_sensitive' argument will control whether the name is looked up in a case sensitive way or not. Note that " + "with case insensitive search on a netlist that is case sensitive, the same name may render more than one hit. By " + "default, case sensitivity is taken from the netlist.\n" "\n" "This method has been introduced in version 0.26.4.\n" + "The 'case_sensitive' argument has been added in version 0.30.2." ) + - gsi::method_ext ("circuits_by_name", &circuits_by_name_const, gsi::arg ("name_pattern"), + gsi::method_ext ("circuits_by_name", &circuits_by_name_const, gsi::arg ("name_pattern"), gsi::arg ("case_sensitive", tl::Variant (), "default"), "@brief Gets the circuit objects for a given name filter (const version).\n" "The name filter is a glob pattern. This method will return all \\Circuit objects matching the glob pattern.\n" - "\n\n" + "The 'case_sensitive' argument will control whether the name is looked up in a case sensitive way or not. Note that " + "with case insensitive search on a netlist that is case sensitive, the same name may render more than one hit. By " + "default, case sensitivity is taken from the netlist.\n" + "\n" "This constness variant has been introduced in version 0.26.8." + "The 'case_sensitive' argument has been added in version 0.30.2." ) + - gsi::method_ext ("nets_by_name", &nets_by_name_from_netlist, gsi::arg ("name_pattern"), + gsi::method_ext ("nets_by_name", &nets_by_name_from_netlist, gsi::arg ("name_pattern"), gsi::arg ("case_sensitive", tl::Variant (), "default"), "@brief Gets the net objects for a given name filter.\n" "The name filter is a glob pattern. This method will return all \\Net objects matching the glob pattern.\n" + "The 'case_sensitive' argument will control whether the name is looked up in a case sensitive way or not. Note that " + "with case insensitive search on a netlist that is case sensitive, the same name may render more than one hit. By " + "default, case sensitivity is taken from the netlist.\n" "\n" "This method has been introduced in version 0.28.4.\n" + "The 'case_sensitive' argument has been added in version 0.30.2." + ) + + gsi::method_ext ("nets_by_name", &nets_by_name_const_from_netlist, gsi::arg ("name_pattern"), gsi::arg ("case_sensitive", tl::Variant (), "default"), + "@brief Gets the net objects for a given name filter (const version).\n" + "The name filter is a glob pattern. This method will return all \\Net objects matching the glob pattern.\n" + "The 'case_sensitive' argument will control whether the name is looked up in a case sensitive way or not. Note that " + "with case insensitive search on a netlist that is case sensitive, the same name may render more than one hit. By " + "default, case sensitivity is taken from the netlist.\n" + "\n" + "This constness variant has been introduced in version 0.28.4." + "The 'case_sensitive' argument has been added in version 0.30.2." ) + gsi::method ("top_circuit", static_cast (&db::Netlist::top_circuit), "@brief Gets the top circuit.\n" @@ -2126,12 +2158,6 @@ Class decl_dbNetlist ("db", "Netlist", "\n" "This convenience method has been added in version 0.29.5." ) + - gsi::method_ext ("nets_by_name", &nets_by_name_const_from_netlist, gsi::arg ("name_pattern"), - "@brief Gets the net objects for a given name filter (const version).\n" - "The name filter is a glob pattern. This method will return all \\Net objects matching the glob pattern.\n" - "\n\n" - "This constness variant has been introduced in version 0.28.4." - ) + 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. " diff --git a/src/lvs/lvs/built-in-macros/_lvs_netter.rb b/src/lvs/lvs/built-in-macros/_lvs_netter.rb index 4b3a44f7c..15e0dbbad 100644 --- a/src/lvs/lvs/built-in-macros/_lvs_netter.rb +++ b/src/lvs/lvs/built-in-macros/_lvs_netter.rb @@ -652,8 +652,8 @@ CODE if ca.is_a?(String) && (!cb || cb == "*") n2c = {} - nl_a.circuits_by_name(ca).each { |c| name = cs ? c.name.upcase : c.name; n2c[name] ||= [ nil, nil ]; n2c[name][0] = c } - nl_b.circuits_by_name(ca).each { |c| name = cs ? c.name.upcase : c.name; n2c[name] ||= [ nil, nil ]; n2c[name][1] = c } + nl_a.circuits_by_name(ca, cs).each { |c| name = cs ? c.name.upcase : c.name; n2c[name] ||= [ nil, nil ]; n2c[name][0] = c } + nl_b.circuits_by_name(ca, cs).each { |c| name = cs ? c.name.upcase : c.name; n2c[name] ||= [ nil, nil ]; n2c[name][1] = c } circuits = [] n2c.keys.sort.each do |n| @@ -679,8 +679,8 @@ CODE if a.is_a?(String) && (!b || b == "*") n2n = {} - circuit_a.nets_by_name(a).each { |n| name = cs ? n.name.upcase : n.name; n2n[name] ||= [ nil, nil ]; n2n[name][0] = n } - circuit_b.nets_by_name(a).each { |n| name = cs ? n.name.upcase : n.name; n2n[name] ||= [ nil, nil ]; n2n[name][1] = n } + circuit_a.nets_by_name(a, cs).each { |n| name = cs ? n.name.upcase : n.name; n2n[name] ||= [ nil, nil ]; n2n[name][0] = n } + circuit_b.nets_by_name(a, cs).each { |n| name = cs ? n.name.upcase : n.name; n2n[name] ||= [ nil, nil ]; n2n[name][1] = n } nets = [] n2n.keys.sort.each do |n| diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index aaf5e9cad..fa0b859e8 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -111,9 +111,17 @@ class DBNetlist_TestClass < TestBase assert_equal(nl.circuits_by_name("X*").collect { |x| x.name }, [ "XYZ" ]) assert_equal(nl.circuits_by_name("x*").collect { |x| x.name }, []) + assert_equal(nl.circuits_by_name("X*", true).collect { |x| x.name }, [ "XYZ" ]) + assert_equal(nl.circuits_by_name("x*", true).collect { |x| x.name }, []) + assert_equal(nl.circuits_by_name("X*", false).collect { |x| x.name }, [ "XYZ" ]) + assert_equal(nl.circuits_by_name("x*", false).collect { |x| x.name }, [ "XYZ" ]) nl.case_sensitive = false assert_equal(nl.circuits_by_name("X*").collect { |x| x.name }, [ "XYZ" ]) assert_equal(nl.circuits_by_name("x*").collect { |x| x.name }, [ "XYZ" ]) + assert_equal(nl.circuits_by_name("X*", true).collect { |x| x.name }, [ "XYZ" ]) + assert_equal(nl.circuits_by_name("x*", true).collect { |x| x.name }, []) + assert_equal(nl.circuits_by_name("X*", false).collect { |x| x.name }, [ "XYZ" ]) + assert_equal(nl.circuits_by_name("x*", false).collect { |x| x.name }, [ "XYZ" ]) nl.case_sensitive = true assert_equal(nl.circuits_by_name("???").collect { |x| x.name }, [ "XYZ", "UVW" ]) assert_equal(nl.circuits_by_name("*").collect { |x| x.name }, [ "XYZ", "UVW" ]) @@ -719,7 +727,31 @@ class DBNetlist_TestClass < TestBase assert_equal(c.net_by_name("net1").inspect, "nil") nl.case_sensitive = false assert_equal(c.net_by_name("net1").name, "NET1") + assert_equal(c.nets_by_name("NET*").collect(&:name), ["NET1"]) + assert_equal(c.nets_by_name("net*").collect(&:name), ["NET1"]) + assert_equal(c.nets_by_name("NET*", true).collect(&:name), ["NET1"]) + assert_equal(c.nets_by_name("net*", true).collect(&:name), []) + assert_equal(c.nets_by_name("NET*", false).collect(&:name), ["NET1"]) + assert_equal(c.nets_by_name("net*", false).collect(&:name), ["NET1"]) + assert_equal(nl.nets_by_name("NET*").collect(&:name), ["NET1"]) + assert_equal(nl.nets_by_name("net*").collect(&:name), ["NET1"]) + assert_equal(nl.nets_by_name("NET*", true).collect(&:name), ["NET1"]) + assert_equal(nl.nets_by_name("net*", true).collect(&:name), []) + assert_equal(nl.nets_by_name("NET*", false).collect(&:name), ["NET1"]) + assert_equal(nl.nets_by_name("net*", false).collect(&:name), ["NET1"]) nl.case_sensitive = true + assert_equal(c.nets_by_name("NET*").collect(&:name), ["NET1"]) + assert_equal(c.nets_by_name("net*").collect(&:name), []) + assert_equal(c.nets_by_name("NET*", true).collect(&:name), ["NET1"]) + assert_equal(c.nets_by_name("net*", true).collect(&:name), []) + assert_equal(c.nets_by_name("NET*", false).collect(&:name), ["NET1"]) + assert_equal(c.nets_by_name("net*", false).collect(&:name), ["NET1"]) + assert_equal(nl.nets_by_name("NET*").collect(&:name), ["NET1"]) + assert_equal(nl.nets_by_name("net*").collect(&:name), []) + assert_equal(nl.nets_by_name("NET*", true).collect(&:name), ["NET1"]) + assert_equal(nl.nets_by_name("net*", true).collect(&:name), []) + assert_equal(nl.nets_by_name("NET*", false).collect(&:name), ["NET1"]) + assert_equal(nl.nets_by_name("net*", false).collect(&:name), ["NET1"]) net2 = c.create_net net2.name = "NET2" From 6d28c78eeb59cd4d63d8e344c81aca8a347862bd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 22 May 2025 22:48:48 +0200 Subject: [PATCH 233/392] Fixed issue #2060 - the precision of the check was too low. --- src/db/db/dbEdgesUtils.cc | 10 +++++----- src/db/db/dbEdgesUtils.h | 2 +- src/db/unit_tests/dbEdgesTests.cc | 13 +++++++++++++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/db/db/dbEdgesUtils.cc b/src/db/db/dbEdgesUtils.cc index 15dcb5a1b..382c378bd 100644 --- a/src/db/db/dbEdgesUtils.cc +++ b/src/db/db/dbEdgesUtils.cc @@ -250,8 +250,8 @@ EdgeAngleChecker::EdgeAngleChecker (double angle_start, bool include_angle_start include_angle_start = true; } - 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_t_start = db::ICplxTrans (1.0, angle_start, false, db::Vector ()); + m_t_end = db::ICplxTrans (1.0, angle_end, false, db::Vector ()); m_include_start = include_angle_start; m_include_end = include_angle_end; @@ -266,10 +266,10 @@ EdgeAngleChecker::EdgeAngleChecker (double angle_start, bool include_angle_start bool EdgeAngleChecker::check (const db::Vector &a, const db::Vector &b) const { - db::DVector vout (b); + db::Vector vout (b); - db::DVector v1 = m_t_start * a; - db::DVector v2 = m_t_end * a; + db::Vector v1 = m_t_start * a; + db::Vector v2 = m_t_end * a; int vps1 = db::vprod_sign (v1, vout); int vps2 = db::vprod_sign (v2, vout); diff --git a/src/db/db/dbEdgesUtils.h b/src/db/db/dbEdgesUtils.h index d0082806d..769687b01 100644 --- a/src/db/db/dbEdgesUtils.h +++ b/src/db/db/dbEdgesUtils.h @@ -155,7 +155,7 @@ public: } private: - db::CplxTrans m_t_start, m_t_end; + db::ICplxTrans m_t_start, m_t_end; bool m_include_start, m_include_end; bool m_big_angle, m_all; bool m_inverse, m_absolute; diff --git a/src/db/unit_tests/dbEdgesTests.cc b/src/db/unit_tests/dbEdgesTests.cc index 0db2b9676..662aceb7b 100644 --- a/src/db/unit_tests/dbEdgesTests.cc +++ b/src/db/unit_tests/dbEdgesTests.cc @@ -261,6 +261,19 @@ TEST(4) db::EdgeOrientationFilter f1 (89.0, true, 90.0, false, false, false); EXPECT_EQ (r.filtered (f1).to_string (), ""); } + + // issue-2060 + { + db::EdgeOrientationFilter f1 (90.0, true, false); + + db::Edges rr; + rr.insert (db::Box (db::Point (0, 0), db::Point (1000, 4000000))); + EXPECT_EQ (db::compare (rr.filtered (f1), "(1000,0;0,0);(0,4000000;1000,4000000)"), true); + + rr.clear (); + rr.insert (db::Box (db::Point (0, 0), db::Point (1000, 400000))); + EXPECT_EQ (db::compare (rr.filtered (f1), "(1000,0;0,0);(0,400000;1000,400000)"), true); + } } TEST(5) From 3e69da8911db13cb05763a4161fa6b3e02f724df Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 10:41:25 +0900 Subject: [PATCH 234/392] reverting build.yml --- .github/workflows/build.yml | 52 +++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a49910d91..d9f6a3b9b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,10 +34,6 @@ jobs: - os: "ubuntu-latest" cibuild: "*musllinux*" cibw_arch: "musllinux" - - os: "ubuntu-24.04-arm" # aarch64 manylinux on ARM runner - cibuild: "*manylinux*" - cibw_arch: "manylinux" - steps: - name: Free Disk Space (Ubuntu) if: matrix.os == 'ubuntu-latest' @@ -94,43 +90,43 @@ jobs: name: Make SDist runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Build SDist - run: pipx run build --sdist + - name: Build SDist + run: pipx run build --sdist - - uses: actions/upload-artifact@v4 - with: - name: artifact-sdist - path: dist/*.tar.gz + - uses: actions/upload-artifact@v4 + with: + name: artifact-sdist + path: dist/*.tar.gz upload_to_test_pypy: needs: [build, make_sdist] runs-on: ubuntu-latest steps: - - uses: actions/download-artifact@v4 - with: - merge-multiple: true - path: dist + - uses: actions/download-artifact@v4 + with: + merge-multiple: true + path: dist - - uses: pypa/gh-action-pypi-publish@v1.12.4 + - uses: pypa/gh-action-pypi-publish@v1.12.4 continue-on-error: true # might fail if we don't bump the version - with: - user: __token__ - password: ${{ secrets.test_pypi_password }} - repository-url: https://test.pypi.org/legacy/ + with: + user: __token__ + password: ${{ secrets.test_pypi_password }} + repository-url: https://test.pypi.org/legacy/ upload_to_pypi: needs: [build, make_sdist] runs-on: ubuntu-latest if: github.event_name == 'release' && github.event.action == 'published' steps: - - uses: actions/download-artifact@v4 - with: - merge-multiple: true - path: dist + - uses: actions/download-artifact@v4 + with: + merge-multiple: true + path: dist - - uses: pypa/gh-action-pypi-publish@v1.12.4 - with: - user: __token__ - password: ${{ secrets.pypi_password }} + - uses: pypa/gh-action-pypi-publish@v1.12.4 + with: + user: __token__ + password: ${{ secrets.pypi_password }} From fc00191f5440b3f1bba56815b5322e4e6b90afb6 Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 10:44:57 +0900 Subject: [PATCH 235/392] adding arm config back to test matrix --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d9f6a3b9b..ceaa6b5c9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,6 +34,9 @@ jobs: - os: "ubuntu-latest" cibuild: "*musllinux*" cibw_arch: "musllinux" + - os: "ubuntu-24.04-arm" # aarch64 manylinux on ARM runner + cibuild: "*manylinux*" + cibw_arch: "manylinux" steps: - name: Free Disk Space (Ubuntu) if: matrix.os == 'ubuntu-latest' From 8f96e14a45ed64f2900a79cf08e862f083b04cdd Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 10:56:04 +0900 Subject: [PATCH 236/392] trying the job in a new workflow i can run --- .github/workflows/try_build.yml | 83 +++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 .github/workflows/try_build.yml diff --git a/.github/workflows/try_build.yml b/.github/workflows/try_build.yml new file mode 100644 index 000000000..f5c25b543 --- /dev/null +++ b/.github/workflows/try_build.yml @@ -0,0 +1,83 @@ +name: Build Python Wheels +# https://docs.github.com/en/free-pro-team@latest/actions/guides/building-and-testing-python + +on: + # we just want to test in this PR + pull_request: + + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + max-parallel: 12 + matrix: + include: + - os: "macos-13" # intel runner + cibuild: "*macosx*" + cibw_arch: "macos_x86_64" + macos-arch: "x86_64" + - os: "macos-14" # M1 runner + cibuild: "*macosx*" + cibw_arch: "macos_arm64" + macos-arch: "arm64" + - os: "ubuntu-latest" + cibuild: "*manylinux*" + cibw_arch: "manylinux" + - os: "ubuntu-latest" + cibuild: "*musllinux*" + cibw_arch: "musllinux" + - os: "ubuntu-24.04-arm" # aarch64 manylinux on ARM runner + cibuild: "*manylinux*" + cibw_arch: "manylinux" + steps: + - name: Free Disk Space (Ubuntu) + if: matrix.os == 'ubuntu-latest' + uses: jlumbroso/free-disk-space@main + with: + android: true + dotnet: true + haskell: true + large-packages: true + - uses: hmarr/debug-action@v3 + - name: Cancel Workflow Action + uses: styfle/cancel-workflow-action@0.12.1 + - uses: actions/checkout@v4 + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.cibuild }} # Make cache specific to OS + max-size: "5G" + - name: Install dependencies + run: | + env + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH + HOST_CCACHE_DIR="$(ccache -k cache_dir)" + mkdir -p $HOST_CCACHE_DIR + - name: Build wheels # check https://cibuildwheel.readthedocs.io/en/stable/setup/#github-actions + uses: pypa/cibuildwheel@v2.23.3 + # to supply options, put them in 'env', like: + # env: + # CIBW_SOME_OPTION: value + env: + CIBW_BUILD: ${{ matrix.cibuild }} + CIBW_ARCHS_MACOS: ${{ matrix.macos-arch }} + CIBW_DEPENDENCY_VERSIONS_MACOS: cibw_constraints.txt + + - name: Download Cache from Docker (linux only) + if: ${{ runner.os == 'Linux' }} and ${{ matrix.os != 'ubuntu-24.04-arm' }} + # hack until https://github.com/pypa/cibuildwheel/issues/1030 is fixed + run: | + env + ccache -s + HOST_CCACHE_DIR="$(ccache -k cache_dir)" + rm -rf $HOST_CCACHE_DIR + mv ./wheelhouse/.ccache $HOST_CCACHE_DIR + ls -la $HOST_CCACHE_DIR + ccache -s + - uses: actions/upload-artifact@v4 + with: + name: artifact-${{ matrix.os }}-${{ matrix.cibw_arch }}-${{ strategy.job-index }} + path: ./wheelhouse/*.whl \ No newline at end of file From 5ee1ccd0e267886cf8c8f695cbb84c647b35c2ea Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 10:57:56 +0900 Subject: [PATCH 237/392] see if i can build without cache --- .github/workflows/try_build.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/try_build.yml b/.github/workflows/try_build.yml index f5c25b543..92be2b359 100644 --- a/.github/workflows/try_build.yml +++ b/.github/workflows/try_build.yml @@ -44,11 +44,6 @@ jobs: - name: Cancel Workflow Action uses: styfle/cancel-workflow-action@0.12.1 - uses: actions/checkout@v4 - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.cibuild }} # Make cache specific to OS - max-size: "5G" - name: Install dependencies run: | env From ae5503e5db14478e762a18c65789661fdb54719e Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 11:00:18 +0900 Subject: [PATCH 238/392] try to run on my own fork --- .github/workflows/try_build.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/try_build.yml b/.github/workflows/try_build.yml index 92be2b359..1a1c7ad9e 100644 --- a/.github/workflows/try_build.yml +++ b/.github/workflows/try_build.yml @@ -2,8 +2,9 @@ name: Build Python Wheels # https://docs.github.com/en/free-pro-team@latest/actions/guides/building-and-testing-python on: - # we just want to test in this PR - pull_request: + push: + branches: + - support-arm-linux jobs: From 793f29296ebba10f1fa6c540bfc60157fe4065a1 Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 11:01:55 +0900 Subject: [PATCH 239/392] adding ccache job back --- .github/workflows/try_build.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/try_build.yml b/.github/workflows/try_build.yml index 1a1c7ad9e..4d7e7e691 100644 --- a/.github/workflows/try_build.yml +++ b/.github/workflows/try_build.yml @@ -45,6 +45,11 @@ jobs: - name: Cancel Workflow Action uses: styfle/cancel-workflow-action@0.12.1 - uses: actions/checkout@v4 + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.cibuild }} # Make cache specific to OS + max-size: "5G" - name: Install dependencies run: | env From 30362cc6bf59c8755cbf884fe0859cffca273637 Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 11:16:34 +0900 Subject: [PATCH 240/392] skipping ccache for arm again --- .github/workflows/try_build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/try_build.yml b/.github/workflows/try_build.yml index 4d7e7e691..5e362b824 100644 --- a/.github/workflows/try_build.yml +++ b/.github/workflows/try_build.yml @@ -46,11 +46,13 @@ jobs: uses: styfle/cancel-workflow-action@0.12.1 - uses: actions/checkout@v4 - name: ccache + if: matrix.os != 'ubuntu-24.04-arm' uses: hendrikmuhs/ccache-action@v1.2 with: key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.cibuild }} # Make cache specific to OS max-size: "5G" - name: Install dependencies + if: matrix.os != 'ubuntu-24.04-arm' run: | env export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" From 30ef907f4cb11813f1b0351eb0507f5fecb211b7 Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 11:34:54 +0900 Subject: [PATCH 241/392] breaking out arm build job --- .github/workflows/try_build.yml | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/.github/workflows/try_build.yml b/.github/workflows/try_build.yml index 5e362b824..efb3161dd 100644 --- a/.github/workflows/try_build.yml +++ b/.github/workflows/try_build.yml @@ -59,11 +59,24 @@ jobs: echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH HOST_CCACHE_DIR="$(ccache -k cache_dir)" mkdir -p $HOST_CCACHE_DIR - - name: Build wheels # check https://cibuildwheel.readthedocs.io/en/stable/setup/#github-actions + - name: Build wheels (ARM) + if: matrix.os == 'ubuntu-24.04-arm' + uses: pypa/cibuildwheel@v2.23.3 + env: + # override the default CentOS “yum install … ccache” and drop ccache + CIBW_BEFORE_ALL_LINUX: | + yum install -y \ + zlib-devel \ + curl-devel \ + expat-devel \ + libpng-devel + CIBW_BUILD: ${{ matrix.cibuild }} + CIBW_ARCHS_LINUX: ${{ matrix.cibw_arch }} + # … any other cibuildwheel env you already have … + + - name: Build wheels (all other platforms) + if: matrix.os != 'ubuntu-24.04-arm' uses: pypa/cibuildwheel@v2.23.3 - # to supply options, put them in 'env', like: - # env: - # CIBW_SOME_OPTION: value env: CIBW_BUILD: ${{ matrix.cibuild }} CIBW_ARCHS_MACOS: ${{ matrix.macos-arch }} From 50ccbff88bcc5e1d49c9c7971a3ddec5118e6cf9 Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 11:39:30 +0900 Subject: [PATCH 242/392] set arch to aarch64 --- .github/workflows/try_build.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/try_build.yml b/.github/workflows/try_build.yml index efb3161dd..e4e2089d2 100644 --- a/.github/workflows/try_build.yml +++ b/.github/workflows/try_build.yml @@ -31,7 +31,7 @@ jobs: cibw_arch: "musllinux" - os: "ubuntu-24.04-arm" # aarch64 manylinux on ARM runner cibuild: "*manylinux*" - cibw_arch: "manylinux" + cibw_arch: "aarch64" steps: - name: Free Disk Space (Ubuntu) if: matrix.os == 'ubuntu-latest' @@ -72,7 +72,6 @@ jobs: libpng-devel CIBW_BUILD: ${{ matrix.cibuild }} CIBW_ARCHS_LINUX: ${{ matrix.cibw_arch }} - # … any other cibuildwheel env you already have … - name: Build wheels (all other platforms) if: matrix.os != 'ubuntu-24.04-arm' From 2917fd509894a76f22860e7f77f219808091dd9e Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 13:30:42 +0900 Subject: [PATCH 243/392] skipping before_build stage --- .github/workflows/try_build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/try_build.yml b/.github/workflows/try_build.yml index e4e2089d2..1d4076604 100644 --- a/.github/workflows/try_build.yml +++ b/.github/workflows/try_build.yml @@ -70,6 +70,7 @@ jobs: curl-devel \ expat-devel \ libpng-devel + CIBW_BEFORE_BUILD_LINUX: "true" CIBW_BUILD: ${{ matrix.cibuild }} CIBW_ARCHS_LINUX: ${{ matrix.cibw_arch }} From c816655fde5f717dbaf6dd24402d6ff56d6a011f Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 14:50:55 +0900 Subject: [PATCH 244/392] fixing if statement. porting changes back to original file --- .github/workflows/build.yml | 27 +++++++++++++++++++++------ .github/workflows/try_build.yml | 2 +- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ceaa6b5c9..d3b1fba45 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -36,7 +36,7 @@ jobs: cibw_arch: "musllinux" - os: "ubuntu-24.04-arm" # aarch64 manylinux on ARM runner cibuild: "*manylinux*" - cibw_arch: "manylinux" + cibw_arch: "aarch64" steps: - name: Free Disk Space (Ubuntu) if: matrix.os == 'ubuntu-latest' @@ -51,29 +51,44 @@ jobs: uses: styfle/cancel-workflow-action@0.12.1 - uses: actions/checkout@v4 - name: ccache + if: matrix.os != 'ubuntu-24.04-arm' uses: hendrikmuhs/ccache-action@v1.2 with: key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.cibuild }} # Make cache specific to OS max-size: "5G" - name: Install dependencies + if: matrix.os != 'ubuntu-24.04-arm' run: | env export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH HOST_CCACHE_DIR="$(ccache -k cache_dir)" mkdir -p $HOST_CCACHE_DIR - - name: Build wheels # check https://cibuildwheel.readthedocs.io/en/stable/setup/#github-actions + - name: Build wheels (ARM) + if: matrix.os == 'ubuntu-24.04-arm' + uses: pypa/cibuildwheel@v2.23.3 + env: + # override the default CentOS “yum install … ccache” and drop ccache + CIBW_BEFORE_ALL_LINUX: | + yum install -y \ + zlib-devel \ + curl-devel \ + expat-devel \ + libpng-devel + CIBW_BEFORE_BUILD_LINUX: "true" + CIBW_BUILD: ${{ matrix.cibuild }} + CIBW_ARCHS_LINUX: ${{ matrix.cibw_arch }} + + - name: Build wheels (all other platforms) + if: matrix.os != 'ubuntu-24.04-arm' uses: pypa/cibuildwheel@v2.23.3 - # to supply options, put them in 'env', like: - # env: - # CIBW_SOME_OPTION: value env: CIBW_BUILD: ${{ matrix.cibuild }} CIBW_ARCHS_MACOS: ${{ matrix.macos-arch }} CIBW_DEPENDENCY_VERSIONS_MACOS: cibw_constraints.txt - name: Download Cache from Docker (linux only) - if: ${{ runner.os == 'Linux' }} + if: runner.os == 'Linux' && matrix.os != 'ubuntu-24.04-arm' # hack until https://github.com/pypa/cibuildwheel/issues/1030 is fixed run: | env diff --git a/.github/workflows/try_build.yml b/.github/workflows/try_build.yml index 1d4076604..d72a6cf7c 100644 --- a/.github/workflows/try_build.yml +++ b/.github/workflows/try_build.yml @@ -83,7 +83,7 @@ jobs: CIBW_DEPENDENCY_VERSIONS_MACOS: cibw_constraints.txt - name: Download Cache from Docker (linux only) - if: ${{ runner.os == 'Linux' }} and ${{ matrix.os != 'ubuntu-24.04-arm' }} + if: runner.os == 'Linux' && matrix.os != 'ubuntu-24.04-arm' # hack until https://github.com/pypa/cibuildwheel/issues/1030 is fixed run: | env From 0bf0b5210c4658ef437851e458b432e3bd80b77a Mon Sep 17 00:00:00 2001 From: Troy Tamas Date: Fri, 23 May 2025 16:28:20 +0900 Subject: [PATCH 245/392] removing temporary test workflow --- .github/workflows/try_build.yml | 99 --------------------------------- 1 file changed, 99 deletions(-) delete mode 100644 .github/workflows/try_build.yml diff --git a/.github/workflows/try_build.yml b/.github/workflows/try_build.yml deleted file mode 100644 index d72a6cf7c..000000000 --- a/.github/workflows/try_build.yml +++ /dev/null @@ -1,99 +0,0 @@ -name: Build Python Wheels -# https://docs.github.com/en/free-pro-team@latest/actions/guides/building-and-testing-python - -on: - push: - branches: - - support-arm-linux - - -jobs: - build: - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - max-parallel: 12 - matrix: - include: - - os: "macos-13" # intel runner - cibuild: "*macosx*" - cibw_arch: "macos_x86_64" - macos-arch: "x86_64" - - os: "macos-14" # M1 runner - cibuild: "*macosx*" - cibw_arch: "macos_arm64" - macos-arch: "arm64" - - os: "ubuntu-latest" - cibuild: "*manylinux*" - cibw_arch: "manylinux" - - os: "ubuntu-latest" - cibuild: "*musllinux*" - cibw_arch: "musllinux" - - os: "ubuntu-24.04-arm" # aarch64 manylinux on ARM runner - cibuild: "*manylinux*" - cibw_arch: "aarch64" - steps: - - name: Free Disk Space (Ubuntu) - if: matrix.os == 'ubuntu-latest' - uses: jlumbroso/free-disk-space@main - with: - android: true - dotnet: true - haskell: true - large-packages: true - - uses: hmarr/debug-action@v3 - - name: Cancel Workflow Action - uses: styfle/cancel-workflow-action@0.12.1 - - uses: actions/checkout@v4 - - name: ccache - if: matrix.os != 'ubuntu-24.04-arm' - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.job }}-${{ matrix.os }}-${{ matrix.cibuild }} # Make cache specific to OS - max-size: "5G" - - name: Install dependencies - if: matrix.os != 'ubuntu-24.04-arm' - run: | - env - export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH - HOST_CCACHE_DIR="$(ccache -k cache_dir)" - mkdir -p $HOST_CCACHE_DIR - - name: Build wheels (ARM) - if: matrix.os == 'ubuntu-24.04-arm' - uses: pypa/cibuildwheel@v2.23.3 - env: - # override the default CentOS “yum install … ccache” and drop ccache - CIBW_BEFORE_ALL_LINUX: | - yum install -y \ - zlib-devel \ - curl-devel \ - expat-devel \ - libpng-devel - CIBW_BEFORE_BUILD_LINUX: "true" - CIBW_BUILD: ${{ matrix.cibuild }} - CIBW_ARCHS_LINUX: ${{ matrix.cibw_arch }} - - - name: Build wheels (all other platforms) - if: matrix.os != 'ubuntu-24.04-arm' - uses: pypa/cibuildwheel@v2.23.3 - env: - CIBW_BUILD: ${{ matrix.cibuild }} - CIBW_ARCHS_MACOS: ${{ matrix.macos-arch }} - CIBW_DEPENDENCY_VERSIONS_MACOS: cibw_constraints.txt - - - name: Download Cache from Docker (linux only) - if: runner.os == 'Linux' && matrix.os != 'ubuntu-24.04-arm' - # hack until https://github.com/pypa/cibuildwheel/issues/1030 is fixed - run: | - env - ccache -s - HOST_CCACHE_DIR="$(ccache -k cache_dir)" - rm -rf $HOST_CCACHE_DIR - mv ./wheelhouse/.ccache $HOST_CCACHE_DIR - ls -la $HOST_CCACHE_DIR - ccache -s - - uses: actions/upload-artifact@v4 - with: - name: artifact-${{ matrix.os }}-${{ matrix.cibw_arch }}-${{ strategy.job-index }} - path: ./wheelhouse/*.whl \ No newline at end of file From aba912107bff5722cb2fb3af5bf6fe133e836199 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 24 May 2025 00:09:52 +0200 Subject: [PATCH 246/392] Refined solution, so it would allow a larger coordinate range. --- src/db/db/dbEdgesUtils.cc | 8 +++++--- src/db/unit_tests/dbEdgesTests.cc | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/db/db/dbEdgesUtils.cc b/src/db/db/dbEdgesUtils.cc index 382c378bd..68db12206 100644 --- a/src/db/db/dbEdgesUtils.cc +++ b/src/db/db/dbEdgesUtils.cc @@ -306,12 +306,14 @@ EdgeOrientationFilter::EdgeOrientationFilter (double a, bool inverse, bool absol bool EdgeOrientationFilter::selected (const db::Edge &edge, db::properties_id_type) const { + db::Vector en = db::Vector (std::max (edge.dx_abs (), edge.dy_abs ()), 0); + // NOTE: this edge normalization confines the angle to a range between (-90 .. 90] (-90 excluded). // A horizontal edge has 0 degree, a vertical one has 90 degree. if (edge.dx () < 0 || (edge.dx () == 0 && edge.dy () < 0)) { - return m_checker (db::Vector (edge.ortho_length (), 0), -edge.d ()); + return m_checker (en, -edge.d ()); } else { - return m_checker (db::Vector (edge.ortho_length (), 0), edge.d ()); + return m_checker (en, edge.d ()); } } @@ -363,7 +365,7 @@ SpecialEdgeOrientationFilter::selected (const db::Edge &edge, properties_id_type } db::Vector en, ev; - en = db::Vector (edge.ortho_length (), 0); + en = db::Vector (std::max (edge.dx_abs (), edge.dy_abs ()), 0); // NOTE: this edge normalization confines the angle to a range between (-90 .. 90] (-90 excluded). // A horizontal edge has 0 degree, a vertical one has 90 degree. diff --git a/src/db/unit_tests/dbEdgesTests.cc b/src/db/unit_tests/dbEdgesTests.cc index 662aceb7b..2c20bf043 100644 --- a/src/db/unit_tests/dbEdgesTests.cc +++ b/src/db/unit_tests/dbEdgesTests.cc @@ -265,6 +265,9 @@ TEST(4) // issue-2060 { db::EdgeOrientationFilter f1 (90.0, true, false); + db::EdgeOrientationFilter f2 (90.0, false, false); + db::EdgeOrientationFilter f45 (45.0, false, false); + db::SpecialEdgeOrientationFilter fs (db::SpecialEdgeOrientationFilter::Diagonal, false); db::Edges rr; rr.insert (db::Box (db::Point (0, 0), db::Point (1000, 4000000))); @@ -273,6 +276,21 @@ TEST(4) rr.clear (); rr.insert (db::Box (db::Point (0, 0), db::Point (1000, 400000))); EXPECT_EQ (db::compare (rr.filtered (f1), "(1000,0;0,0);(0,400000;1000,400000)"), true); + + rr.clear (); + rr.insert (db::Box (db::Point (0, -1000000000), db::Point (1000, 1000000000))); + EXPECT_EQ (db::compare (rr.filtered (f1), "(1000,-1000000000;0,-1000000000);(0,1000000000;1000,1000000000)"), true); + + rr.clear (); + rr.insert (db::Box (db::Point (0, -1000000000), db::Point (1000, 1000000000))); + EXPECT_EQ (db::compare (rr.filtered (f2), "(0,-1000000000;0,1000000000);(1000,1000000000;1000,-1000000000)"), true); + + EXPECT_EQ (f2.selected (db::Edge (db::Point (0, -1000000000), db::Point (0, 1000000000)), size_t (0)), true); + EXPECT_EQ (f2.selected (db::Edge (db::Point (0, -1000000000), db::Point (1, 1000000000)), size_t (0)), false); + EXPECT_EQ (f45.selected (db::Edge (db::Point (-1000000000, -1000000000), db::Point (1000000000, 1000000000)), size_t (0)), true); + EXPECT_EQ (f45.selected (db::Edge (db::Point (-1000000000, -1000000000), db::Point (1000000000, 1000000001)), size_t (0)), false); + EXPECT_EQ (fs.selected (db::Edge (db::Point (-1000000000, -1000000000), db::Point (1000000000, 1000000000)), size_t (0)), true); + EXPECT_EQ (fs.selected (db::Edge (db::Point (-1000000000, -1000000000), db::Point (1000000000, 1000000001)), size_t (0)), false); } } From 094e11897affec7a9e4a69676205c09c6e916a28 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 24 May 2025 22:33:18 +0200 Subject: [PATCH 247/392] Added the new feature: 'flag_missing_ports' --- src/db/db/dbLayoutVsSchematic.cc | 38 +++ src/db/db/dbLayoutVsSchematic.h | 16 + src/db/db/gsiDeclDbLayoutVsSchematic.cc | 12 + src/lvs/lvs/built-in-macros/_lvs_engine.rb | 9 +- src/lvs/lvs/built-in-macros/_lvs_netter.rb | 27 ++ src/lvs/unit_tests/lvsSimpleTests.cc | 5 + testdata/lvs/flag_missing_ports.cir | 23 ++ testdata/lvs/flag_missing_ports.gds | Bin 0 -> 4358 bytes testdata/lvs/flag_missing_ports.lvs | 76 +++++ testdata/lvs/flag_missing_ports.lvsdb | 362 +++++++++++++++++++++ testdata/lvs/flag_missing_ports.sch | 7 + 11 files changed, 574 insertions(+), 1 deletion(-) create mode 100644 testdata/lvs/flag_missing_ports.cir create mode 100644 testdata/lvs/flag_missing_ports.gds create mode 100644 testdata/lvs/flag_missing_ports.lvs create mode 100644 testdata/lvs/flag_missing_ports.lvsdb create mode 100644 testdata/lvs/flag_missing_ports.sch diff --git a/src/db/db/dbLayoutVsSchematic.cc b/src/db/db/dbLayoutVsSchematic.cc index 449f49d69..4104d67e9 100644 --- a/src/db/db/dbLayoutVsSchematic.cc +++ b/src/db/db/dbLayoutVsSchematic.cc @@ -25,6 +25,7 @@ #include "dbLayoutVsSchematic.h" #include "dbLayoutVsSchematicWriter.h" #include "dbLayoutVsSchematicReader.h" +#include "dbNetlistCompareUtils.h" namespace db { @@ -86,6 +87,43 @@ db::NetlistCrossReference *LayoutVsSchematic::make_cross_ref () return mp_cross_ref.get (); } +bool +LayoutVsSchematic::flag_missing_ports (const db::Circuit *circuit) +{ + if (! mp_cross_ref.get ()) { + return false; + } + + db::NetlistCrossReference::PerCircuitData *pcd = const_cast (mp_cross_ref->per_circuit_data_for (std::make_pair (circuit, circuit))); + if (! pcd) { + return false; + } + + bool error = false; + + for (auto n = pcd->nets.begin (); n != pcd->nets.end (); ++n) { + + const db::Net *schem = n->pair.second; + const db::Net *layout = n->pair.first; + + if (schem && layout && schem->begin_pins () != schem->end_pins ()) { + + if (db::name_compare (layout, schem) != 0) { + + std::string msg = tl::sprintf (tl::to_string (tr ("Port mismatch '%s' vs. '%s'")), layout->expanded_name (), schem->expanded_name ()); + db::LogEntryData entry (db::Error, msg); + + pcd->log_entries.push_back (entry); + + error = true; + + } + } + + } + + return !error; +} void LayoutVsSchematic::save (const std::string &path, bool short_format) { diff --git a/src/db/db/dbLayoutVsSchematic.h b/src/db/db/dbLayoutVsSchematic.h index 1e27ea954..6c2b242e8 100644 --- a/src/db/db/dbLayoutVsSchematic.h +++ b/src/db/db/dbLayoutVsSchematic.h @@ -144,6 +144,22 @@ public: */ db::NetlistCrossReference *make_cross_ref (); + /** + * @brief Checks top-level port names + * + * This method checks that every top-level pin has a corresponding + * schematic pin and their names are equivalent. This verifies that + * all pins are labelles properly. + * + * Errors are placed in the log file. The return values indicates + * if there are no errors. + * + * The circuit is either a schematic or layout circuit. + * + * See issue #2055. + */ + bool flag_missing_ports (const db::Circuit *circuit); + /** * @brief Saves the database to the given path * diff --git a/src/db/db/gsiDeclDbLayoutVsSchematic.cc b/src/db/db/gsiDeclDbLayoutVsSchematic.cc index f17694359..87e1d6346 100644 --- a/src/db/db/gsiDeclDbLayoutVsSchematic.cc +++ b/src/db/db/gsiDeclDbLayoutVsSchematic.cc @@ -110,6 +110,18 @@ Class decl_dbLayoutVsSchematic (decl_dbLayoutToNetlist, " "\n" "See \\NetlistCrossReference for more details.\n" ) + + gsi::method ("flag_missing_ports", &db::LayoutVsSchematic::flag_missing_ports, gsi::arg ("circuit"), + "@brief Flags inconsistent port labels in the given circuit\n" + "@param circuit Either a layout or schematic circuit\n" + "@return True, if no errors were found\n" + "This method will check all schematic nets which have pins and tests whether the corresponding layout net " + "has the same name. This way, it is checked if the pins are properly labelled.\n" + "\n" + "The method must be called after the compare step was successful. Error messages will be added " + "to the log entries. If an error occured or the cross reference is not value, 'false' is returned.\n" + "\n" + "This method was introduced in version 0.30.2." + ) + gsi::method_ext ("write_l2n", &save_l2n, gsi::arg ("path"), gsi::arg ("short_format", false), "@brief Writes the \\LayoutToNetlist part of the object to a file.\n" "This method employs the native format of KLayout.\n" diff --git a/src/lvs/lvs/built-in-macros/_lvs_engine.rb b/src/lvs/lvs/built-in-macros/_lvs_engine.rb index 2465c2ca7..11959cf40 100644 --- a/src/lvs/lvs/built-in-macros/_lvs_engine.rb +++ b/src/lvs/lvs/built-in-macros/_lvs_engine.rb @@ -225,9 +225,16 @@ module LVS # @synopsis lvs_data # See \Netter#lvs_data for a description of that function. + # %LVS% + # @name flag_missing_ports + # @brief Checks if all top level ports are properly labelled + # @synopsis flag_missing_ports + # See \Netter#flag_missing_ports for a description of that function. + %w(schematic compare split_gates join_symmetric_nets tolerance ignore_parameter enable_parameter disable_parameter blank_circuit align same_nets same_nets! same_circuits same_device_classes equivalent_pins - min_caps max_res max_depth max_branch_complexity consider_net_names lvs_data no_lvs_hints).each do |f| + min_caps max_res max_depth max_branch_complexity consider_net_names lvs_data no_lvs_hints + flag_missing_ports).each do |f| eval <<"CODE" def #{f}(*args) _netter.#{f}(*args) diff --git a/src/lvs/lvs/built-in-macros/_lvs_netter.rb b/src/lvs/lvs/built-in-macros/_lvs_netter.rb index 4b3a44f7c..c4f8f5197 100644 --- a/src/lvs/lvs/built-in-macros/_lvs_netter.rb +++ b/src/lvs/lvs/built-in-macros/_lvs_netter.rb @@ -530,6 +530,33 @@ CODE @comparer_config << lambda { |comparer| comparer.with_log = false } end + # %LVS% + # @name flag_missing_ports + # @brief Flags inconsistently labelled or missing ports in the current top circuit + # @synopsis flag_missing_ports + # This method must be called after "compare" was executed successfully and will + # report errors if pins in the current top circuit's schematic are not labelled + # correspondingly in the layout. This prevents swapping of port labels or + # pads. + # + # @code + # success = compare + # success && flag_missing_ports + # @/code + # + # Note that in order to use this method, the top circuit from the schematic netlist + # needs to have pins. This may not be always the case - for example, if the top + # level circuit is not a subcircuit in a Spice netlist. + + def flag_missing_ports + + lvs_data.netlist || raise("Netlist not extracted yet") + lvs_data.xref || raise("Compare step was not executed yet") + + lvs_data.flag_missing_ports(lvs_data.netlist.top_circuit) + + end + # %LVS% # @name same_nets # @brief Establishes an equivalence between the nets diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc index dbf89d211..69fc7e1b7 100644 --- a/src/lvs/unit_tests/lvsSimpleTests.cc +++ b/src/lvs/unit_tests/lvsSimpleTests.cc @@ -357,3 +357,8 @@ TEST(62_LayerNames) run_test (_this, "layer_names", "layer_names.gds", false, true, "TOP"); } +TEST(63_FlagMissingPorts) +{ + run_test (_this, "flag_missing_ports", "flag_missing_ports.gds", false, true, "TOP"); +} + diff --git a/testdata/lvs/flag_missing_ports.cir b/testdata/lvs/flag_missing_ports.cir new file mode 100644 index 000000000..9a86f30d6 --- /dev/null +++ b/testdata/lvs/flag_missing_ports.cir @@ -0,0 +1,23 @@ +* Extracted by KLayout + +* cell ND2X1 +* pin VSS +* pin VDD +* pin b +* pin X +* pin SUBSTRATE +.SUBCKT ND2X1 1 3 5 6 7 +* net 1 VSS +* net 3 VDD +* net 5 b +* net 6 X +* net 7 SUBSTRATE +* device instance $1 r0 *1 0.85,5.8 PMOS +M$1 1 6 2 4 PMOS L=0.25U W=1.5U AS=0.6375P AD=0.3375P PS=3.85U PD=1.95U +* device instance $2 r0 *1 1.55,5.8 PMOS +M$2 2 5 1 4 PMOS L=0.25U W=1.5U AS=0.3375P AD=0.6375P PS=1.95U PD=3.85U +* device instance $3 r0 *1 0.85,2.135 NMOS +M$3 8 6 3 7 NMOS L=0.25U W=0.95U AS=0.40375P AD=0.21375P PS=2.75U PD=1.4U +* device instance $4 r0 *1 1.55,2.135 NMOS +M$4 2 5 8 7 NMOS L=0.25U W=0.95U AS=0.21375P AD=0.40375P PS=1.4U PD=2.75U +.ENDS ND2X1 diff --git a/testdata/lvs/flag_missing_ports.gds b/testdata/lvs/flag_missing_ports.gds new file mode 100644 index 0000000000000000000000000000000000000000..9311b21cce8daa406eccb94742079d798362305c GIT binary patch literal 4358 zcmb`KO^6&t6vtn8PxpN6Cc8U3Y_jW!tK<;EfaC+zONg?F;${O~=lsK|og*FijqcnTuHlb-Y>9y}!?AqUTn|9@5eyQ>+x_d^?s-|(vb_1>#`Ro!ES zrRk21leBxsayxD-HfN8MsHp=qzkH7iIGq*mw_t?g_5AS|>^;sL|&ALHR zT)wpV+{M$@>6sNrQp{f*P0ehwm`$hCm(*Z+FK)7v)>$&?vpcc=pYzkHZ)VB&ag&{l z_125^(@!zKYw&$FZnBd#eZQtZK>S_XYo)d>UfMoWW*V;)GW!CN6eI?GZI*r_%I?^( zQW$?1*p4b^I##GCdtGmhm74$ND^5qaOQN-ieqV)x?T$Rdt9;XN9Sv zRAHj*^-7!dPv30uDdBI~!6^T_{{J{Bo$pZH$RkG#JSn2=V55J=$Z;ffj9ZkwuD8s} z^{<^Km@yYlnrE%g`QQ7bZEc*{IgLpMW`m*Lh4YIU@>(~WANxFJd-EU77Ix#_@OE(8 zWP+Gpi+jU~E_;{53Wb^ddG~&Cd(?a5nX#AU%)Oa6HdgXovpIMthc_|c?TWI4(eBi5 zhx8Y(C#lbVu1J08cly5bU!Lyb!4$jNTfo{S@7RcI$fx(dS^s&*1E{d_PNf~#pD*l) zQ)y@Rr&>PYRp8zcj+NKS=Ux>tUe)M3#;iLuJN~NY>PhgHnJ7Ehsvl8@c}BSNs4pq| z$#(t9me(J{E>SPa4mSEvKJf9Aj620#BI6JJt$O<5IQpetl)cS==L+(LUq|Gh#Y`tL7#|NS4l|B|vVw)>}^{;8w?lClTAKeMLq`1n?t*g2lQ?#oz> zuRQ18z|C?BcUIsKzmvalX9X_*Rf|fqPIQWKQ@MxV?0wu)qU>P2sx$hvpua!pMcKhd zJ-@yXpOLFCA`euCD0^G{$u+MhsY~CgNPXyU^w0ko97i(V{K<;4xA|w>!<#;Sj%WO$ z>|mpR#>e$02iOJ1FUk%&{XE>@-znCA1zxN;mhio`mf-jNeffp6?m1`f4a%>52{k_%V%Erba_2YG;etY{6`fS~OcOd$mpzitHk5pel$U1r+xZrD7 zjJo2rPbxjWls+ psd, "G" => pgate, "W" => nwell, + "tS" => psd, "tD" => psd, "tG" => poly, "tW" => nwell }) + +# NMOS transistor device extraction +extract_devices(mos4("NMOS"), { "SD" => nsd, "G" => ngate, "W" => bulk, + "tS" => nsd, "tD" => nsd, "tG" => poly, "tW" => bulk }) + +# Define connectivity for netlist extraction + +# Inter-layer +connect(psd, contact) +connect(nsd, contact) +connect(poly, contact) +connect(ntie, contact) +connect(nwell, ntie) +connect(ptie, contact) +connect(contact, metal1) +connect(metal1, via1) +connect(via1, metal2) + +# Global +connect_global(bulk, "SUBSTRATE") +connect_global(ptie, "SUBSTRATE") + +# Compare section + +netlist.simplify + +compare + +flag_missing_ports + diff --git a/testdata/lvs/flag_missing_ports.lvsdb b/testdata/lvs/flag_missing_ports.lvsdb new file mode 100644 index 000000000..34ba019af --- /dev/null +++ b/testdata/lvs/flag_missing_ports.lvsdb @@ -0,0 +1,362 @@ +#%lvsdb-klayout + +# Layout +layout( + top(ND2X1) + unit(0.001) + + # Layer section + # This section lists the mask layers (drawing or derived) and their connections. + + # Mask layers + layer(l3 '1/0') + layer(l4 '5/0') + layer(l8 '8/0') + layer(l11 '9/0') + layer(l12) + layer(l13) + layer(l7) + layer(l2) + layer(l9) + layer(l6) + layer(l10) + + # Mask layer connectivity + connect(l3 l3 l9) + connect(l4 l4 l8) + connect(l8 l4 l8 l11 l2 l9 l6 l10) + connect(l11 l8 l11 l12) + connect(l12 l11 l12 l13) + connect(l13 l12 l13) + connect(l7 l7) + connect(l2 l8 l2) + connect(l9 l3 l8 l9) + connect(l6 l8 l6) + connect(l10 l8 l10) + + # Global nets and connectivity + global(l7 SUBSTRATE) + global(l10 SUBSTRATE) + + # Device class section + class(PMOS MOS4) + class(NMOS MOS4) + + # Device abstracts section + # Device abstracts list the pin shapes of the devices. + device(D$PMOS PMOS + terminal(S + rect(l2 (-550 -750) (425 1500)) + ) + terminal(G + rect(l4 (-125 -750) (250 1500)) + ) + terminal(D + rect(l2 (125 -750) (450 1500)) + ) + terminal(B + rect(l3 (-125 -750) (250 1500)) + ) + ) + device(D$PMOS$1 PMOS + terminal(S + rect(l2 (-575 -750) (450 1500)) + ) + terminal(G + rect(l4 (-125 -750) (250 1500)) + ) + terminal(D + rect(l2 (125 -750) (425 1500)) + ) + terminal(B + rect(l3 (-125 -750) (250 1500)) + ) + ) + device(D$NMOS NMOS + terminal(S + rect(l6 (-550 -475) (425 950)) + ) + terminal(G + rect(l4 (-125 -475) (250 950)) + ) + terminal(D + rect(l6 (125 -475) (450 950)) + ) + terminal(B + rect(l7 (-125 -475) (250 950)) + ) + ) + device(D$NMOS$1 NMOS + terminal(S + rect(l6 (-575 -475) (450 950)) + ) + terminal(G + rect(l4 (-125 -475) (250 950)) + ) + terminal(D + rect(l6 (125 -475) (425 950)) + ) + terminal(B + rect(l7 (-125 -475) (250 950)) + ) + ) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(ND2X1 + + # Circuit boundary + rect((-100 400) (2600 7600)) + + # Nets with their geometries + net(1 name(VSS) + rect(l8 (1110 5160) (180 180)) + rect(l8 (-180 920) (180 180)) + rect(l8 (-180 -730) (180 180)) + rect(l11 (-240 -790) (300 1700)) + rect(l11 (-1350 0) (2400 800)) + rect(l11 (-1150 -400) (0 0)) + rect(l2 (-275 -2150) (425 1500)) + rect(l2 (-400 -1500) (425 1500)) + ) + net(2 + rect(l8 (1810 1770) (180 180)) + rect(l8 (-180 370) (180 180)) + rect(l8 (-1580 3760) (180 180)) + rect(l8 (-180 -730) (180 180)) + rect(l8 (-180 -730) (180 180)) + rect(l8 (1220 920) (180 180)) + rect(l8 (-180 -1280) (180 180)) + rect(l8 (-180 370) (180 180)) + polygon(l11 (-240 -4180) (0 1390) (490 0) (0 -300) (-190 0) (0 -1090)) + rect(l11 (-110 1390) (300 1400)) + polygon(l11 (-1890 0) (0 600) (300 0) (0 -300) (1590 0) (0 -300)) + rect(l11 (-1890 600) (300 1400)) + rect(l11 (1100 -1700) (300 300)) + rect(l11 (-300 0) (300 1400)) + rect(l2 (-1750 -1450) (425 1500)) + rect(l2 (950 -1500) (425 1500)) + rect(l6 (-425 -4890) (425 950)) + ) + net(3 name(VDD) + rect(l8 (410 1770) (180 180)) + rect(l8 (-180 370) (180 180)) + rect(l11 (-240 -1300) (300 1360)) + rect(l11 (-650 -2160) (2400 800)) + rect(l11 (-1150 -400) (0 0)) + rect(l6 (-950 860) (425 950)) + ) + net(4 + rect(l3 (-100 4500) (2600 3500)) + ) + net(5 name(b) + rect(l4 (1425 2860) (250 1940)) + rect(l4 (-345 -950) (300 300)) + rect(l4 (-205 650) (250 2000)) + rect(l4 (-250 -2000) (250 2000)) + rect(l4 (-250 -5390) (250 1450)) + rect(l8 (-285 1050) (180 180)) + rect(l11 (-70 -90) (0 0)) + rect(l11 (-170 -150) (300 300)) + ) + net(6 name(X) + rect(l4 (725 2860) (250 1940)) + rect(l4 (-325 -1850) (300 300)) + rect(l4 (-225 1550) (250 2000)) + rect(l4 (-250 -2000) (250 2000)) + rect(l4 (-250 -5390) (250 1450)) + rect(l8 (-265 150) (180 180)) + rect(l11 (-90 -90) (0 0)) + rect(l11 (-150 -150) (300 300)) + ) + net(7 name(SUBSTRATE)) + net(8 + rect(l6 (975 1660) (425 950)) + rect(l6 (-400 -950) (425 950)) + ) + + # Outgoing pins and their connections to nets + pin(1 name(VSS)) + pin(3 name(VDD)) + pin(5 name(b)) + pin(6 name(X)) + pin(7 name(SUBSTRATE)) + + # Devices and their connections + device(1 D$PMOS + location(850 5800) + param(L 0.25) + param(W 1.5) + param(AS 0.6375) + param(AD 0.3375) + param(PS 3.85) + param(PD 1.95) + terminal(S 2) + terminal(G 6) + terminal(D 1) + terminal(B 4) + ) + device(2 D$PMOS$1 + location(1550 5800) + param(L 0.25) + param(W 1.5) + param(AS 0.3375) + param(AD 0.6375) + param(PS 1.95) + param(PD 3.85) + terminal(S 1) + terminal(G 5) + terminal(D 2) + terminal(B 4) + ) + device(3 D$NMOS + location(850 2135) + param(L 0.25) + param(W 0.95) + param(AS 0.40375) + param(AD 0.21375) + param(PS 2.75) + param(PD 1.4) + terminal(S 3) + terminal(G 6) + terminal(D 8) + terminal(B 7) + ) + device(4 D$NMOS$1 + location(1550 2135) + param(L 0.25) + param(W 0.95) + param(AS 0.21375) + param(AD 0.40375) + param(PS 1.4) + param(PD 2.75) + terminal(S 8) + terminal(G 5) + terminal(D 2) + terminal(B 7) + ) + + ) +) + +# Reference netlist +reference( + + # Device class section + class(PMOS MOS4) + class(NMOS MOS4) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(ND2X1 + + # Nets + net(1 name(VDD)) + net(2 name(OUT)) + net(3 name(VSS)) + net(4 name(NWELL)) + net(5 name(B)) + net(6 name(A)) + net(7 name(BULK)) + net(8 name('1')) + + # Outgoing pins and their connections to nets + pin(1 name(VDD)) + pin(2 name(OUT)) + pin(3 name(VSS)) + pin(4 name(NWELL)) + pin(5 name(B)) + pin(6 name(A)) + pin(7 name(BULK)) + + # Devices and their connections + device(1 PMOS + name($1) + param(L 0.25) + param(W 1.5) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 1) + terminal(G 6) + terminal(D 2) + terminal(B 4) + ) + device(2 PMOS + name($2) + param(L 0.25) + param(W 1.5) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 1) + terminal(G 5) + terminal(D 2) + terminal(B 4) + ) + device(3 NMOS + name($3) + param(L 0.25) + param(W 0.95) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 3) + terminal(G 6) + terminal(D 8) + terminal(B 7) + ) + device(4 NMOS + name($4) + param(L 0.25) + param(W 0.95) + param(AS 0) + param(AD 0) + param(PS 0) + param(PD 0) + terminal(S 8) + terminal(G 5) + terminal(D 2) + terminal(B 7) + ) + + ) +) + +# Cross reference +xref( + circuit(ND2X1 ND2X1 match + log( + entry(error description('Port mismatch \'$4\' vs. \'NWELL\'')) + entry(error description('Port mismatch \'$2\' vs. \'OUT\'')) + entry(error description('Port mismatch \'SUBSTRATE\' vs. \'BULK\'')) + entry(error description('Port mismatch \'VDD\' vs. \'VSS\'')) + entry(error description('Port mismatch \'VSS\' vs. \'VDD\'')) + entry(error description('Port mismatch \'X\' vs. \'A\'')) + ) + xref( + net(8 8 match) + net(4 4 match) + net(2 2 match) + net(7 7 match) + net(3 3 match) + net(1 1 match) + net(6 6 match) + net(5 5 match) + pin(() 3 match) + pin(() 1 match) + pin(4 6 match) + pin(1 2 match) + pin(0 0 match) + pin(3 5 match) + pin(2 4 match) + device(3 3 match) + device(4 4 match) + device(1 1 match) + device(2 2 match) + ) + ) +) diff --git a/testdata/lvs/flag_missing_ports.sch b/testdata/lvs/flag_missing_ports.sch new file mode 100644 index 000000000..f0f917e0a --- /dev/null +++ b/testdata/lvs/flag_missing_ports.sch @@ -0,0 +1,7 @@ + +.SUBCKT ND2X1 VDD OUT VSS NWELL B A BULK +M$1 OUT A VDD NWELL PMOS L=0.25U W=1.5U +M$2 OUT B VDD NWELL PMOS L=0.25U W=1.5U +M$3 1 A VSS BULK NMOS L=0.25U W=0.95U +M$4 OUT B 1 BULK NMOS L=0.25U W=0.95U +.ENDS ND2X1 From dec7ad9da111f230d33250e91f173906491fed67 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 May 2025 16:28:46 +0200 Subject: [PATCH 248/392] [consider merging] properly conveying string encoding for Ruby, so that UTF-8 encoding is maintained when returning such strings from C++ --- src/gsi/gsi_test/gsiTest.cc | 16 +++-- src/gsi/gsi_test/gsiTest.h | 11 ++++ src/pya/pya/pyaConvert.cc | 2 +- src/rba/rba/rbaConvert.cc | 16 ++++- src/rba/rba/rbaConvert.h | 6 +- src/rba/rba/rbaMarshal.cc | 2 +- src/tl/tl/tlInternational.cc | 7 ++- testdata/python/basic.py | 85 ++++++++++++++++--------- testdata/ruby/basic_testcore.rb | 108 +++++++++++++++++++------------- 9 files changed, 166 insertions(+), 87 deletions(-) diff --git a/src/gsi/gsi_test/gsiTest.cc b/src/gsi/gsi_test/gsiTest.cc index 29363acca..27ac72de1 100644 --- a/src/gsi/gsi_test/gsiTest.cc +++ b/src/gsi/gsi_test/gsiTest.cc @@ -218,7 +218,7 @@ A::ia_cref_to_qs (const std::vector &ia) { QString s; for (std::vector::const_iterator i = ia.begin (); i != ia.end (); ++i) { - s.push_back (char (*i)); + s.push_back (QChar (*i)); } return s; } @@ -229,7 +229,7 @@ A::ia_cref_to_qs_ref (const std::vector &ia) static QString s; s.clear (); for (std::vector::const_iterator i = ia.begin (); i != ia.end (); ++i) { - s.push_back (char (*i)); + s.push_back (QChar (*i)); } return s; } @@ -243,7 +243,7 @@ A::ql1s_cref_to_ia (const QLatin1String &ql1s) const char *cp = ql1s.data (); size_t n = ql1s.size (); for (size_t i = 0; i < n; ++i) { - ia.push_back (*cp++); + ia.push_back ((unsigned char) *cp++); } return ia; } @@ -1270,7 +1270,15 @@ static gsi::Class decl_a ("", "A", gsi::method ("to_s", &A::to_s) + gsi::iterator ("a6", &A::a6b, &A::a6e) + gsi::iterator ("a7", &A::a7b, &A::a7e) + - gsi::iterator ("a8", &A::a8b, &A::a8e) + gsi::iterator ("a8", &A::a8b, &A::a8e) + +#if defined(HAVE_QT) + gsi::method ("ft_qba", &A::ft_qba) + + gsi::method ("ft_qs", &A::ft_qs) + +#endif + gsi::method ("ft_str", &A::ft_str) + + gsi::method ("ft_cv", &A::ft_cv) + + gsi::method ("ft_cptr", &A::ft_cptr) + + gsi::method ("ft_var", &A::ft_var) ); static gsi::Class decl_a_nc (decl_a, "", "A_NC"); diff --git a/src/gsi/gsi_test/gsiTest.h b/src/gsi/gsi_test/gsiTest.h index c2b4bf58b..100d8dd2d 100644 --- a/src/gsi/gsi_test/gsiTest.h +++ b/src/gsi/gsi_test/gsiTest.h @@ -419,6 +419,17 @@ struct A static int sp_i_get (); static void sp_i_set (int v); + // feed-through values for full cycle tests + // (mainly for string encoding and binary strings) + static std::string ft_str (const std::string &v) { return v; } + static std::vector ft_cv (const std::vector &v) { return v; } + static const char *ft_cptr (const char *v) { return v; } + static tl::Variant ft_var (const tl::Variant &v) { return v; } +#if defined(HAVE_QT) + static QString ft_qs (const QString &v) { return v; } + static QByteArray ft_qba (const QByteArray &v) { return v; } +#endif + // members std::vector m_d; int n; diff --git a/src/pya/pya/pyaConvert.cc b/src/pya/pya/pyaConvert.cc index c7b7c6f64..44afc5917 100644 --- a/src/pya/pya/pyaConvert.cc +++ b/src/pya/pya/pyaConvert.cc @@ -501,7 +501,7 @@ PyObject *c2python_func::operator() (const tl::Variant &c) } else if (c.is_bool ()) { return c2python (c.to_bool ()); } else if (c.is_a_string ()) { - return c2python (c.to_string ()); + return c2python (c.to_stdstring ()); } else if (c.is_a_bytearray ()) { return c2python (c.to_bytearray ()); } else if (c.is_long ()) { diff --git a/src/rba/rba/rbaConvert.cc b/src/rba/rba/rbaConvert.cc index 61817a5a7..cd8aa5d0d 100644 --- a/src/rba/rba/rbaConvert.cc +++ b/src/rba/rba/rbaConvert.cc @@ -29,6 +29,8 @@ #include "gsiDecl.h" +#include + namespace rba { @@ -120,9 +122,17 @@ tl::Variant ruby2c (VALUE rval) } } else if (TYPE (rval) == T_STRING) { - return tl::Variant (ruby2c (rval)); + + // UTF-8 encoded strings are taken to be string, others are byte strings + // At least this ensures consistency for a full Ruby-C++ turnaround cycle. + if (rb_enc_from_index (rb_enc_get_index (rval)) == rb_utf8_encoding ()) { + return tl::Variant (ruby2c (rval)); + } else { + return tl::Variant (ruby2c > (rval)); + } + } else { - return tl::Variant (ruby2c (rba_safe_obj_as_string (rval))); + return tl::Variant (ruby2c (rba_safe_obj_as_string (rval))); } } @@ -261,7 +271,7 @@ VALUE c2ruby (const tl::Variant &c) } else if (c.is_bool ()) { return c2ruby (c.to_bool ()); } else if (c.is_a_string ()) { - return c2ruby (c.to_string ()); + return c2ruby (c.to_stdstring ()); } else if (c.is_a_bytearray ()) { return c2ruby > (c.to_bytearray ()); } else if (c.is_long () || c.is_char ()) { diff --git a/src/rba/rba/rbaConvert.h b/src/rba/rba/rbaConvert.h index 01533f843..6865dc7d2 100644 --- a/src/rba/rba/rbaConvert.h +++ b/src/rba/rba/rbaConvert.h @@ -461,7 +461,7 @@ inline VALUE c2ruby (const float &c) template <> inline VALUE c2ruby (const std::string &c) { - return rb_str_new (c.c_str (), long (c.size ())); + return rb_utf8_str_new (c.c_str (), long (c.size ())); } template <> @@ -488,7 +488,7 @@ inline VALUE c2ruby (const QString &qs) return Qnil; } else { std::string c (tl::to_string (qs)); - return rb_str_new (c.c_str (), long (c.size ())); + return rb_utf8_str_new (c.c_str (), long (c.size ())); } } #endif @@ -509,7 +509,7 @@ inline VALUE c2ruby (const char * const & s) static const char null_string[] = "(null)"; return rb_str_new (null_string, sizeof (null_string) - 1); } else { - return rb_str_new (s, long (strlen (s))); + return rb_utf8_str_new (s, long (strlen (s))); } } diff --git a/src/rba/rba/rbaMarshal.cc b/src/rba/rba/rbaMarshal.cc index b55f82111..29cf797c5 100644 --- a/src/rba/rba/rbaMarshal.cc +++ b/src/rba/rba/rbaMarshal.cc @@ -679,7 +679,7 @@ struct reader if (!a.get ()) { *ret = Qnil; } else { - *ret = rb_str_new (a->c_str (), long (a->size ())); + *ret = rb_utf8_str_new (a->c_str (), long (a->size ())); } } }; diff --git a/src/tl/tl/tlInternational.cc b/src/tl/tl/tlInternational.cc index 343a5ffa4..f9d4e60b8 100644 --- a/src/tl/tl/tlInternational.cc +++ b/src/tl/tl/tlInternational.cc @@ -45,12 +45,13 @@ QTextCodec *ms_system_codec = 0; QString to_qstring (const std::string &s) { - return QString::fromUtf8 (s.c_str ()); + return QString::fromUtf8 (s.c_str (), s.size ()); } std::string to_string (const QString &s) { - return std::string (s.toUtf8 ().constData ()); + auto utf8 = s.toUtf8 (); + return std::string (utf8.constData (), utf8.size ()); } #if !defined(_WIN32) @@ -70,7 +71,7 @@ std::string string_to_system (const std::string &s) initialize_codecs (); } - QString qs = QString::fromUtf8 (s.c_str ()); + QString qs = QString::fromUtf8 (s.c_str (), s.size ()); return std::string (ms_system_codec->fromUnicode (qs).constData ()); } #endif diff --git a/testdata/python/basic.py b/testdata/python/basic.py index a0b39a025..9f604f9d0 100644 --- a/testdata/python/basic.py +++ b/testdata/python/basic.py @@ -2994,8 +2994,12 @@ class BasicTest(unittest.TestCase): qba = pya.A.ia_cref_to_qba([ 16, 42, 0, 8 ]) if sys.version_info < (3, 0): self.assertEqual(repr(qba), "bytearray(b'\\x10*\\x00\\x08')") + self.assertEqual(repr(pya.A.ft_qba(qba)), "bytearray(b'\\x10*\\x00\\x08')") + self.assertEqual(repr(pya.A.ft_var(qba)), "bytearray(b'\\x10*\\x00\\x08')") else: self.assertEqual(repr(qba), "b'\\x10*\\x00\\x08'") + self.assertEqual(repr(pya.A.ft_qba(qba)), "b'\\x10*\\x00\\x08'") + self.assertEqual(repr(pya.A.ft_var(qba)), "b'\\x10*\\x00\\x08'") self.assertEqual(pya.A.qba_to_ia(qba), [ 16, 42, 0, 8 ]) self.assertEqual(pya.A.qba_cref_to_ia(qba), [ 16, 42, 0, 8 ]) @@ -3079,26 +3083,29 @@ class BasicTest(unittest.TestCase): if "ia_cref_to_qs" in pya.A.__dict__: - qs = pya.A.ia_cref_to_qs([ 16, 42, 0, 8 ]) - self.assertEqual(repr(qs), "'\\x10*\\x00\\x08'") + qs = pya.A.ia_cref_to_qs([ 16, 42, 0, 8, 0x03a9 ]) + self.assertEqual(repr(qs), "'\\x10*\\x00\\x08\u03a9'") + # full cycle must preserve encoding, also for var + self.assertEqual(repr(pya.A.ft_qs(qs)), "'\\x10*\\x00\\x08\u03a9'") + self.assertEqual(repr(pya.A.ft_var(qs)), "'\\x10*\\x00\\x08\u03a9'") - self.assertEqual(pya.A.qs_to_ia(qs), [ 16, 42, 0, 8 ]) - self.assertEqual(pya.A.qs_cref_to_ia(qs), [ 16, 42, 0, 8 ]) - self.assertEqual(pya.A.qs_cptr_to_ia(qs), [ 16, 42, 0, 8 ]) - self.assertEqual(pya.A.qs_ref_to_ia(qs), [ 16, 42, 0, 8 ]) - self.assertEqual(pya.A.qs_ptr_to_ia(qs), [ 16, 42, 0, 8 ]) + self.assertEqual(pya.A.qs_to_ia(qs), [ 16, 42, 0, 8, 0x03a9 ]) + self.assertEqual(pya.A.qs_cref_to_ia(qs), [ 16, 42, 0, 8, 0x03a9 ]) + self.assertEqual(pya.A.qs_cptr_to_ia(qs), [ 16, 42, 0, 8, 0x03a9 ]) + self.assertEqual(pya.A.qs_ref_to_ia(qs), [ 16, 42, 0, 8, 0x03a9 ]) + self.assertEqual(pya.A.qs_ptr_to_ia(qs), [ 16, 42, 0, 8, 0x03a9 ]) - qs = pya.A.ia_cref_to_qs_cref([ 17, 42, 0, 8 ]) - self.assertEqual(repr(qs), "'\\x11*\\x00\\x08'") + qs = pya.A.ia_cref_to_qs_cref([ 17, 42, 0, 8, 0x03a9 ]) + self.assertEqual(repr(qs), "'\\x11*\\x00\\x08\u03a9'") - qs = pya.A.ia_cref_to_qs_ref([ 18, 42, 0, 8 ]) - self.assertEqual(repr(qs), "'\\x12*\\x00\\x08'") + qs = pya.A.ia_cref_to_qs_ref([ 18, 42, 0, 8, 0x03a9 ]) + self.assertEqual(repr(qs), "'\\x12*\\x00\\x08\u03a9'") - qs = pya.A.ia_cref_to_qs_cptr([ 19, 42, 0, 8 ]) - self.assertEqual(repr(qs), "'\\x13*\\x00\\x08'") + qs = pya.A.ia_cref_to_qs_cptr([ 19, 42, 0, 8, 0x03a9 ]) + self.assertEqual(repr(qs), "'\\x13*\\x00\\x08\u03a9'") - qs = pya.A.ia_cref_to_qs_ptr([ 20, 42, 0, 8 ]) - self.assertEqual(repr(qs), "'\\x14*\\x00\\x08'") + qs = pya.A.ia_cref_to_qs_ptr([ 20, 42, 0, 8, 0x03a9 ]) + self.assertEqual(repr(qs), "'\\x14*\\x00\\x08\u03a9'") self.assertEqual(pya.A.qs_to_ia('\x00\x01\x02'), [ 0, 1, 2 ]) @@ -3137,29 +3144,42 @@ class BasicTest(unittest.TestCase): if "ia_cref_to_ql1s" in pya.A.__dict__: - ql1s = pya.A.ia_cref_to_ql1s([ 16, 42, 0, 8 ]) - self.assertEqual(repr(ql1s), "'\\x10*\\x00\\x08'") + ql1s = pya.A.ia_cref_to_ql1s([ 16, 42, 0, 8, 0x03a9 ]) + self.assertEqual(repr(ql1s), "'\\x10*\\x00\\x08\u00a9'") - self.assertEqual(pya.A.ql1s_to_ia(ql1s), [ 16, 42, 0, 8 ]) - self.assertEqual(pya.A.ql1s_cref_to_ia(ql1s), [ 16, 42, 0, 8 ]) - self.assertEqual(pya.A.ql1s_cptr_to_ia(ql1s), [ 16, 42, 0, 8 ]) - self.assertEqual(pya.A.ql1s_ref_to_ia(ql1s), [ 16, 42, 0, 8 ]) - self.assertEqual(pya.A.ql1s_ptr_to_ia(ql1s), [ 16, 42, 0, 8 ]) + self.assertEqual(pya.A.ql1s_to_ia(ql1s), [ 16, 42, 0, 8, 0xa9 ]) + self.assertEqual(pya.A.ql1s_cref_to_ia(ql1s), [ 16, 42, 0, 8, 0xa9 ]) + self.assertEqual(pya.A.ql1s_cptr_to_ia(ql1s), [ 16, 42, 0, 8, 0xa9 ]) + self.assertEqual(pya.A.ql1s_ref_to_ia(ql1s), [ 16, 42, 0, 8, 0xa9 ]) + self.assertEqual(pya.A.ql1s_ptr_to_ia(ql1s), [ 16, 42, 0, 8, 0xa9 ]) - ql1s = pya.A.ia_cref_to_ql1s_cref([ 17, 42, 0, 8 ]) - self.assertEqual(repr(ql1s), "'\\x11*\\x00\\x08'") + ql1s = pya.A.ia_cref_to_ql1s_cref([ 17, 42, 0, 8, 0x03a9 ]) + self.assertEqual(repr(ql1s), "'\\x11*\\x00\\x08\u00a9'") - ql1s = pya.A.ia_cref_to_ql1s_ref([ 18, 42, 0, 8 ]) - self.assertEqual(repr(ql1s), "'\\x12*\\x00\\x08'") + ql1s = pya.A.ia_cref_to_ql1s_ref([ 18, 42, 0, 8, 0x03a9 ]) + self.assertEqual(repr(ql1s), "'\\x12*\\x00\\x08\u00a9'") - ql1s = pya.A.ia_cref_to_ql1s_cptr([ 19, 42, 0, 8 ]) - self.assertEqual(repr(ql1s), "'\\x13*\\x00\\x08'") + ql1s = pya.A.ia_cref_to_ql1s_cptr([ 19, 42, 0, 8, 0x03a9 ]) + self.assertEqual(repr(ql1s), "'\\x13*\\x00\\x08\u00a9'") - ql1s = pya.A.ia_cref_to_ql1s_ptr([ 20, 42, 0, 8 ]) - self.assertEqual(repr(ql1s), "'\\x14*\\x00\\x08'") + ql1s = pya.A.ia_cref_to_ql1s_ptr([ 20, 42, 0, 8, 0x03a9 ]) + self.assertEqual(repr(ql1s), "'\\x14*\\x00\\x08\u00a9'") self.assertEqual(pya.A.ql1s_to_ia('\x00\x01\x02'), [ 0, 1, 2 ]) + def test_utf8Strings(self): + + # UTF8 strings (non-Qt) + s = "\u0010*\u0000\b\u03a9" + self.assertEqual(repr(s), "'\\x10*\\x00\\x08\u03a9'") + + # full cycle must preserve encoding, also for var + self.assertEqual(repr(pya.A.ft_str(s)), "'\\x10*\\x00\\x08\u03a9'") + self.assertEqual(repr(pya.A.ft_var(s)), "'\\x10*\\x00\\x08\u03a9'") + + # NUL character terminates in const char * mode: + self.assertEqual(repr(pya.A.ft_cptr(s)), "'\\x10*'") + def test_binaryStrings(self): # binary strings (non-Qt) @@ -3167,8 +3187,13 @@ class BasicTest(unittest.TestCase): ba = pya.A.ia_cref_to_ba([ 17, 42, 0, 8 ]) if sys.version_info < (3, 0): self.assertEqual(repr(ba), "bytearray(b'\\x11*\\x00\\x08')") + self.assertEqual(repr(pya.A.ft_cv(ba)), "bytearray(b'\\x11*\\x00\\x08')") + self.assertEqual(repr(pya.A.ft_var(ba)), "bytearray(b'\\x11*\\x00\\x08')") else: self.assertEqual(repr(ba), "b'\\x11*\\x00\\x08'") + self.assertEqual(repr(pya.A.ft_cv(ba)), "b'\\x11*\\x00\\x08'") + self.assertEqual(repr(pya.A.ft_var(ba)), "b'\\x11*\\x00\\x08'") + self.assertEqual(pya.A.ba_to_ia(ba), [ 17, 42, 0, 8 ]) self.assertEqual(pya.A.ba_cref_to_ia(ba), [ 17, 42, 0, 8 ]) self.assertEqual(pya.A.ba_cptr_to_ia(ba), [ 17, 42, 0, 8 ]) diff --git a/testdata/ruby/basic_testcore.rb b/testdata/ruby/basic_testcore.rb index 72a8d1e02..ac734c3bc 100644 --- a/testdata/ruby/basic_testcore.rb +++ b/testdata/ruby/basic_testcore.rb @@ -2958,6 +2958,9 @@ class Basic_TestClass < TestBase qba = RBA::A::ia_cref_to_qba([ 16, 42, 0, 8 ]) assert_equal(qba.inspect, "\"\\x10*\\x00\\b\"") + # full cycle must preserve encoding, also for var + assert_equal(RBA::A::ft_qba(qba).inspect, "\"\\x10*\\x00\\b\"") + assert_equal(RBA::A::ft_var(qba).inspect, "\"\\x10*\\x00\\b\"") assert_equal(RBA::A::qba_to_ia(qba), [ 16, 42, 0, 8 ]) assert_equal(RBA::A::qba_cref_to_ia(qba), [ 16, 42, 0, 8 ]) @@ -3016,23 +3019,26 @@ class Basic_TestClass < TestBase if RBA::A.respond_to?(:ia_cref_to_qs) - qs = RBA::A::ia_cref_to_qs([ 16, 42, 0, 8 ]) - assert_equal(qs.inspect, "\"\\x10*\\x00\\b\"") + qs = RBA::A::ia_cref_to_qs([ 16, 42, 0, 8, 0x03a9 ]) + assert_equal(qs.inspect, "\"\\u0010*\\u0000\\b\u03a9\"") + # full cycle must preserve encoding, also for var + assert_equal(RBA::A::ft_qs(qs).inspect, "\"\\u0010*\\u0000\\b\u03a9\"") + assert_equal(RBA::A::ft_var(qs).inspect, "\"\\u0010*\\u0000\\b\u03a9\"") - assert_equal(RBA::A::qs_to_ia(qs), [ 16, 42, 0, 8 ]) - assert_equal(RBA::A::qs_cref_to_ia(qs), [ 16, 42, 0, 8 ]) - assert_equal(RBA::A::qs_cptr_to_ia(qs), [ 16, 42, 0, 8 ]) - assert_equal(RBA::A::qs_ref_to_ia(qs), [ 16, 42, 0, 8 ]) - assert_equal(RBA::A::qs_ptr_to_ia(qs), [ 16, 42, 0, 8 ]) + assert_equal(RBA::A::qs_to_ia(qs), [ 16, 42, 0, 8, 0x03a9 ]) + assert_equal(RBA::A::qs_cref_to_ia(qs), [ 16, 42, 0, 8, 0x03a9 ]) + assert_equal(RBA::A::qs_cptr_to_ia(qs), [ 16, 42, 0, 8, 0x03a9 ]) + assert_equal(RBA::A::qs_ref_to_ia(qs), [ 16, 42, 0, 8, 0x03a9 ]) + assert_equal(RBA::A::qs_ptr_to_ia(qs), [ 16, 42, 0, 8, 0x03a9 ]) - qs = RBA::A::ia_cref_to_qs_cref([ 17, 42, 0, 8 ]) - assert_equal(qs.inspect, "\"\\x11*\\x00\\b\"") - qs = RBA::A::ia_cref_to_qs_ref([ 18, 42, 0, 8 ]) - assert_equal(qs.inspect, "\"\\x12*\\x00\\b\"") - qs = RBA::A::ia_cref_to_qs_cptr([ 19, 42, 0, 8 ]) - assert_equal(qs.inspect, "\"\\x13*\\x00\\b\"") - qs = RBA::A::ia_cref_to_qs_ptr([ 20, 42, 0, 8 ]) - assert_equal(qs.inspect, "\"\\x14*\\x00\\b\"") + qs = RBA::A::ia_cref_to_qs_cref([ 17, 42, 0, 8, 0x03a9 ]) + assert_equal(qs.inspect, "\"\\u0011*\\u0000\\b\u03a9\"") + qs = RBA::A::ia_cref_to_qs_ref([ 18, 42, 0, 8, 0x03a9 ]) + assert_equal(qs.inspect, "\"\\u0012*\\u0000\\b\u03a9\"") + qs = RBA::A::ia_cref_to_qs_cptr([ 19, 42, 0, 8, 0x03a9 ]) + assert_equal(qs.inspect, "\"\\u0013*\\u0000\\b\u03a9\"") + qs = RBA::A::ia_cref_to_qs_ptr([ 20, 42, 0, 8, 0x03a9 ]) + assert_equal(qs.inspect, "\"\\u0014*\\u0000\\b\u03a9\"") assert_equal(RBA::A::qs_to_ia("\x00\x01\x02"), [ 0, 1, 2 ]) @@ -3046,23 +3052,23 @@ class Basic_TestClass < TestBase if RBA::A.respond_to?(:ia_cref_to_ql1s) - ql1s = RBA::A::ia_cref_to_ql1s([ 16, 42, 0, 8 ]) - assert_equal(ql1s.inspect, "\"\\x10*\\x00\\b\"") + ql1s = RBA::A::ia_cref_to_ql1s([ 16, 42, 0, 8, 0x03a9 ]) + assert_equal(ql1s.inspect, "\"\\u0010*\\u0000\\b\u00a9\"") - assert_equal(RBA::A::ql1s_to_ia(ql1s), [ 16, 42, 0, 8 ]) - assert_equal(RBA::A::ql1s_cref_to_ia(ql1s), [ 16, 42, 0, 8 ]) - assert_equal(RBA::A::ql1s_cptr_to_ia(ql1s), [ 16, 42, 0, 8 ]) - assert_equal(RBA::A::ql1s_ref_to_ia(ql1s), [ 16, 42, 0, 8 ]) - assert_equal(RBA::A::ql1s_ptr_to_ia(ql1s), [ 16, 42, 0, 8 ]) + assert_equal(RBA::A::ql1s_to_ia(ql1s), [ 16, 42, 0, 8, 0xa9 ]) + assert_equal(RBA::A::ql1s_cref_to_ia(ql1s), [ 16, 42, 0, 8, 0xa9 ]) + assert_equal(RBA::A::ql1s_cptr_to_ia(ql1s), [ 16, 42, 0, 8, 0xa9 ]) + assert_equal(RBA::A::ql1s_ref_to_ia(ql1s), [ 16, 42, 0, 8, 0xa9 ]) + assert_equal(RBA::A::ql1s_ptr_to_ia(ql1s), [ 16, 42, 0, 8, 0xa9 ]) - ql1s = RBA::A::ia_cref_to_ql1s_cref([ 17, 42, 0, 8 ]) - assert_equal(ql1s.inspect, "\"\\x11*\\x00\\b\"") - ql1s = RBA::A::ia_cref_to_ql1s_ref([ 18, 42, 0, 8 ]) - assert_equal(ql1s.inspect, "\"\\x12*\\x00\\b\"") - ql1s = RBA::A::ia_cref_to_ql1s_cptr([ 19, 42, 0, 8 ]) - assert_equal(ql1s.inspect, "\"\\x13*\\x00\\b\"") - ql1s = RBA::A::ia_cref_to_ql1s_ptr([ 20, 42, 0, 8 ]) - assert_equal(ql1s.inspect, "\"\\x14*\\x00\\b\"") + ql1s = RBA::A::ia_cref_to_ql1s_cref([ 17, 42, 0, 8, 0xa9 ]) + assert_equal(ql1s.inspect, "\"\\u0011*\\u0000\\b\u00a9\"") + ql1s = RBA::A::ia_cref_to_ql1s_ref([ 18, 42, 0, 8, 0xa9 ]) + assert_equal(ql1s.inspect, "\"\\u0012*\\u0000\\b\u00a9\"") + ql1s = RBA::A::ia_cref_to_ql1s_cptr([ 19, 42, 0, 8, 0xa9 ]) + assert_equal(ql1s.inspect, "\"\\u0013*\\u0000\\b\u00a9\"") + ql1s = RBA::A::ia_cref_to_ql1s_ptr([ 20, 42, 0, 8, 0xa9 ]) + assert_equal(ql1s.inspect, "\"\\u0014*\\u0000\\b\u00a9\"") assert_equal(RBA::A::ql1s_to_ia("\x00\x01\x02"), [ 0, 1, 2 ]) @@ -3077,7 +3083,7 @@ class Basic_TestClass < TestBase if RBA::A.respond_to?(:ia_cref_to_qsv) qsv = RBA::A::ia_cref_to_qsv([ 16, 42, 0, 8 ]) - assert_equal(qsv.inspect, "\"\\x10*\\x00\\b\"") + assert_equal(qsv.inspect, "\"\\u0010*\\u0000\\b\"") assert_equal(RBA::A::qsv_to_ia(qsv), [ 16, 42, 0, 8 ]) assert_equal(RBA::A::qsv_cref_to_ia(qsv), [ 16, 42, 0, 8 ]) @@ -3086,13 +3092,13 @@ class Basic_TestClass < TestBase assert_equal(RBA::A::qsv_ptr_to_ia(qsv), [ 16, 42, 0, 8 ]) qsv = RBA::A::ia_cref_to_qsv_cref([ 17, 42, 0, 8 ]) - assert_equal(qsv.inspect, "\"\\x11*\\x00\\b\"") + assert_equal(qsv.inspect, "\"\\u0011*\\u0000\\b\"") qsv = RBA::A::ia_cref_to_qsv_ref([ 18, 42, 0, 8 ]) - assert_equal(qsv.inspect, "\"\\x12*\\x00\\b\"") + assert_equal(qsv.inspect, "\"\\u0012*\\u0000\\b\"") qsv = RBA::A::ia_cref_to_qsv_cptr([ 19, 42, 0, 8 ]) - assert_equal(qsv.inspect, "\"\\x13*\\x00\\b\"") + assert_equal(qsv.inspect, "\"\\u0013*\\u0000\\b\"") qsv = RBA::A::ia_cref_to_qsv_ptr([ 20, 42, 0, 8 ]) - assert_equal(qsv.inspect, "\"\\x14*\\x00\\b\"") + assert_equal(qsv.inspect, "\"\\u0014*\\u0000\\b\"") assert_equal(RBA::A::qsv_to_ia("\x00\x01\x02"), [ 0, 1, 2 ]) @@ -3100,18 +3106,36 @@ class Basic_TestClass < TestBase end + def test_utf8Strings + + # UTF8 strings (non-Qt) + s = "\u0010*\u0000\b\u03a9" + assert_equal(s.inspect, "\"\\u0010*\\u0000\\b\u03a9\"") + + # full cycle must preserve encoding, also for var + assert_equal(RBA::A::ft_str(s).inspect, "\"\\u0010*\\u0000\\b\u03a9\"") + assert_equal(RBA::A::ft_var(s).inspect, "\"\\u0010*\\u0000\\b\u03a9\"") + + # NUL character terminates in const char * mode: + assert_equal(RBA::A::ft_cptr(s).inspect, "\"\\u0010*\"") + + end + def test_binaryStrings # binary strings (non-Qt) - ba = RBA::A::ia_cref_to_ba([ 16, 42, 1, 8 ]) - assert_equal(ba.inspect, "\"\\x10*\\x01\\b\"") + ba = RBA::A::ia_cref_to_ba([ 16, 42, 0, 8 ]) + assert_equal(ba.inspect, "\"\\x10*\\x00\\b\"") + # full cycle must preserve encoding, also for var + assert_equal(RBA::A::ft_cv(ba).inspect, "\"\\x10*\\x00\\b\"") + assert_equal(RBA::A::ft_var(ba).inspect, "\"\\x10*\\x00\\b\"") - assert_equal(RBA::A::ba_to_ia(ba), [ 16, 42, 1, 8 ]) - assert_equal(RBA::A::ba_cref_to_ia(ba), [ 16, 42, 1, 8 ]) - assert_equal(RBA::A::ba_cptr_to_ia(ba), [ 16, 42, 1, 8 ]) - assert_equal(RBA::A::ba_ref_to_ia(ba), [ 16, 42, 1, 8 ]) - assert_equal(RBA::A::ba_ptr_to_ia(ba), [ 16, 42, 1, 8 ]) + assert_equal(RBA::A::ba_to_ia(ba), [ 16, 42, 0, 8 ]) + assert_equal(RBA::A::ba_cref_to_ia(ba), [ 16, 42, 0, 8 ]) + assert_equal(RBA::A::ba_cptr_to_ia(ba), [ 16, 42, 0, 8 ]) + assert_equal(RBA::A::ba_ref_to_ia(ba), [ 16, 42, 0, 8 ]) + assert_equal(RBA::A::ba_ptr_to_ia(ba), [ 16, 42, 0, 8 ]) ba = RBA::A::ia_cref_to_ba_cref([ 17, 42, 0, 8 ]) assert_equal(ba.inspect, "\"\\x11*\\x00\\b\"") From ad80019b1219e2598f66ff5b38821e6311b74dc1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 May 2025 16:31:20 +0200 Subject: [PATCH 249/392] Adding to_s (aka str(...)) methods to RNetExtractor tech objects --- src/pex/pex/gsiDeclRNetExtractor.cc | 9 +++ src/pex/pex/pexRExtractorTech.cc | 62 +++++++++++++++++++++ src/pex/pex/pexRExtractorTech.h | 16 ++++++ src/pex/unit_tests/pexRNetExtractorTests.cc | 39 +++++++++++++ testdata/ruby/pexTests.rb | 6 ++ 5 files changed, 132 insertions(+) diff --git a/src/pex/pex/gsiDeclRNetExtractor.cc b/src/pex/pex/gsiDeclRNetExtractor.cc index 7ba3c1b54..1b9b02f03 100644 --- a/src/pex/pex/gsiDeclRNetExtractor.cc +++ b/src/pex/pex/gsiDeclRNetExtractor.cc @@ -81,6 +81,9 @@ static void via_set_merge_distance (pex::RExtractorTechVia *via, double dist) } Class decl_RExtractorTechVia ("pex", "RExtractorTechVia", + gsi::method ("to_s", &pex::RExtractorTechVia::to_string, + "@brief Returns a string describing this object" + ) + gsi::method_ext ("merge_distance", &via_get_merge_distance, "@brief Gets the merge distance\n" "If this value is not zero, it specifies the distance below (or equal) which " @@ -186,6 +189,9 @@ static void cond_set_triangulation_max_area (pex::RExtractorTechConductor *cond, } Class decl_RExtractorTechConductor ("pex", "RExtractorTechConductor", + gsi::method ("to_s", &pex::RExtractorTechConductor::to_string, + "@brief Returns a string describing this object" + ) + gsi::method_ext ("algorithm", &cond_get_algorithm, "@brief Gets the algorithm to use\n" "Specifies the algorithm to use. The default algorithm is 'SquareCounting'." @@ -305,6 +311,9 @@ static void tech_add_conductor (pex::RExtractorTech *tech, const pex::RExtractor } Class decl_RExtractorTech ("pex", "RExtractorTech", + gsi::method ("to_s", &pex::RExtractorTech::to_string, + "@brief Returns a string describing this object" + ) + gsi::method_ext ("skip_simplify", &tech_get_skip_simplify, "@brief Gets a value indicating whether to skip the simplify step\n" "This values specifies to skip the simplify step of the network after the extraction has " diff --git a/src/pex/pex/pexRExtractorTech.cc b/src/pex/pex/pexRExtractorTech.cc index 38cc77554..90195a05f 100644 --- a/src/pex/pex/pexRExtractorTech.cc +++ b/src/pex/pex/pexRExtractorTech.cc @@ -23,13 +23,75 @@ #include "pexRExtractorTech.h" +#include "tlString.h" + namespace pex { +// ------------------------------------------------------------------ + +std::string +RExtractorTechVia::to_string () const +{ + std::string res = "Via("; + res += tl::sprintf ("bottom=L%u, cut=L%u, top=L%u, R=%.12gµm²*Ohm", bottom_conductor, cut_layer, top_conductor, resistance); + if (merge_distance > 1e-10) { + res += tl::sprintf(", d_merge=%.12gµm", merge_distance); + } + res += ")"; + return res; +} + +// ------------------------------------------------------------------ + +std::string +RExtractorTechConductor::to_string () const +{ + std::string res = "Conductor("; + res += tl::sprintf ("layer=L%u, R=%.12gOhm/sq", layer, resistance); + + switch (algorithm) { + case SquareCounting: + res += ", algo=SquareCounting"; + break; + case Tesselation: + res += ", algo=Tesselation"; + break; + default: + break; + } + + if (triangulation_min_b > 1e-10) { + res += tl::sprintf(", tri_min_b=%.12gµm", triangulation_min_b); + } + + if (triangulation_max_area > 1e-10) { + res += tl::sprintf(", tri_max_area=%.12gµm", triangulation_max_area); + } + + res += ")"; + return res; +} + +// ------------------------------------------------------------------ + RExtractorTech::RExtractorTech () : skip_simplify (false) { // .. nothing yet .. } +std::string +RExtractorTech::to_string () const +{ + std::string res; + if (skip_simplify) { + res += "skip_simplify=true\n"; + } + res += tl::join (vias.begin (), vias.end (), "\n"); + res += "\n"; + res += tl::join (conductors.begin (), conductors.end (), "\n"); + return res; +} + } diff --git a/src/pex/pex/pexRExtractorTech.h b/src/pex/pex/pexRExtractorTech.h index 6062910ae..60bc6edda 100644 --- a/src/pex/pex/pexRExtractorTech.h +++ b/src/pex/pex/pexRExtractorTech.h @@ -26,6 +26,7 @@ #include "pexCommon.h" #include +#include namespace pex { @@ -45,6 +46,11 @@ public: // .. nothing yet .. } + /** + * @brief Returns a string describing this object + */ + std::string to_string () const; + /** * @brief Specifies the cut layer * This is the layer the via sits on @@ -112,6 +118,11 @@ public: // .. nothing yet .. } + /** + * @brief Returns a string describing this object + */ + std::string to_string () const; + /** * @brief Specifies the layer * The value is the generic ID of the layer. @@ -155,6 +166,11 @@ public: */ RExtractorTech (); + /** + * @brief Returns a string describing this object + */ + std::string to_string () const; + /** * @brief A list of via definitions */ diff --git a/src/pex/unit_tests/pexRNetExtractorTests.cc b/src/pex/unit_tests/pexRNetExtractorTests.cc index 2b6f9f083..bba24e0c0 100644 --- a/src/pex/unit_tests/pexRNetExtractorTests.cc +++ b/src/pex/unit_tests/pexRNetExtractorTests.cc @@ -64,6 +64,45 @@ static std::string network2s (const pex::RNetwork &network) return tl::join (r, "\n"); } +TEST(basic) +{ + unsigned int l1 = 1; + unsigned int l2 = 1; + unsigned int l3 = 1; + + pex::RExtractorTech tech; + + pex::RExtractorTechVia via1; + via1.bottom_conductor = l1; + via1.cut_layer = l2; + via1.top_conductor = l3; + via1.resistance = 2.0; + via1.merge_distance = 0.2; + tech.vias.push_back (via1); + + pex::RExtractorTechConductor cond1; + cond1.layer = l1; + cond1.resistance = 0.5; + tech.conductors.push_back (cond1); + + pex::RExtractorTechConductor cond2; + cond2.layer = l3; + cond2.resistance = 0.25; + cond2.algorithm = pex::RExtractorTechConductor::Tesselation; + cond2.triangulation_max_area = 1.5; + cond2.triangulation_min_b = 0.5; + tech.conductors.push_back (cond2); + + tech.skip_simplify = true; + + EXPECT_EQ (tech.to_string (), + "skip_simplify=true\n" + "Via(bottom=L1, cut=L1, top=L1, R=2µm²*Ohm, d_merge=0.2µm)\n" + "Conductor(layer=L1, R=0.5Ohm/sq, algo=SquareCounting)\n" + "Conductor(layer=L1, R=0.25Ohm/sq, algo=Tesselation, tri_min_b=0.5µm, tri_max_area=1.5µm)" + ); +} + TEST(netex_viagen1) { db::Layout ly; diff --git a/testdata/ruby/pexTests.rb b/testdata/ruby/pexTests.rb index 2e11f42de..838095a2b 100644 --- a/testdata/ruby/pexTests.rb +++ b/testdata/ruby/pexTests.rb @@ -142,6 +142,7 @@ class PEX_TestClass < TestBase via1.top_conductor = l3 via1.resistance = 2.0 via1.merge_distance = 0.2 + assert_equal(via1.to_s, "Via(bottom=L1, cut=L2, top=L3, R=2µm²*Ohm, d_merge=0.2µm)") assert_equal(via1.bottom_conductor, l1) assert_equal(via1.cut_layer, l2) @@ -161,6 +162,7 @@ class PEX_TestClass < TestBase cond1 = RBA::RExtractorTechConductor::new cond1.layer = l1 cond1.resistance = 0.5 + assert_equal(cond1.to_s, "Conductor(layer=L1, R=0.5Ohm/sq, algo=SquareCounting)") assert_equal(cond1.layer, l1) assert_equal(cond1.resistance, 0.5) @@ -171,6 +173,10 @@ class PEX_TestClass < TestBase tech.add_conductor(cond2) assert_equal(tech.each_conductor.collect { |c| c.layer }, [ l3 ]) + assert_equal(tech.to_s, + "Via(bottom=L1, cut=L2, top=L3, R=2µm²*Ohm, d_merge=0.2µm)\n" + + "Conductor(layer=L3, R=0.25Ohm/sq, algo=SquareCounting)" + ) tech.clear_conductors assert_equal(tech.each_conductor.collect { |c| c.layer }, []) From 125e06bd49e120b4981adafcea84456ff58dc939 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 May 2025 17:53:11 +0200 Subject: [PATCH 250/392] Added doc, added a warning about no pins found at top level. --- src/db/db/dbLayoutVsSchematic.cc | 12 +++++- src/doc/doc/about/lvs_ref_global.xml | 9 ++++ src/doc/doc/about/lvs_ref_netter.xml | 21 ++++++++++ src/doc/doc/manual/lvs_compare.xml | 63 ++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 1 deletion(-) diff --git a/src/db/db/dbLayoutVsSchematic.cc b/src/db/db/dbLayoutVsSchematic.cc index 4104d67e9..a31cfe333 100644 --- a/src/db/db/dbLayoutVsSchematic.cc +++ b/src/db/db/dbLayoutVsSchematic.cc @@ -100,6 +100,7 @@ LayoutVsSchematic::flag_missing_ports (const db::Circuit *circuit) } bool error = false; + bool any = false; for (auto n = pcd->nets.begin (); n != pcd->nets.end (); ++n) { @@ -108,11 +109,12 @@ LayoutVsSchematic::flag_missing_ports (const db::Circuit *circuit) if (schem && layout && schem->begin_pins () != schem->end_pins ()) { + any = true; + if (db::name_compare (layout, schem) != 0) { std::string msg = tl::sprintf (tl::to_string (tr ("Port mismatch '%s' vs. '%s'")), layout->expanded_name (), schem->expanded_name ()); db::LogEntryData entry (db::Error, msg); - pcd->log_entries.push_back (entry); error = true; @@ -122,6 +124,14 @@ LayoutVsSchematic::flag_missing_ports (const db::Circuit *circuit) } + if (! any) { + + std::string msg = tl::to_string (tr ("No pins found in circuit during 'flag_missing_ports'")); + db::LogEntryData entry (db::Warning, msg); + pcd->log_entries.push_back (entry); + + } + return !error; } diff --git a/src/doc/doc/about/lvs_ref_global.xml b/src/doc/doc/about/lvs_ref_global.xml index ac633b4b3..d1121526c 100644 --- a/src/doc/doc/about/lvs_ref_global.xml +++ b/src/doc/doc/about/lvs_ref_global.xml @@ -82,6 +82,15 @@ See Netter#enable_parameter

See Netter#equivalent_pins for a description of that function.

+

"flag_missing_ports" - Checks if all top level ports are properly labelled

+ +

Usage:

+
    +
  • flag_missing_ports
  • +
+

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

"ignore_parameter" - Specifies whether to ignore a parameter from a given device class for the compare

Usage:

diff --git a/src/doc/doc/about/lvs_ref_netter.xml b/src/doc/doc/about/lvs_ref_netter.xml index 463eebd69..be3e6e538 100644 --- a/src/doc/doc/about/lvs_ref_netter.xml +++ b/src/doc/doc/about/lvs_ref_netter.xml @@ -186,6 +186,27 @@ case pin names for SPICE netlists.

Use this method andwhere in the script before the compare call.

+

"flag_missing_ports" - Flags inconsistently labelled or missing ports in the current top circuit

+ +

Usage:

+
    +
  • flag_missing_ports
  • +
+

+This method must be called after "compare" was executed successfully and will +report errors if pins in the current top circuit's schematic are not labelled +correspondingly in the layout. This prevents swapping of port labels or +pads. +

+

+success = compare
+success && flag_missing_ports
+
+

+Note that in order to use this method, the top circuit from the schematic netlist +needs to have pins. This may not be always the case - for example, if the top +level circuit is not a subcircuit in a Spice netlist. +

"ignore_parameter" - Skip a specific parameter for a given device class name during device compare

Usage:

diff --git a/src/doc/doc/manual/lvs_compare.xml b/src/doc/doc/manual/lvs_compare.xml index f58319310..d4fe34697 100644 --- a/src/doc/doc/manual/lvs_compare.xml +++ b/src/doc/doc/manual/lvs_compare.xml @@ -296,6 +296,69 @@ tolerance("NMOS", "L", :absolute => 0.05, :relative => 0.01)
min_caps(1e-16)
+

Checking pin labels

+ +

+ LVS is basically name-agnostic, so except for resolving ambiguities, net names are + not considered. Topology matching has priority - if nets are not labelled + properly, LVS by default does not care. +

+ +

+ This may have adverse effects in the case of outbound connections - for example + pads. It's a fatal error to connect the chip pads incorrectly. To mitigate this + issue, the "flag_missing_ports" function is provided. +

+ +

+ You need to call this function after the compare step, i.e. +

+ +
compare
+flag_missing_ports
+ +

+ Or, if you want to quench pseudo errors, only in case of successful compare: +

+ +
success = compare
+success && flag_missing_ports
+ +

+ This function takes the schematic top circuit and investigates all + nets that are connected to a pin. It will check the name (label) of the + corresponding layout net and if names do not match, an error is written + into the log section of the LVS report. +

+ +

+ When you use this feature while working yourself bottom-up in the design, + it will make sure that all pins are properly labelled. If you use pins + in the top level circuit to describe the chip pads, this feature will make + sure that the correct nets are connected to the pads with the corresponding labels + on them. +

+ +

+ Note that it is possible to have SPICE netlists which do not have pins + at the top level circuit - e.g. if the top level circuit is not a SUBCKT. + In that case, the function will not report errors as there are not pin-carrying + nets. Only a warning is issues saying that no top level pins have been found. +

+ +

+ You can use +

+ +
schematic.make_top_level_pins
+ +

+ to create pins if none are provided. However, this method will turn every net into a pin + and force you to label every net in the top circuit then. + Hence, it is better to provide pins inside the schematic netlist. + Also note, that "make_top_level_pins" is implicitly included in "schematic.simplify". +

+

Compare and netlist hierarchy

From 4a20a308886fe99e3abddf569822f35490e4bb2d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 May 2025 18:55:52 +0200 Subject: [PATCH 251/392] Hope to fix dependency on Ruby version rgarding string encoding. --- src/rba/rba/rbaConvert.h | 2 +- src/rba/rba/rbaUtils.cc | 16 +++++++++++ src/rba/rba/rbaUtils.h | 4 +++ testdata/ruby/basic_testcore.rb | 47 ++++++++++++++++++++++----------- 4 files changed, 52 insertions(+), 17 deletions(-) diff --git a/src/rba/rba/rbaConvert.h b/src/rba/rba/rbaConvert.h index 6865dc7d2..1f1e0ee14 100644 --- a/src/rba/rba/rbaConvert.h +++ b/src/rba/rba/rbaConvert.h @@ -507,7 +507,7 @@ inline VALUE c2ruby (const char * const & s) { if (! s) { static const char null_string[] = "(null)"; - return rb_str_new (null_string, sizeof (null_string) - 1); + return rb_utf8_str_new (null_string, sizeof (null_string) - 1); } else { return rb_utf8_str_new (s, long (strlen (s))); } diff --git a/src/rba/rba/rbaUtils.cc b/src/rba/rba/rbaUtils.cc index bdb9e5c16..ab4272ba1 100644 --- a/src/rba/rba/rbaUtils.cc +++ b/src/rba/rba/rbaUtils.cc @@ -32,6 +32,10 @@ # include #endif +#if HAVE_RUBY_VERSION_CODE < 20200 +# include +#endif + static VALUE ruby_top_self = Qnil; VALUE rb_get_top_self () @@ -188,6 +192,18 @@ rba_check_error (int state) } } +#if HAVE_RUBY_VERSION_CODE < 20200 + +// Ruby <2.2 does not have this useful function +VALUE rb_utf8_str_new (const char *ptr, long len) +{ + VALUE str = rb_str_new (ptr, len); + rb_enc_associate_index (str, rb_utf8_encindex ()); + return str; +} + +#endif + /** * @brief needed because StringValue is a macro: */ diff --git a/src/rba/rba/rbaUtils.h b/src/rba/rba/rbaUtils.h index 99422bd6d..fca9b06e9 100644 --- a/src/rba/rba/rbaUtils.h +++ b/src/rba/rba/rbaUtils.h @@ -135,6 +135,10 @@ inline void rb_hash_clear(VALUE hash) #endif +#if HAVE_RUBY_VERSION_CODE < 20200 +VALUE rb_utf8_str_new (const char *str, long len); +#endif + typedef VALUE (*ruby_func)(ANYARGS); /** diff --git a/testdata/ruby/basic_testcore.rb b/testdata/ruby/basic_testcore.rb index ac734c3bc..7db26da8b 100644 --- a/testdata/ruby/basic_testcore.rb +++ b/testdata/ruby/basic_testcore.rb @@ -3020,10 +3020,13 @@ class Basic_TestClass < TestBase if RBA::A.respond_to?(:ia_cref_to_qs) qs = RBA::A::ia_cref_to_qs([ 16, 42, 0, 8, 0x03a9 ]) - assert_equal(qs.inspect, "\"\\u0010*\\u0000\\b\u03a9\"") + assert_equal(qs.encoding.name, "UTF-8") + assert_equal(qs, "\u0010*\u0000\b\u03a9") # full cycle must preserve encoding, also for var - assert_equal(RBA::A::ft_qs(qs).inspect, "\"\\u0010*\\u0000\\b\u03a9\"") - assert_equal(RBA::A::ft_var(qs).inspect, "\"\\u0010*\\u0000\\b\u03a9\"") + assert_equal(RBA::A::ft_qs(qs).encoding.name, "UTF-8") + assert_equal(RBA::A::ft_qs(qs), "\u0010*\u0000\b\u03a9") + assert_equal(RBA::A::ft_var(qs).encoding.name, "UTF-8") + assert_equal(RBA::A::ft_var(qs), "\u0010*\u0000\b\u03a9") assert_equal(RBA::A::qs_to_ia(qs), [ 16, 42, 0, 8, 0x03a9 ]) assert_equal(RBA::A::qs_cref_to_ia(qs), [ 16, 42, 0, 8, 0x03a9 ]) @@ -3032,13 +3035,17 @@ class Basic_TestClass < TestBase assert_equal(RBA::A::qs_ptr_to_ia(qs), [ 16, 42, 0, 8, 0x03a9 ]) qs = RBA::A::ia_cref_to_qs_cref([ 17, 42, 0, 8, 0x03a9 ]) - assert_equal(qs.inspect, "\"\\u0011*\\u0000\\b\u03a9\"") + assert_equal(qs.encoding.name, "UTF-8") + assert_equal(qs, "\u0011*\u0000\b\u03a9") qs = RBA::A::ia_cref_to_qs_ref([ 18, 42, 0, 8, 0x03a9 ]) - assert_equal(qs.inspect, "\"\\u0012*\\u0000\\b\u03a9\"") + assert_equal(qs.encoding.name, "UTF-8") + assert_equal(qs, "\u0012*\u0000\b\u03a9") qs = RBA::A::ia_cref_to_qs_cptr([ 19, 42, 0, 8, 0x03a9 ]) - assert_equal(qs.inspect, "\"\\u0013*\\u0000\\b\u03a9\"") + assert_equal(qs.encoding.name, "UTF-8") + assert_equal(qs, "\u0013*\u0000\b\u03a9") qs = RBA::A::ia_cref_to_qs_ptr([ 20, 42, 0, 8, 0x03a9 ]) - assert_equal(qs.inspect, "\"\\u0014*\\u0000\\b\u03a9\"") + assert_equal(qs.encoding.name, "UTF-8") + assert_equal(qs, "\u0014*\u0000\b\u03a9") assert_equal(RBA::A::qs_to_ia("\x00\x01\x02"), [ 0, 1, 2 ]) @@ -3053,7 +3060,8 @@ class Basic_TestClass < TestBase if RBA::A.respond_to?(:ia_cref_to_ql1s) ql1s = RBA::A::ia_cref_to_ql1s([ 16, 42, 0, 8, 0x03a9 ]) - assert_equal(ql1s.inspect, "\"\\u0010*\\u0000\\b\u00a9\"") + assert_equal(ql1s.encoding.name, "UTF-8") + assert_equal(ql1s, "\u0010*\u0000\b\u00a9") assert_equal(RBA::A::ql1s_to_ia(ql1s), [ 16, 42, 0, 8, 0xa9 ]) assert_equal(RBA::A::ql1s_cref_to_ia(ql1s), [ 16, 42, 0, 8, 0xa9 ]) @@ -3062,13 +3070,17 @@ class Basic_TestClass < TestBase assert_equal(RBA::A::ql1s_ptr_to_ia(ql1s), [ 16, 42, 0, 8, 0xa9 ]) ql1s = RBA::A::ia_cref_to_ql1s_cref([ 17, 42, 0, 8, 0xa9 ]) - assert_equal(ql1s.inspect, "\"\\u0011*\\u0000\\b\u00a9\"") + assert_equal(ql1s.encoding.name, "UTF-8") + assert_equal(ql1s, "\u0011*\u0000\b\u00a9") ql1s = RBA::A::ia_cref_to_ql1s_ref([ 18, 42, 0, 8, 0xa9 ]) - assert_equal(ql1s.inspect, "\"\\u0012*\\u0000\\b\u00a9\"") + assert_equal(ql1s.encoding.name, "UTF-8") + assert_equal(ql1s, "\u0012*\u0000\b\u00a9") ql1s = RBA::A::ia_cref_to_ql1s_cptr([ 19, 42, 0, 8, 0xa9 ]) - assert_equal(ql1s.inspect, "\"\\u0013*\\u0000\\b\u00a9\"") + assert_equal(ql1s.encoding.name, "UTF-8") + assert_equal(ql1s, "\u0013*\u0000\b\u00a9") ql1s = RBA::A::ia_cref_to_ql1s_ptr([ 20, 42, 0, 8, 0xa9 ]) - assert_equal(ql1s.inspect, "\"\\u0014*\\u0000\\b\u00a9\"") + assert_equal(ql1s.encoding.name, "UTF-8") + assert_equal(ql1s, "\u0014*\u0000\b\u00a9") assert_equal(RBA::A::ql1s_to_ia("\x00\x01\x02"), [ 0, 1, 2 ]) @@ -3110,14 +3122,17 @@ class Basic_TestClass < TestBase # UTF8 strings (non-Qt) s = "\u0010*\u0000\b\u03a9" - assert_equal(s.inspect, "\"\\u0010*\\u0000\\b\u03a9\"") + assert_equal(s.encoding.name, "UTF-8") # full cycle must preserve encoding, also for var - assert_equal(RBA::A::ft_str(s).inspect, "\"\\u0010*\\u0000\\b\u03a9\"") - assert_equal(RBA::A::ft_var(s).inspect, "\"\\u0010*\\u0000\\b\u03a9\"") + assert_equal(RBA::A::ft_str(s).encoding.name, "UTF-8") + assert_equal(RBA::A::ft_str(s), "\u0010*\u0000\b\u03a9") + assert_equal(RBA::A::ft_var(s).encoding.name, "UTF-8") + assert_equal(RBA::A::ft_var(s), "\u0010*\u0000\b\u03a9") # NUL character terminates in const char * mode: - assert_equal(RBA::A::ft_cptr(s).inspect, "\"\\u0010*\"") + assert_equal(RBA::A::ft_cptr(s).encoding.name, "UTF-8") + assert_equal(RBA::A::ft_cptr(s), "\u0010*") end From fb16c8c6f61ad40d6220e74fb3152d7610cb3cef Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 May 2025 19:01:04 +0200 Subject: [PATCH 252/392] Trying to fix a linker issue --- src/rba/rba/rbaUtils.cc | 16 ---------------- src/rba/rba/rbaUtils.h | 12 +++++++++++- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/rba/rba/rbaUtils.cc b/src/rba/rba/rbaUtils.cc index ab4272ba1..bdb9e5c16 100644 --- a/src/rba/rba/rbaUtils.cc +++ b/src/rba/rba/rbaUtils.cc @@ -32,10 +32,6 @@ # include #endif -#if HAVE_RUBY_VERSION_CODE < 20200 -# include -#endif - static VALUE ruby_top_self = Qnil; VALUE rb_get_top_self () @@ -192,18 +188,6 @@ rba_check_error (int state) } } -#if HAVE_RUBY_VERSION_CODE < 20200 - -// Ruby <2.2 does not have this useful function -VALUE rb_utf8_str_new (const char *ptr, long len) -{ - VALUE str = rb_str_new (ptr, len); - rb_enc_associate_index (str, rb_utf8_encindex ()); - return str; -} - -#endif - /** * @brief needed because StringValue is a macro: */ diff --git a/src/rba/rba/rbaUtils.h b/src/rba/rba/rbaUtils.h index fca9b06e9..e4d4f1684 100644 --- a/src/rba/rba/rbaUtils.h +++ b/src/rba/rba/rbaUtils.h @@ -136,7 +136,17 @@ inline void rb_hash_clear(VALUE hash) #endif #if HAVE_RUBY_VERSION_CODE < 20200 -VALUE rb_utf8_str_new (const char *str, long len); + +#include + +// Ruby <2.2 does not have this useful function +inline VALUE rb_utf8_str_new (const char *ptr, long len) +{ + VALUE str = rb_str_new (ptr, len); + rb_enc_associate_index (str, rb_utf8_encindex ()); + return str; +} + #endif typedef VALUE (*ruby_func)(ANYARGS); From bfe49c22e3aedf69df8bb5175f1599fd6535aa99 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 May 2025 19:42:14 +0200 Subject: [PATCH 253/392] Properly using 'long' and 'unsigned long' in tl::sprintf to fit Windows definition of 'long' --- src/tl/tl/tlString.cc | 6 +++--- src/tl/unit_tests/tlStringTests.cc | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/tl/tl/tlString.cc b/src/tl/tl/tlString.cc index d57dee236..7bd12c832 100644 --- a/src/tl/tl/tlString.cc +++ b/src/tl/tl/tlString.cc @@ -1975,17 +1975,17 @@ sprintf (const char *f, const std::vector &vv, unsigned int a0) os.setf (std::ios::uppercase); } if (a < vv.size ()) { - os << vv [a].to_ulong (); + os << vv [a].to_ulonglong (); } } else if (*cp == 'u' || *cp == 'U') { os.setf (std::ios_base::fmtflags (0), std::ios::basefield); if (a < vv.size ()) { - os << vv [a].to_ulong (); + os << vv [a].to_ulonglong (); } } else if (*cp == 'd' || *cp == 'D') { os.setf (std::ios_base::fmtflags (0), std::ios::basefield); if (a < vv.size ()) { - os << vv [a].to_long (); + os << vv [a].to_longlong (); } } else if (*cp == 's' || *cp == 'S') { os.setf (std::ios_base::fmtflags (0), std::ios::basefield); diff --git a/src/tl/unit_tests/tlStringTests.cc b/src/tl/unit_tests/tlStringTests.cc index 634b38e6e..8d8696b09 100644 --- a/src/tl/unit_tests/tlStringTests.cc +++ b/src/tl/unit_tests/tlStringTests.cc @@ -57,6 +57,20 @@ TEST(1) EXPECT_EQ (tl::sprintf("%lu %llu %02x", 1, 2, 167), "1 2 a7"); EXPECT_EQ (tl::sprintf("%lu %llu %02X", 1, 2, 761), "1 2 2F9"); EXPECT_EQ (tl::sprintf("%c%c", 'a', 'X'), "aX"); + + // 64bit numbers + EXPECT_EQ (tl::sprintf("%x", 0x1000000000ll), "1000000000"); + EXPECT_EQ (tl::sprintf("%lx", 0x1000000000ll), "1000000000"); + EXPECT_EQ (tl::sprintf("%llx", 0x1000000000ll), "1000000000"); + EXPECT_EQ (tl::sprintf("%d", 100000000000ll), "100000000000"); + EXPECT_EQ (tl::sprintf("%ld", 100000000000ll), "100000000000"); + EXPECT_EQ (tl::sprintf("%lld", 100000000000ll), "100000000000"); + EXPECT_EQ (tl::sprintf("%d", -100000000000ll), "-100000000000"); + EXPECT_EQ (tl::sprintf("%ld", -100000000000ll), "-100000000000"); + EXPECT_EQ (tl::sprintf("%lld", -100000000000ll), "-100000000000"); + EXPECT_EQ (tl::sprintf("%u", 100000000000ull), "100000000000"); + EXPECT_EQ (tl::sprintf("%lu", 100000000000ull), "100000000000"); + EXPECT_EQ (tl::sprintf("%llu", 100000000000ull), "100000000000"); } TEST(1a) From 534b33be1c2552cca5b3bf332bf6f5dd28134725 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 May 2025 20:40:45 +0200 Subject: [PATCH 254/392] MSVC does not assume UTF-8 encoding by default, hence use explicit bytes --- src/pex/pex/pexRExtractorTech.cc | 10 +++++----- src/pex/unit_tests/pexRNetExtractorTests.cc | 8 ++++---- testdata/ruby/pexTests.rb | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/pex/pex/pexRExtractorTech.cc b/src/pex/pex/pexRExtractorTech.cc index 90195a05f..075f8417b 100644 --- a/src/pex/pex/pexRExtractorTech.cc +++ b/src/pex/pex/pexRExtractorTech.cc @@ -34,9 +34,9 @@ std::string RExtractorTechVia::to_string () const { std::string res = "Via("; - res += tl::sprintf ("bottom=L%u, cut=L%u, top=L%u, R=%.12gµm²*Ohm", bottom_conductor, cut_layer, top_conductor, resistance); + res += tl::sprintf ("bottom=L%u, cut=L%u, top=L%u, R=%.12g \xC2\xB5m\xC2\xB2*Ohm", bottom_conductor, cut_layer, top_conductor, resistance); if (merge_distance > 1e-10) { - res += tl::sprintf(", d_merge=%.12gµm", merge_distance); + res += tl::sprintf(", d_merge=%.12g \xC2\xB5m", merge_distance); } res += ")"; return res; @@ -48,7 +48,7 @@ std::string RExtractorTechConductor::to_string () const { std::string res = "Conductor("; - res += tl::sprintf ("layer=L%u, R=%.12gOhm/sq", layer, resistance); + res += tl::sprintf ("layer=L%u, R=%.12g Ohm/sq", layer, resistance); switch (algorithm) { case SquareCounting: @@ -62,11 +62,11 @@ RExtractorTechConductor::to_string () const } if (triangulation_min_b > 1e-10) { - res += tl::sprintf(", tri_min_b=%.12gµm", triangulation_min_b); + res += tl::sprintf(", tri_min_b=%.12g \xC2\xB5m", triangulation_min_b); } if (triangulation_max_area > 1e-10) { - res += tl::sprintf(", tri_max_area=%.12gµm", triangulation_max_area); + res += tl::sprintf(", tri_max_area=%.12g \xC2\xB5m\xC2\xB2", triangulation_max_area); } res += ")"; diff --git a/src/pex/unit_tests/pexRNetExtractorTests.cc b/src/pex/unit_tests/pexRNetExtractorTests.cc index bba24e0c0..372384819 100644 --- a/src/pex/unit_tests/pexRNetExtractorTests.cc +++ b/src/pex/unit_tests/pexRNetExtractorTests.cc @@ -96,10 +96,10 @@ TEST(basic) tech.skip_simplify = true; EXPECT_EQ (tech.to_string (), - "skip_simplify=true\n" - "Via(bottom=L1, cut=L1, top=L1, R=2µm²*Ohm, d_merge=0.2µm)\n" - "Conductor(layer=L1, R=0.5Ohm/sq, algo=SquareCounting)\n" - "Conductor(layer=L1, R=0.25Ohm/sq, algo=Tesselation, tri_min_b=0.5µm, tri_max_area=1.5µm)" + u8"skip_simplify=true\n" + u8"Via(bottom=L1, cut=L1, top=L1, R=2 \xC2\xB5m\xC2\xB2*Ohm, d_merge=0.2 \xC2\xB5m)\n" + u8"Conductor(layer=L1, R=0.5 Ohm/sq, algo=SquareCounting)\n" + u8"Conductor(layer=L1, R=0.25 Ohm/sq, algo=Tesselation, tri_min_b=0.5 \xC2\xB5m, tri_max_area=1.5 \xC2\xB5m\xC2\xB2)" ); } diff --git a/testdata/ruby/pexTests.rb b/testdata/ruby/pexTests.rb index 838095a2b..efb4e3706 100644 --- a/testdata/ruby/pexTests.rb +++ b/testdata/ruby/pexTests.rb @@ -142,7 +142,7 @@ class PEX_TestClass < TestBase via1.top_conductor = l3 via1.resistance = 2.0 via1.merge_distance = 0.2 - assert_equal(via1.to_s, "Via(bottom=L1, cut=L2, top=L3, R=2µm²*Ohm, d_merge=0.2µm)") + assert_equal(via1.to_s, "Via(bottom=L1, cut=L2, top=L3, R=2 µm²*Ohm, d_merge=0.2 µm)") assert_equal(via1.bottom_conductor, l1) assert_equal(via1.cut_layer, l2) @@ -162,7 +162,7 @@ class PEX_TestClass < TestBase cond1 = RBA::RExtractorTechConductor::new cond1.layer = l1 cond1.resistance = 0.5 - assert_equal(cond1.to_s, "Conductor(layer=L1, R=0.5Ohm/sq, algo=SquareCounting)") + assert_equal(cond1.to_s, "Conductor(layer=L1, R=0.5 Ohm/sq, algo=SquareCounting)") assert_equal(cond1.layer, l1) assert_equal(cond1.resistance, 0.5) @@ -174,8 +174,8 @@ class PEX_TestClass < TestBase tech.add_conductor(cond2) assert_equal(tech.each_conductor.collect { |c| c.layer }, [ l3 ]) assert_equal(tech.to_s, - "Via(bottom=L1, cut=L2, top=L3, R=2µm²*Ohm, d_merge=0.2µm)\n" + - "Conductor(layer=L3, R=0.25Ohm/sq, algo=SquareCounting)" + "Via(bottom=L1, cut=L2, top=L3, R=2 µm²*Ohm, d_merge=0.2 µm)\n" + + "Conductor(layer=L3, R=0.25 Ohm/sq, algo=SquareCounting)" ) tech.clear_conductors From 6cccb81293a4f26ff39c0ebfc9b5a6aabe1ff858 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 May 2025 21:28:11 +0200 Subject: [PATCH 255/392] Implemented solution for #2057 (nanometer scalebar) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scale bar switches to nm below 0.1µm. Correspondingly it will switch to mm above 100µm. --- src/layview/layview/layGridNet.cc | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/layview/layview/layGridNet.cc b/src/layview/layview/layGridNet.cc index b960579b1..92fc3b6ab 100644 --- a/src/layview/layview/layGridNet.cc +++ b/src/layview/layview/layGridNet.cc @@ -538,7 +538,22 @@ GridNet::render_bg (const lay::Viewport &vp, ViewObjectCanvas &canvas) db::Point (xoffset + int (floor (0.5 + 2 * dgrid)), vp.height () - yoffset + rh / 2), ruler_color); - painter.draw_text (tl::sprintf ("%g \265m", grid * 2).c_str (), + double grid_value = grid * 2; + std::string fmt = "%g \265m"; + if (grid_value < 0.1 * (1 + db::epsilon)) { + grid_value *= 1000.0; + fmt = "%g nm"; + } else if (grid_value < 100.0 * (1 + db::epsilon)) { + fmt = "%g \265m"; + } else if (grid_value < 100000.0 * (1 + db::epsilon)) { + grid_value *= 1e-3; + fmt = "%g mm"; + } else { + grid_value *= 1e-6; + fmt = "%g m"; + } + + painter.draw_text (tl::sprintf (fmt, grid_value).c_str (), db::Point (xoffset + int (floor (0.5 + trans.ctrans (2 * grid))), vp.height () - yoffset - rh / 2 - 2), ruler_color, -1, 1); From 3a75665bfe9f91e211423ea15e4638e3d4474f38 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 May 2025 21:50:45 +0200 Subject: [PATCH 256/392] Dropping setuptools requirements as this rules out older Python versions --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a436bf5c6..aee59ff0c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools >= 77.0.3"] +requires = ["setuptools"] build-backend = "setuptools.build_meta" [tool.cibuildwheel] From 13bc72383142464ecf2e9623692ec60e860b0bae Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 25 May 2025 23:24:38 +0200 Subject: [PATCH 257/392] Fix for MSVC builds --- src/pex/unit_tests/pexRNetExtractorTests.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pex/unit_tests/pexRNetExtractorTests.cc b/src/pex/unit_tests/pexRNetExtractorTests.cc index 372384819..b8fd19f71 100644 --- a/src/pex/unit_tests/pexRNetExtractorTests.cc +++ b/src/pex/unit_tests/pexRNetExtractorTests.cc @@ -96,10 +96,10 @@ TEST(basic) tech.skip_simplify = true; EXPECT_EQ (tech.to_string (), - u8"skip_simplify=true\n" - u8"Via(bottom=L1, cut=L1, top=L1, R=2 \xC2\xB5m\xC2\xB2*Ohm, d_merge=0.2 \xC2\xB5m)\n" - u8"Conductor(layer=L1, R=0.5 Ohm/sq, algo=SquareCounting)\n" - u8"Conductor(layer=L1, R=0.25 Ohm/sq, algo=Tesselation, tri_min_b=0.5 \xC2\xB5m, tri_max_area=1.5 \xC2\xB5m\xC2\xB2)" + "skip_simplify=true\n" + "Via(bottom=L1, cut=L1, top=L1, R=2 \xC2\xB5m\xC2\xB2*Ohm, d_merge=0.2 \xC2\xB5m)\n" + "Conductor(layer=L1, R=0.5 Ohm/sq, algo=SquareCounting)\n" + "Conductor(layer=L1, R=0.25 Ohm/sq, algo=Tesselation, tri_min_b=0.5 \xC2\xB5m, tri_max_area=1.5 \xC2\xB5m\xC2\xB2)" ); } From 010fa2d5ba20bb195b531b4202bc0a3ffce72504 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 27 May 2025 00:48:10 +0200 Subject: [PATCH 258/392] Avoiding a segfault when trying to run Ruby from a non-Ruby thread --- src/rba/rba/rbaUtils.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rba/rba/rbaUtils.cc b/src/rba/rba/rbaUtils.cc index bdb9e5c16..887ae0471 100644 --- a/src/rba/rba/rbaUtils.cc +++ b/src/rba/rba/rbaUtils.cc @@ -450,6 +450,10 @@ VALUE rba_funcall2_checked (VALUE obj, ID id, int argc, VALUE *args) // HINT: the ugly (VALUE) cast is required since there is only one form of rb_protect rb_protect_init (); // see above + if (! ruby_native_thread_p ()) { + throw tl::Exception (tl::to_string (tr ("Can't execute Ruby callbacks from non-Ruby threads"))); + } + RUBY_BEGIN_EXEC ret = rb_protect (&rb_funcall2_wrap, (VALUE) &p, &error); RUBY_END_EXEC From 0cb5f356592ca050c37af947efbd390f324b66ea Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 27 May 2025 21:07:28 +0200 Subject: [PATCH 259/392] Allowing the polygon neighborhood visitor to specify a variant type for deep mode --- src/db/db/dbCellVariants.cc | 22 +++++++++++ src/db/db/dbCellVariants.h | 45 +++++++++++++++++++++++ src/db/db/dbCompoundOperation.cc | 7 +++- src/db/db/dbCompoundOperation.h | 8 ++-- src/db/db/dbPolygonNeighborhood.cc | 40 ++++++++++++++------ src/db/db/dbPolygonNeighborhood.h | 32 ++++++++++++++-- src/db/db/gsiDeclDbCell.cc | 35 ++++++++++++++++++ src/db/db/gsiDeclDbPolygonNeighborhood.cc | 14 +++++++ 8 files changed, 184 insertions(+), 19 deletions(-) diff --git a/src/db/db/dbCellVariants.cc b/src/db/db/dbCellVariants.cc index 74ab634a4..f4d928289 100644 --- a/src/db/db/dbCellVariants.cc +++ b/src/db/db/dbCellVariants.cc @@ -712,6 +712,28 @@ VariantsCollectorBase::create_var_instances_tl_invariant (db::Cell &in_cell, std // ------------------------------------------------------------------------------------------ +TransformationReducer * +make_reducer (ReducerType type) +{ + + switch (type) { + case Orientation: + return new OrientationReducer (); + case Orthogonal: + return new OrthogonalTransformationReducer (); + case Magnification: + return new MagnificationReducer (); + case XYAnisotropyAndMagnification: + return new XYAnisotropyAndMagnificationReducer (); + case MagnificationAndOrientation: + return new MagnificationAndOrientationReducer (); + default: + return 0; + } +} + +// ------------------------------------------------------------------------------------------ + VariantStatistics::VariantStatistics () : mp_red () { diff --git a/src/db/db/dbCellVariants.h b/src/db/db/dbCellVariants.h index 18e21f11a..b33e40c81 100644 --- a/src/db/db/dbCellVariants.h +++ b/src/db/db/dbCellVariants.h @@ -166,6 +166,51 @@ private: int64_t m_grid; }; +/** + * @brief An enum describing a reducer type + * + * This enum is used to create a generic reducer (parameterless) from the code. + */ +enum ReducerType +{ + /** + * @brief No specific reducer + */ + NoReducer = 0, + + /** + * @brief Rotation/mirror variants + */ + Orientation = 1, + + /** + * @brief Orthogonal transformations (rotations of multiple of 90 degree) variants + */ + Orthogonal = 2, + + /** + * @brief Scaling variants + */ + Magnification = 3, + + /** + * @brief Scaling and x/y assymmetry variants (i.e. anisotropic size) + */ + XYAnisotropyAndMagnification = 4, + + /** + * @brief Scaling and orientation variants + */ + MagnificationAndOrientation = 5 +}; + +/** + * @brief Creates a TransformationReducer from the type enum + * + * This function returns 0 if the type is NoReducer or invalid. + */ +DB_PUBLIC TransformationReducer *make_reducer (ReducerType type); + /** * @brief A class computing variants for cells according to a given criterion * diff --git a/src/db/db/dbCompoundOperation.cc b/src/db/db/dbCompoundOperation.cc index cd9f8dcad..80445387f 100644 --- a/src/db/db/dbCompoundOperation.cc +++ b/src/db/db/dbCompoundOperation.cc @@ -302,7 +302,7 @@ CompoundTransformationReducer::is_translation_invariant () const // --------------------------------------------------------------------------------------------- -CompoundRegionMultiInputOperationNode::CompoundRegionMultiInputOperationNode (const std::vector &children) +CompoundRegionMultiInputOperationNode::CompoundRegionMultiInputOperationNode (const std::vector &children, bool no_init) { for (std::vector::const_iterator c = children.begin (); c != children.end (); ++c) { (*c)->keep (); @@ -362,6 +362,11 @@ CompoundRegionMultiInputOperationNode::init () for (tl::shared_collection::iterator i = m_children.begin (); i != m_children.end (); ++i) { m_vars.add (i->vars ()); } + + // add the local variant reducer + if (local_vars ()) { + m_vars.add (local_vars ()); + } } CompoundRegionMultiInputOperationNode::~CompoundRegionMultiInputOperationNode () diff --git a/src/db/db/dbCompoundOperation.h b/src/db/db/dbCompoundOperation.h index 4f109553a..78c8661c3 100644 --- a/src/db/db/dbCompoundOperation.h +++ b/src/db/db/dbCompoundOperation.h @@ -453,7 +453,7 @@ class DB_PUBLIC CompoundRegionMultiInputOperationNode : public CompoundRegionOperationNode { public: - CompoundRegionMultiInputOperationNode (const std::vector &children); + CompoundRegionMultiInputOperationNode (const std::vector &children, bool no_init = false); CompoundRegionMultiInputOperationNode (); CompoundRegionMultiInputOperationNode (CompoundRegionOperationNode *child); CompoundRegionMultiInputOperationNode (CompoundRegionOperationNode *a, CompoundRegionOperationNode *b); @@ -521,14 +521,16 @@ protected: CompoundRegionOperationNode *child (unsigned int index); const CompoundRegionOperationNode *child (unsigned int index) const; + virtual const TransformationReducer *local_vars () const { return 0; } + + void init (); + private: tl::shared_collection m_children; // maps child#,layer# to layer# of child: std::map, unsigned int> m_map_layer_to_child; std::vector m_inputs; CompoundTransformationReducer m_vars; - - void init (); }; diff --git a/src/db/db/dbPolygonNeighborhood.cc b/src/db/db/dbPolygonNeighborhood.cc index 21a7a9696..35fe0b384 100644 --- a/src/db/db/dbPolygonNeighborhood.cc +++ b/src/db/db/dbPolygonNeighborhood.cc @@ -28,38 +28,42 @@ namespace db { PolygonNeighborhoodVisitor::PolygonNeighborhoodVisitor () - : m_result_type (db::CompoundRegionOperationNode::ResultType::Edges) + : m_result_type (db::CompoundRegionOperationNode::ResultType::Edges), m_variant_type (db::NoReducer) { disconnect_outputs (); } void -PolygonNeighborhoodVisitor::connect_output (Layout * /*layout*/, std::unordered_set *polygons) const +PolygonNeighborhoodVisitor::connect_output (Layout * /*layout*/, std::unordered_set *polygons, const db::ICplxTrans &trans) const { disconnect_outputs (); mp_polygons = polygons; + m_trans = trans; } void -PolygonNeighborhoodVisitor::connect_output (db::Layout *layout, std::unordered_set *polygons) const +PolygonNeighborhoodVisitor::connect_output (db::Layout *layout, std::unordered_set *polygons, const db::ICplxTrans &trans) const { disconnect_outputs (); mp_layout = layout; mp_polygon_refs = polygons; + m_trans = trans; } void -PolygonNeighborhoodVisitor::connect_output (db::Layout * /*layout*/, std::unordered_set *edges) const +PolygonNeighborhoodVisitor::connect_output (db::Layout * /*layout*/, std::unordered_set *edges, const db::ICplxTrans &trans) const { disconnect_outputs (); mp_edges = edges; + m_trans = trans; } void -PolygonNeighborhoodVisitor::connect_output (Layout * /*layout*/, std::unordered_set *edge_pairs) const +PolygonNeighborhoodVisitor::connect_output (Layout * /*layout*/, std::unordered_set *edge_pairs, const db::ICplxTrans &trans) const { disconnect_outputs (); mp_edge_pairs = edge_pairs; + m_trans = trans; } void @@ -76,10 +80,10 @@ void PolygonNeighborhoodVisitor::output_polygon (const db::PolygonWithProperties &poly) { if (mp_polygons) { - mp_polygons->insert (poly); + mp_polygons->insert (poly.transformed (m_trans)); } else if (mp_polygon_refs) { tl_assert (mp_layout != 0); - mp_polygon_refs->insert (db::PolygonRefWithProperties (db::PolygonRef (poly, mp_layout->shape_repository ()), poly.properties_id ())); + mp_polygon_refs->insert (db::PolygonRefWithProperties (db::PolygonRef (poly.transformed (m_trans), mp_layout->shape_repository ()), poly.properties_id ())); } else { throw tl::Exception (tl::to_string (tr ("PolygonNeighborhoodVisitor is not configured for edge output (use 'result_type=Edges')"))); } @@ -91,7 +95,7 @@ PolygonNeighborhoodVisitor::output_edge (const db::EdgeWithProperties &edge) if (mp_edges == 0) { throw tl::Exception (tl::to_string (tr ("PolygonNeighborhoodVisitor is not configured for edge output (use 'result_type=Edges')"))); } - mp_edges->insert (edge); + mp_edges->insert (edge.transformed (m_trans)); } void @@ -100,16 +104,22 @@ PolygonNeighborhoodVisitor::output_edge_pair (const db::EdgePairWithProperties & if (mp_edge_pairs == 0) { throw tl::Exception (tl::to_string (tr ("PolygonNeighborhoodVisitor is not configured for edge pair output (use 'result_type=EdgePairs')"))); } - mp_edge_pairs->insert (edge_pair); + mp_edge_pairs->insert (edge_pair.transformed (m_trans)); } // -------------------------------------------------------------------------------------------------- PolygonNeighborhoodCompoundOperationNode::PolygonNeighborhoodCompoundOperationNode (const std::vector &children, PolygonNeighborhoodVisitor *visitor, db::Coord dist) - : CompoundRegionMultiInputOperationNode (children), m_dist (dist), mp_visitor (visitor) + : CompoundRegionMultiInputOperationNode (children, true /*no implicit init()*/), + m_dist (dist), mp_visitor (visitor) { tl_assert (visitor != 0); visitor->keep (); + + m_vars.reset (db::make_reducer (visitor->variant_type ())); + + // must be called after local_vars() is available + init (); } db::Coord @@ -170,12 +180,19 @@ PolygonNeighborhoodCompoundOperationNode::compute_local_impl (CompoundRegionOper tl_assert (interactions.num_subjects () == 1); tl_assert (! results.empty ()); + db::ICplxTrans var_trans, var_trans_inv; + if (proc->vars ()) { + var_trans_inv = proc->vars ()->single_variant_transformation (cell->cell_index ()); + var_trans = var_trans_inv.inverted (); + } + try { - mp_visitor->connect_output (layout, &results.front ()); + mp_visitor->connect_output (layout, &results.front (), var_trans_inv); const T &pr = interactions.begin_subjects ()->second; db::PolygonWithProperties subject (pr.instantiate (), pr.properties_id ()); + subject.transform (var_trans); PolygonNeighborhoodVisitor::neighbors_type neighbors; @@ -191,6 +208,7 @@ PolygonNeighborhoodCompoundOperationNode::compute_local_impl (CompoundRegionOper for (auto p = others.front ().begin (); p != others.front ().end (); ++p) { n.push_back (db::PolygonWithProperties (p->instantiate (), p->properties_id ())); + n.back ().transform (var_trans); } } diff --git a/src/db/db/dbPolygonNeighborhood.h b/src/db/db/dbPolygonNeighborhood.h index 17bd13178..25314f6dd 100644 --- a/src/db/db/dbPolygonNeighborhood.h +++ b/src/db/db/dbPolygonNeighborhood.h @@ -58,22 +58,22 @@ public: /** * @brief Configure the polygon output */ - void connect_output (db::Layout * /*layout*/, std::unordered_set *polygons) const; + void connect_output (db::Layout * /*layout*/, std::unordered_set *polygons, const db::ICplxTrans &trans) const; /** * @brief Configure the polygon ref output */ - void connect_output (db::Layout *layout, std::unordered_set *polygons) const; + void connect_output (db::Layout *layout, std::unordered_set *polygons, const db::ICplxTrans &trans) const; /** * @brief Configure the edge output */ - void connect_output (db::Layout * /*layout*/, std::unordered_set *edges) const; + void connect_output (db::Layout * /*layout*/, std::unordered_set *edges, const db::ICplxTrans &trans) const; /** * @brief Configure the edge pair output */ - void connect_output (db::Layout * /*layout*/, std::unordered_set *edge_pairs) const; + void connect_output (db::Layout * /*layout*/, std::unordered_set *edge_pairs, const db::ICplxTrans &trans) const; /** * @brief Disconnects output @@ -102,6 +102,22 @@ public: return m_result_type; } + /** + * @brief Sets the variant type + */ + void set_variant_type (db::ReducerType variant_type) + { + m_variant_type = variant_type; + } + + /** + * @brief Gets the variant type + */ + db::ReducerType variant_type () const + { + return m_variant_type; + } + /** * @brief Delivers a polygon * This function is only permitted if the result type is Region. @@ -122,11 +138,13 @@ public: private: db::CompoundRegionOperationNode::ResultType m_result_type; + db::ReducerType m_variant_type; mutable std::unordered_set *mp_polygons; mutable std::unordered_set *mp_polygon_refs; mutable std::unordered_set *mp_edges; mutable std::unordered_set *mp_edge_pairs; mutable db::Layout *mp_layout; + mutable db::ICplxTrans m_trans; }; /** @@ -148,6 +166,11 @@ public: return false; } + virtual const TransformationReducer *local_vars () const + { + return m_vars.get (); + } + protected: virtual db::Coord computed_dist () const; virtual std::string generated_description () const; @@ -162,6 +185,7 @@ protected: private: db::Coord m_dist; tl::weak_ptr mp_visitor; + std::unique_ptr m_vars; template void compute_local_impl (CompoundRegionOperationCache * /*cache*/, db::Layout * /*layout*/, db::Cell * /*cell*/, const shape_interactions & /*interactions*/, std::vector > & /*results*/, const db::LocalProcessorBase * /*proc*/) const; diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index 4b4bc70a5..0e2f6f119 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -22,6 +22,7 @@ #include "gsiDecl.h" #include "gsiDeclDbMetaInfo.h" +#include "gsiEnums.h" #include "gsiDeclDbHelpers.h" #include "dbLayout.h" @@ -4655,5 +4656,39 @@ Class decl_DCellInstArray ("db", "DCellInstArray", "This class has been introduced in version 0.25." ); +gsi::Enum decl_VariantType ("db", "VariantType", + gsi::enum_const ("NoVariants", db::NoReducer, + "@brief No variants needed." + ) + + gsi::enum_const ("Orientation", db::Orientation, + "@brief Orientation variants needed.\n" + "For example, the edge orientation selection operation needs this variant type." + ) + + gsi::enum_const ("Orthogonal", db::Orthogonal, + "@brief Orthogonal transformations (rotations by multiples of 90 degree) need variants.\n" + "For example, the diagonal edge selection operation needs this variant type." + ) + + gsi::enum_const ("Magnification", db::Magnification, + "@brief Scaling variants needed.\n" + "For example, distance measurements or the isotropic sizing operations needs this variant type." + ) + + gsi::enum_const ("XYAnisotropyAndMagnification", db::XYAnisotropyAndMagnification, + "@brief Scaling and anisotropy variants needed.\n" + "For example, the anisotropic sizing operation needs this variant type." + ) + + gsi::enum_const ("MagnificationAndOrientation", db::MagnificationAndOrientation, + "@brief Scaling and orientation variants needed.\n" + "For example, the 'move' operation needs this variant type." + ), + "@brief This class represents the cell variant type for various methods.\n" + "\n" + "Cell variants are needed in hierarchical applications, when operations are to be " + "performed on cell level, but the operations are not transformation invariant.\n" + "In that case, a variant type needs to be specified in order to make the algorithm " + "separate the cells by their absolute orientation or by their accumulated magnification.\n" + "\n" + "This enum has been introduced in version 0.30.2." +); + } diff --git a/src/db/db/gsiDeclDbPolygonNeighborhood.cc b/src/db/db/gsiDeclDbPolygonNeighborhood.cc index 37c0ffded..b8c9ebb5c 100644 --- a/src/db/db/gsiDeclDbPolygonNeighborhood.cc +++ b/src/db/db/gsiDeclDbPolygonNeighborhood.cc @@ -109,6 +109,20 @@ Class decl_PolygonNeighborhoodVisitorImpl ( ) + gsi::method ("result_type", &PolygonNeighborhoodVisitorImpl::result_type, "@brief Gets the result type\n" + ) + + gsi::method ("variant_type=", &PolygonNeighborhoodVisitorImpl::set_variant_type, gsi::arg ("variant_type"), + "@brief Configures the variant type\n" + "The variant type configures transformation variant formation. The polygons presented to the visitor are " + "normalized to the given variant type. For example, specify \\VariantType#Orientation to force orientation variants " + "in the cell tree. Polygons presented to the visitor are normalized to 'as if top' orientation with this variant type.\n" + "\n" + "This property was introduced in version 0.30.2." + ) + + gsi::method ("variant_type", &PolygonNeighborhoodVisitorImpl::variant_type, + "@brief Gets the variant type\n" + "See \\variant_type= for a description of this property.\n" + "\n" + "This property was introduced in version 0.30.2." ), "@brief A visitor for the neighborhood of polygons in the input\n" "\n" From 2a9acd1264cc9595228bbdeea5337905feaeb914 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 27 May 2025 22:25:47 +0200 Subject: [PATCH 260/392] Added tests --- testdata/ruby/dbCellTests.rb | 23 ++++++++++ testdata/ruby/dbPolygonNeighborhood.rb | 63 ++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/testdata/ruby/dbCellTests.rb b/testdata/ruby/dbCellTests.rb index dec702c68..dc92a7ea7 100644 --- a/testdata/ruby/dbCellTests.rb +++ b/testdata/ruby/dbCellTests.rb @@ -348,6 +348,29 @@ class DBCellTests_TestClass < TestBase end + # variant enums + def test_VariantType + + vt = RBA::VariantType::NoVariants + assert_equal(vt.to_s, "NoVariants") + + vt = RBA::VariantType::Orientation + assert_equal(vt.to_s, "Orientation") + + vt = RBA::VariantType::Orthogonal + assert_equal(vt.to_s, "Orthogonal") + + vt = RBA::VariantType::Magnification + assert_equal(vt.to_s, "Magnification") + + vt = RBA::VariantType::XYAnisotropyAndMagnification + assert_equal(vt.to_s, "XYAnisotropyAndMagnification") + + vt = RBA::VariantType::MagnificationAndOrientation + assert_equal(vt.to_s, "MagnificationAndOrientation") + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/dbPolygonNeighborhood.rb b/testdata/ruby/dbPolygonNeighborhood.rb index e5d5aaf67..0bdfaf33f 100644 --- a/testdata/ruby/dbPolygonNeighborhood.rb +++ b/testdata/ruby/dbPolygonNeighborhood.rb @@ -65,6 +65,29 @@ class PMyVisitor2 < RBA::PolygonNeighborhoodVisitor end +class PMyVisitor4Deep < RBA::PolygonNeighborhoodVisitor + + def initialize + self.result_type = RBA::CompoundRegionOperationNode::ResultType::Region + self.variant_type = RBA::VariantType::Orientation + end + + def neighbors(layout, cell, polygon, neighborhood) + prim_box = polygon.bbox + neighborhood.each do |inp, others| + others.each do |other| + other_box = other.bbox + top_part = prim_box.top - other_box.top + bot_part = other_box.bottom - prim_box.bottom + if top_part > 0 && bot_part > 0 && top_part > 2 * bot_part + output(polygon) + end + end + end + end + +end + class DBPolygonNeighborhood_TestClass < TestBase # basic events @@ -136,6 +159,46 @@ class DBPolygonNeighborhood_TestClass < TestBase end + # full example with deep mode, variant building + def test_3 + + ly = RBA::Layout::new + l1 = ly.layer(1, 0) + l2 = ly.layer(2, 0) + + top = ly.create_cell("TOP") + child = ly.create_cell("CHILD") + + child.shapes(l1).insert(RBA::Box::new(-5000, -100, 5000, 100)) + child.shapes(l2).insert(RBA::Box::new(-1100, -3000, -900, 1000)) + child.shapes(l2).insert(RBA::Box::new(-100, -2000, 100, 2000)) + child.shapes(l2).insert(RBA::Box::new(900, -1000, 1100, 3000)) + + top.insert(RBA::CellInstArray::new(child, RBA::Trans::new(RBA::Vector::new(0, -5000)))) + top.insert(RBA::CellInstArray::new(child, RBA::Trans::new(RBA::Trans::M0, RBA::Vector::new(0, 5000)))) + top.insert(RBA::CellInstArray::new(child, RBA::Trans::new(RBA::Trans::R90, RBA::Vector::new(-10000, 0)))) + + dss = RBA::DeepShapeStore::new + # Ruby does not like to be called from threads, so none given here: + dss.threads = 0 + l1r = RBA::Region::new(RBA::RecursiveShapeIterator::new(ly, top, l1), dss) + l2r = RBA::Region::new(RBA::RecursiveShapeIterator::new(ly, top, l2), dss) + + overlap = l1r & l2r + puts overlap.to_s + + children = [ + RBA::CompoundRegionOperationNode::new_secondary(overlap) + ] + visitor = PMyVisitor4Deep::new + node = RBA::CompoundRegionOperationNode::new_polygon_neighborhood(children, visitor, -1) + + errors = l2r.complex_op(node) + + assert_equal(errors.to_s, "(900,-6000;900,-2000;1100,-2000;1100,-6000);(-1100,4000;-1100,8000;-900,8000;-900,4000)") + + end + end load("test_epilogue.rb") From ad444c55525b671700543178768ff36b875caa0f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 28 May 2025 18:58:28 +0200 Subject: [PATCH 261/392] Don't error out on missing MACRO in LEF, but create a dummy macro with size 0,0 instead and issue a warning. This will place nicely with N, but not with other orientations. --- .../lefdef/db_plugin/dbDEFImporter.cc | 30 ++++++++++++++---- .../lefdef/db_plugin/dbLEFImporter.h | 10 +++++- .../lefdef/unit_tests/dbLEFDEFImportTests.cc | 3 ++ .../lefdef/foreigncell/au_no_macros.oas.gz | Bin 0 -> 1242 bytes testdata/lefdef/foreigncell/macros.gds | Bin 0 -> 3718 bytes 5 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 testdata/lefdef/foreigncell/au_no_macros.oas.gz create mode 100644 testdata/lefdef/foreigncell/macros.gds diff --git a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc index b07a347b0..ed3856e6e 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc @@ -1594,6 +1594,7 @@ DEFImporter::read_styles (double scale) } } + void DEFImporter::read_components (db::Layout &layout, std::list > &instances, double scale) { @@ -1607,9 +1608,26 @@ DEFImporter::read_components (db::Layout &layout, std::list::const_iterator m = reader_state ()->lef_importer ().macros ().find (model); - if (m == reader_state ()->lef_importer ().macros ().end ()) { - error (tl::to_string (tr ("Macro not found in LEF file: ")) + model); + const MacroDesc *m = 0; + + std::map::const_iterator im = reader_state ()->lef_importer ().macros ().find (model); + if (im == reader_state ()->lef_importer ().macros ().end ()) { + + warn (tl::sprintf (tl::to_string (tr ("Macro not found in LEF file: %s - creating dummy macro")), model)); + + // create a dummy macro definition (no FOREIGN, size 0x0 etc.) + GeometryBasedLayoutGenerator *mg = new GeometryBasedLayoutGenerator (); + reader_state ()->register_macro_cell (model, mg); + + MacroDesc macro_desc; + macro_desc.bbox = db::Box (db::Point (), db::Point ()); + + m = reader_state ()->lef_importer ().insert_macro (model, macro_desc); + + } else { + + m = &im->second; + } while (test ("+")) { @@ -1621,7 +1639,7 @@ DEFImporter::read_components (db::Layout &layout, std::listsecond.bbox.transformed (ft).lower_left (); + d = pt - m->bbox.transformed (ft).lower_left (); is_placed = true; } else if (test ("UNPLACED")) { @@ -1633,7 +1651,7 @@ DEFImporter::read_components (db::Layout &layout, std::listsecond.bbox.transformed (ft).lower_left (); + d = pt - m->bbox.transformed (ft).lower_left (); is_placed = true; } @@ -1654,7 +1672,7 @@ DEFImporter::read_components (db::Layout &layout, std::list ct = reader_state ()->macro_cell (model, layout, m_component_maskshift, string2masks (maskshift), m->second, &reader_state ()->lef_importer ()); + std::pair ct = reader_state ()->macro_cell (model, layout, m_component_maskshift, string2masks (maskshift), *m, &reader_state ()->lef_importer ()); if (ct.first) { db::CellInstArray inst (db::CellInst (ct.first->cell_index ()), db::Trans (ft.rot (), d) * ct.second); instances.push_back (std::make_pair (inst_name, inst)); diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.h b/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.h index 94721a5fe..38c953194 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.h +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.h @@ -122,7 +122,7 @@ public: } /** - * @brief Gets the + * @brief Gets the macros map * * The map maps the macro name to the macro description. */ @@ -131,6 +131,14 @@ public: return m_macros; } + /** + * @brief Inserts a macro description for a name + */ + const MacroDesc *insert_macro (const std::string &mn, const MacroDesc &m) + { + return &m_macros.insert (std::make_pair (mn, m)).first->second; + } + /** * @brief Finishes reading a LEF file * diff --git a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc index f430e4829..8f27111a6 100644 --- a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc +++ b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc @@ -521,6 +521,9 @@ TEST(109_foreigncell) options.set_macro_resolution_mode (2); run_test (_this, "foreigncell", "gds:foreign.gds+lef:in_tech.lef+lef:in.lef+def:in.def", "au_always_foreign.oas.gz", options, false); + + // no macros -> warning + run_test (_this, "foreigncell", "gds:macros.gds+lef:in_tech.lef+def:in.def", "au_no_macros.oas.gz", options, false); } TEST(110_lefpins) diff --git a/testdata/lefdef/foreigncell/au_no_macros.oas.gz b/testdata/lefdef/foreigncell/au_no_macros.oas.gz new file mode 100644 index 0000000000000000000000000000000000000000..98458d212ca3c5a581a8d6692d4a5d5196e68acb GIT binary patch literal 1242 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKC?n3q!6L)YEF;ds&!EIm7M^>?kgLIf$L*n^ z!iwD$Gn0~auPrkXEdBMqp4&D2`TLJ6LVe~4rOcb!YvuZ(D`;uK)^4_=&-^lYX4)p1 z@x>Ind~wRnEGcr^Il9=>}tN-`TE11|qHHhe}yW6K1d% zOx$sCo0XE2xtHj|3;}WG<|z`wyh~P^xX#^fzBN=Jc5boVyYr2+Jc9JT&njHX6Owf5 z(h=RO+FRKL&K>_ZL+a#%<_Bw+EPJ~>ith?%+B1)tSAH{@a6EHzc~j6WoBl)L`6QkN zkrQI0gq}aQTk_6XFN9O9^IT5ZgWVq{E;G#keIjMI#(9nS$-j;SCvRMEs^EuG#KrnN z|NnPOu6*CM*Y{qq?bkJeGM5jP$NW&Scr5;#$9MYb1iR=RC&Xs3B)(YReE;k1)%Tv2 zo^=jaVvrJgoc5ic_v6Pe#i`fQ(jGWIV_@KARAT64ddcW|Zm-wj0EuIT^S__Gg#Z%9HIMFPbo>AH- zXGQ%)x@j@YSQL}f~9!)LUvZrma`|Tx` z&m%5u%Y69g`MUH+GGG25Kc~+NOhGf4-Z8rNZv=W+r0wtdlfBoM%oCfv`zJ$h@ImpF zO0HgotKMI9oMSabK~_ek#akoj6Hm~Rj>-KBvQ5p-6E6ks*?8gH^WQ)A7w-xz{pixE z-@fRn2NUN~wd0qj>@rcABwSqQHqW!7%P{``g702`*SWT7+U-2hl3wwku5aO~kI!ar zOP@FK=j~*^?$cg7!p-%cJlXpBSd7*4+s3&pfd(9lr*Y;t$CqX0t_-#*RDUS4beqSI ziR+90JBPmEU@jKi?juva@5I52LHwbO)>{?Cr!Cxms44A**o_6Rs@1zEg%@NL&o@cz zwtPGL*_40(4T@yG@xtB9O@>c!8M#%wiNt)&^$33;aq9;SR}ZoQ1s{69v;BRGc|kbv42!uU{+dHTv3*vpP|PaQ=eADj^hjrOpMdm85tOr8S5DD NFflSTj4Z&w006azIpqKV literal 0 HcmV?d00001 diff --git a/testdata/lefdef/foreigncell/macros.gds b/testdata/lefdef/foreigncell/macros.gds new file mode 100644 index 0000000000000000000000000000000000000000..6bf27d07297c1d02d47d91344d8d79f537f21f8d GIT binary patch literal 3718 zcmeH~y-!n77{=dwZ@JVe6_uE1hzlfGMJe_Bqb7o2hy;x3K>TWIDT*o8&_-}|GdeiS z(!s=NjEfEo#=$`sCkOuo6E_DZ7kr*`&Z}3f5{(fxp5*X5_x(QS-20MBD84@??VjKK zR+^+;*2*sFj;gCB5gs_p~Khr9D9EE-M%C9J@vVhIcabDw%__N?Ck99A05dJ zjOFq}BZH%OlvqL}zBZ&xR<#DABi!msQj*=_@&19~;jGb9)*|a6+gvNn)>7H7!O;@~ zLq|t4T|(U|X{Bzea6Xmo=tytxHPV&RMrpZvxlo*}Sd|z}LN*#r;c96*l|5A^_wUT~ zWX@Rq3hOypxl)?0mQvY?Lb+Npo^ffX=ftH-Eng{2LDyBSjpzB?Wqq4X?ZyJdq>Wc- zGUoeDfZc`s9w8Gyda{v;Jo%4jc+x@ulMZPGded} zs2UHOLs%_tHQQIHP8LOCYcO#$341f0y+409y}$NucKp8^-c7T7_gjmH_KS+ealb9T zY>_bOi!Y}mUzq*U27wHw@*vi@cu{V~dK0FSYwDF)x?$d#Mfo0>e2mSZF-cC!ImaC< zAmwBSCgsXKSm!aRKjuhZ%E{H3l(RP4k@Tlr<7iw`^;jBz_s%b>|FNlmmPQyPbJZR1 zgRJLTggmD?OtOx-&5?CdPJ8tu)sE*`$J~HPIqN1l>gKOy0_&TYv}ax=mZrYO`zV`z zJf;x_$$h+ukaa!9RI;vDm`diMUdcM>uf)>C{a*eB8ex#|^I{P)FXc+q=dHuD5^^tT z^@jAPUWujgZ=C-L_wVhd5!j7C^dlLUa^}f7o^^5(--eMZagW}p(;sl8KjkFu&7++0 zX-6_HoJc~?ICjYHu|n=W^+Hcz8E?77~75$#CEc?jaW^1i3$w=R`Tr{gET>DJPkiag?;9ob}V6 zd<{}gG7sg<%Xpf<4bSxBdD2darHSv}lO_6S!uC=8|NF9&2EVzJ2J&}z(m?)=P8!Ja zc2dp1T%9zKf2)%Q@@I5Xts~k=*|Fwb=OWn=+2P20AUi7Um6X$t9r9U(+WC}F{a6Q_ rDio(LWfuQZTUc1Qh)^HwZ-u4%sEL9;iZT}cs_@<;*4|HXVlnv$B$a~F literal 0 HcmV?d00001 From 99b358ffbe59dd20a088cbe8eb88f949d067732a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 28 May 2025 23:02:51 +0200 Subject: [PATCH 262/392] Preparations for 0.30.2 --- Changelog | 29 +++++++++++++++++++++++++++-- Changelog.Debian | 7 +++++++ version.sh | 4 ++-- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/Changelog b/Changelog index 1138f86d5..959045731 100644 --- a/Changelog +++ b/Changelog @@ -1,8 +1,32 @@ -0.30.2 (2025-xx-xx): +0.30.2 (2025-05-29): * Enhancement: %GITHUB%/issues/2016 Lazy evaluation of PCell also when changing guiding shape properties +* Enhancement: %GITHUB%/issues/2041 strm2oas should read --lefdef-lefs and --lefdef-lef-layouts only once +* Enhancement: %GITHUB%/issues/2055 Check ports feature for LVS +* Enhancement: %GITHUB%/issues/2057 Feature request: nanometer scalebar +* Bugfix: %GITHUB%/issues/2060 Edges#with_angle fails for long edges +* Enhancement: Support for ARM Python packages (thanks, Troy!) +* Enhancement: PolygonNeighborHood now allows specifying a variant type + for anisotropic applications +* Bugfix: Lower case hex escapes in URI's were not handled correctly +* Bugfix: internally handling "%ld" sprintf as 64bit compatible for + better Linux/Windows compatibility (e.g. file position in error messages + was not printed correctly on Windows at positions >2G) +* Bugfix: LVS DB internal net names now are preserved and not renumbered + This way, log entries are consistent with net names and the generated netlist + names are consistent with LVS reports. +* Bugfix: "same_nets" net names are now case insensitive in both netlists + instead of schematic only. See https://github.com/IHP-GmbH/IHP-Open-PDK/issues/484 +* Feature: Ruby/Python API for adding and removing log entries in LVS DBs + LayoutToNetlist#clear_log_entries and LayoutToNetlist#add_log_entry +* Feature: NetlistCrossReference#each_net_pair etc. functions take a single + Circuit or Net object instead of a pair (can be schematic or layout objects) +* Feature: Support functions for parasitic extraction - experimental + Currently, pieces for R extraction are available in the pex Python module space. + These are just first bits - see https://martinjankoehler.github.io/klayout-pex-website/doc/doc.html + for ongoing work. +* Feature: MALY jobdeck format support (reading) - experimental 0.30.1 (2025-04-27): -* Bugfix: %GITHUB%/issues/2038 DEdgePairWithProperties not working properly in scripts * Bugfix: %GITHUB%/issues/2011 Some DRC bugs fixed * Bug: %GITHUB%/issues/2014 Bug fixes in LEF/DEF reader * Enhancement: %GITHUB%/issues/2019 Support for Qt 6.9 @@ -10,6 +34,7 @@ * Enhancement: %GITHUB%/issues/2024 Option to configure grid density * Bugfix: %GITHUB%/issues/2025 Brackets get added in List type PCell parameter edit field * Bugfix: %GITHUB%/issues/2026 Display is dead after opening 2.5d view +* Bugfix: %GITHUB%/issues/2038 DEdgePairWithProperties not working properly in scripts * Bugfix/Enhancement: some updates of "strmxor" tool - strmxor was giving wrong results if cell variants are present where one variant is covered entirely by a large shape diff --git a/Changelog.Debian b/Changelog.Debian index a69eb136e..5becd631b 100644 --- a/Changelog.Debian +++ b/Changelog.Debian @@ -1,3 +1,10 @@ +klayout (0.30.2-1) unstable; urgency=low + + * New features and bugfixes + - See changelog + + -- Matthias Köfferlein Wed, 28 May 2025 22:44:45 +0200 + klayout (0.30.1-1) unstable; urgency=low * New features and bugfixes diff --git a/version.sh b/version.sh index 3b9bf8033..6f8701eb0 100644 --- a/version.sh +++ b/version.sh @@ -2,10 +2,10 @@ # This script is sourced to define the main version parameters # The main version -KLAYOUT_VERSION="0.30.1" +KLAYOUT_VERSION="0.30.2" # The version used for PyPI (don't use variables here!) -KLAYOUT_PYPI_VERSION="0.30.1" +KLAYOUT_PYPI_VERSION="0.30.2" # The build date KLAYOUT_VERSION_DATE=$(date "+%Y-%m-%d") From f1cc048a4cc1c7396292f5235a85a7ad49235d0b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 29 May 2025 09:56:11 +0200 Subject: [PATCH 263/392] Refreshing stubs --- src/pymod/distutils_src/klayout/dbcore.pyi | 578 +++++++++++++++++--- src/pymod/distutils_src/klayout/rdbcore.pyi | 8 +- 2 files changed, 507 insertions(+), 79 deletions(-) diff --git a/src/pymod/distutils_src/klayout/dbcore.pyi b/src/pymod/distutils_src/klayout/dbcore.pyi index 703445e19..015e7c718 100644 --- a/src/pymod/distutils_src/klayout/dbcore.pyi +++ b/src/pymod/distutils_src/klayout/dbcore.pyi @@ -4588,22 +4588,25 @@ class Circuit(NetlistObject): """ ... @overload - def nets_by_name(self, name_pattern: str) -> List[Net]: + def nets_by_name(self, name_pattern: str, case_sensitive: Optional[Any] = ...) -> List[Net]: r""" @brief Gets the net objects for a given name filter. The name filter is a glob pattern. This method will return all \Net objects matching the glob pattern. + The 'case_sensitive' argument will control whether the name is looked up in a case sensitive way or not. Note that with case insensitive search on a netlist that is case sensitive, the same name may render more than one hit. By default, case sensitivity is taken from the netlist. This method has been introduced in version 0.27.3. + The 'case_sensitive' argument has been added in version 0.30.2. """ ... @overload - def nets_by_name(self, name_pattern: str) -> List[Net]: + def nets_by_name(self, name_pattern: str, case_sensitive: Optional[Any] = ...) -> List[Net]: r""" @brief Gets the net objects for a given name filter (const version). The name filter is a glob pattern. This method will return all \Net objects matching the glob pattern. + The 'case_sensitive' argument will control whether the name is looked up in a case sensitive way or not. Note that with case insensitive search on a netlist that is case sensitive, the same name may render more than one hit. By default, case sensitivity is taken from the netlist. - - This constness variant has been introduced in version 0.27.3 + This constness variant has been introduced in version 0.27.3. + The 'case_sensitive' argument has been added in version 0.30.2. """ ... @overload @@ -15645,7 +15648,8 @@ class DText: Setter: @brief Sets the horizontal alignment - This is the version accepting integer values. It's provided for backward compatibility. + This property specifies how the text is aligned relative to the anchor point. + This property has been introduced in version 0.22 and extended to enums in 0.28. """ size: float r""" @@ -15681,7 +15685,8 @@ class DText: Setter: @brief Sets the vertical alignment - This is the version accepting integer values. It's provided for backward compatibility. + This property specifies how the text is aligned relative to the anchor point. + This property has been introduced in version 0.22 and extended to enums in 0.28. """ x: float r""" @@ -16438,13 +16443,13 @@ class DTrans: Getter: @brief Gets to the displacement vector - Staring with version 0.25 the displacement type is a vector. + Starting with version 0.25 the displacement type is a vector. Setter: @brief Sets the displacement @param u The new displacement This method was introduced in version 0.20. - Staring with version 0.25 the displacement type is a vector. + Starting with version 0.25 the displacement type is a vector. """ mirror: bool r""" @@ -34958,11 +34963,11 @@ class Instance: Starting with version 0.25 the displacement is of vector type. Setter: - @brief Sets the displacement vector for the 'b' axis + @brief Sets the displacement vector for the 'b' axis in micrometer units - If the instance was not an array instance before it is made one. + Like \b= with an integer displacement, this method will set the displacement vector but it accepts a vector in micrometer units that is of \DVector type. The vector will be translated to database units internally. - This method has been introduced in version 0.23. Starting with version 0.25 the displacement is of vector type. + This method has been introduced in version 0.25. """ cell: Cell r""" @@ -35005,9 +35010,10 @@ class Instance: @brief Gets the complex transformation of the instance or the first instance in the array This method is always valid compared to \trans, since simple transformations can be expressed as complex transformations as well. Setter: - @brief Sets the complex transformation of the instance or the first instance in the array + @brief Sets the complex transformation of the instance or the first instance in the array (in micrometer units) + This method sets the transformation the same way as \cplx_trans=, but the displacement of this transformation is given in micrometer units. It is internally translated into database units. - This method has been introduced in version 0.23. + This method has been introduced in version 0.25. """ da: DVector r""" @@ -35723,6 +35729,13 @@ class LEFDEFReaderConfiguration: See \dbu for details. """ @property + def lef_context_enabled(self) -> None: + r""" + WARNING: This variable can only be set, not retrieved. + @hide + """ + ... + @property def paths_relative_to_cwd(self) -> None: r""" WARNING: This variable can only be set, not retrieved. @@ -41693,6 +41706,12 @@ class LayoutToNetlist: Usually it's not required to call this method. It has been introduced in version 0.24. """ ... + def add_log_entry(self, entry: LogEntryData) -> None: + r""" + @brief Adds a log entry. + This method has been introduced in version 0.30.2 + """ + ... @overload def antenna_check(self, gate: Region, gate_area_factor: float, gate_perimeter_factor: float, metal: Region, metal_area_factor: float, metal_perimeter_factor: float, ratio: float, diodes: Optional[Sequence[Any]] = ..., texts: Optional[Texts] = ...) -> Region: r""" @@ -41781,7 +41800,7 @@ class LayoutToNetlist: 'lmap' defines which layers are to be produced. It is map, where the keys are layer indexes in the target layout and the values are Region or Texts objects or layer indexes, indicating the layer where shapes are to be taken from. 'lmap' can also be left nil, in which case, a layer mapping will be provided based on the layer info attributes of the layers (see \layer_info). - 'cmap' specifies the cell mapping. Use \create_cell_mapping or \const_create_cell_mapping to define the target cells in the target layout and to derive a cell mapping. + 'cmap' specifies the cell mapping. Use \cell_mapping_into or \const_cell_mapping_into to define the target cells in the target layout and to derive a cell mapping. The method has three net annotation modes: @ul @@ -41899,6 +41918,12 @@ class LayoutToNetlist: Explicit net joining has been introduced in version 0.27. """ ... + def clear_log_entries(self) -> None: + r""" + @brief Clears the log entries. + This method has been introduced in version 0.30.2 + """ + ... @overload def connect(self, a: Region, b: Region) -> None: r""" @@ -42841,6 +42866,18 @@ class LayoutVsSchematic(LayoutToNetlist): @brief Compare the layout-extracted netlist against the reference netlist using the given netlist comparer. """ ... + def flag_missing_ports(self, circuit: Circuit) -> bool: + r""" + @brief Flags inconsistent port labels in the given circuit + @param circuit Either a layout or schematic circuit + @return True, if no errors were found + This method will check all schematic nets which have pins and tests whether the corresponding layout net has the same name. This way, it is checked if the pins are properly labelled. + + The method must be called after the compare step was successful. Error messages will be added to the log entries. If an error occured or the cross reference is not value, 'false' is returned. + + This method was introduced in version 0.30.2. + """ + ... def read(self, path: str) -> None: r""" @brief Reads the LVS object from the file. @@ -43787,6 +43824,51 @@ class LoadLayoutOptions: This method has been added in version 0.26.2. """ + maly_create_other_layers: bool + r""" + Getter: + @brief Gets a value indicating whether other layers shall be created + @return True, if other layers will be created. + This attribute acts together with a layer map (see \maly_layer_map=). Layers not listed in this map are created as well when \maly_create_other_layers? is true. Otherwise they are ignored. + + This method has been added in version 0.30.2. + Setter: + @brief Specifies whether other layers shall be created + @param create True, if other layers will be created. + See \maly_create_other_layers? for a description of this attribute. + + This method has been added in version 0.30.2. + """ + maly_dbu: float + r""" + Getter: + @brief Specifies the database unit which the reader uses and produces + See \maly_dbu= method for a description of this property. + + This method has been added in version 0.30.2. + Setter: + @brief Specifies the database unit which the reader uses and produces + The database unit is the final resolution of the produced layout. This physical resolution is usually defined by the layout system - GDS for example typically uses 1nm (maly_dbu=0.001). + All geometry in the MALY pattern files is brought to the database unit by scaling. + + This method has been added in version 0.30.2. + """ + maly_layer_map: LayerMap + r""" + Getter: + @brief Gets the layer map + @return A reference to the layer map + + This method has been added in version 0.30.2. + Setter: + @brief Sets the layer map + This sets a layer mapping for the reader. Unlike \maly_set_layer_map, the 'create_other_layers' flag is not changed. + @param map The layer map to set. + + Layer maps can also be used to map the named MALY mask layers to GDS layer/datatypes. + + This method has been added in version 0.30.2. + """ mebes_boundary_datatype: int r""" Getter: @@ -44224,6 +44306,28 @@ class LoadLayoutOptions: This method has been added in version 0.26.2. """ ... + def maly_select_all_layers(self) -> None: + r""" + @brief Selects all layers and disables the layer map + + This disables any layer map and enables reading of all layers. + New layers will be created when required. + + This method has been added in version 0.30.2. + """ + ... + def maly_set_layer_map(self, map: LayerMap, create_other_layers: bool) -> None: + r""" + @brief Sets the layer map + This sets a layer mapping for the reader. The layer map allows selection and translation of the original layers, for example to assign layer/datatype numbers to the named layers. + @param map The layer map to set. + @param create_other_layers The flag indicating whether other layers will be created as well. Set to false to read only the layers in the layer map. + + Layer maps can also be used to map the named MALY mask layers to GDS layer/datatypes. + + This method has been added in version 0.30.2. + """ + ... def mebes_select_all_layers(self) -> None: r""" @brief Selects all layers and disables the layer map @@ -46439,15 +46543,15 @@ class NetPinRef: @overload def net(self) -> Net: r""" - @brief Gets the net this pin reference is attached to (non-const version). - - This constness variant has been introduced in version 0.26.8 + @brief Gets the net this pin reference is attached to. """ ... @overload def net(self) -> Net: r""" - @brief Gets the net this pin reference is attached to. + @brief Gets the net this pin reference is attached to (non-const version). + + This constness variant has been introduced in version 0.26.8 """ ... def pin(self) -> Pin: @@ -46737,17 +46841,17 @@ class NetTerminalRef: @overload def device(self) -> Device: r""" - @brief Gets the device reference. + @brief Gets the device reference (non-const version). Gets the device object that this connection is made to. + + This constness variant has been introduced in version 0.26.8 """ ... @overload def device(self) -> Device: r""" - @brief Gets the device reference (non-const version). + @brief Gets the device reference. Gets the device object that this connection is made to. - - This constness variant has been introduced in version 0.26.8 """ ... def device_class(self) -> DeviceClass: @@ -46770,15 +46874,15 @@ class NetTerminalRef: @overload def net(self) -> Net: r""" - @brief Gets the net this terminal reference is attached to. + @brief Gets the net this terminal reference is attached to (non-const version). + + This constness variant has been introduced in version 0.26.8 """ ... @overload def net(self) -> Net: r""" - @brief Gets the net this terminal reference is attached to (non-const version). - - This constness variant has been introduced in version 0.26.8 + @brief Gets the net this terminal reference is attached to. """ ... def terminal_def(self) -> DeviceTerminalDefinition: @@ -47757,6 +47861,13 @@ class Netlist: """ ... @overload + def circuit_by_name(self, name: str) -> Circuit: + r""" + @brief Gets the circuit object for a given name. + If the name is not a valid circuit name, nil is returned. + """ + ... + @overload def circuit_by_name(self, name: str) -> Circuit: r""" @brief Gets the circuit object for a given name (const version). @@ -47766,28 +47877,24 @@ class Netlist: """ ... @overload - def circuit_by_name(self, name: str) -> Circuit: - r""" - @brief Gets the circuit object for a given name. - If the name is not a valid circuit name, nil is returned. - """ - ... - @overload - def circuits_by_name(self, name_pattern: str) -> List[Circuit]: - r""" - @brief Gets the circuit objects for a given name filter. - The name filter is a glob pattern. This method will return all \Circuit objects matching the glob pattern. - - This method has been introduced in version 0.26.4. - """ - ... - @overload - def circuits_by_name(self, name_pattern: str) -> List[Circuit]: + def circuits_by_name(self, name_pattern: str, case_sensitive: Optional[Any] = ...) -> List[Circuit]: r""" @brief Gets the circuit objects for a given name filter (const version). The name filter is a glob pattern. This method will return all \Circuit objects matching the glob pattern. + The 'case_sensitive' argument will control whether the name is looked up in a case sensitive way or not. Note that with case insensitive search on a netlist that is case sensitive, the same name may render more than one hit. By default, case sensitivity is taken from the netlist. - This constness variant has been introduced in version 0.26.8. + This constness variant has been introduced in version 0.26.8.The 'case_sensitive' argument has been added in version 0.30.2. + """ + ... + @overload + def circuits_by_name(self, name_pattern: str, case_sensitive: Optional[Any] = ...) -> List[Circuit]: + r""" + @brief Gets the circuit objects for a given name filter. + The name filter is a glob pattern. This method will return all \Circuit objects matching the glob pattern. + The 'case_sensitive' argument will control whether the name is looked up in a case sensitive way or not. Note that with case insensitive search on a netlist that is case sensitive, the same name may render more than one hit. By default, case sensitivity is taken from the netlist. + + This method has been introduced in version 0.26.4. + The 'case_sensitive' argument has been added in version 0.30.2. """ ... def combine_devices(self) -> None: @@ -47952,22 +48059,24 @@ class Netlist: """ ... @overload - def nets_by_name(self, name_pattern: str) -> List[Net]: + def nets_by_name(self, name_pattern: str, case_sensitive: Optional[Any] = ...) -> List[Net]: r""" @brief Gets the net objects for a given name filter. The name filter is a glob pattern. This method will return all \Net objects matching the glob pattern. + The 'case_sensitive' argument will control whether the name is looked up in a case sensitive way or not. Note that with case insensitive search on a netlist that is case sensitive, the same name may render more than one hit. By default, case sensitivity is taken from the netlist. This method has been introduced in version 0.28.4. + The 'case_sensitive' argument has been added in version 0.30.2. """ ... @overload - def nets_by_name(self, name_pattern: str) -> List[Net]: + def nets_by_name(self, name_pattern: str, case_sensitive: Optional[Any] = ...) -> List[Net]: r""" @brief Gets the net objects for a given name filter (const version). The name filter is a glob pattern. This method will return all \Net objects matching the glob pattern. + The 'case_sensitive' argument will control whether the name is looked up in a case sensitive way or not. Note that with case insensitive search on a netlist that is case sensitive, the same name may render more than one hit. By default, case sensitivity is taken from the netlist. - - This constness variant has been introduced in version 0.28.4. + This constness variant has been introduced in version 0.28.4.The 'case_sensitive' argument has been added in version 0.30.2. """ ... def purge(self) -> None: @@ -48032,7 +48141,7 @@ class Netlist: @overload def top_circuit(self) -> Circuit: r""" - @brief Gets the top circuit (const version). + @brief Gets the top circuit. This method will return nil, if there is no top circuit. It will raise an error, if there is more than a single top circuit. This convenience method has been added in version 0.29.5. @@ -48041,7 +48150,7 @@ class Netlist: @overload def top_circuit(self) -> Circuit: r""" - @brief Gets the top circuit. + @brief Gets the top circuit (const version). This method will return nil, if there is no top circuit. It will raise an error, if there is more than a single top circuit. This convenience method has been added in version 0.29.5. @@ -48927,42 +49036,105 @@ class NetlistCrossReference(NetlistCompareLogger): See the class description for details. """ ... + @overload + def each_device_pair(self, circuit: Circuit) -> Iterator[NetlistCrossReference.DevicePairData]: + r""" + @brief Delivers the device pairs and their status for the given circuit pair. + This convenience method looks up the circuit pair from the given circuit. This circuit can be a schematic or layout circuit. + This method has been added in version 0.30.2. + """ + ... + @overload def each_device_pair(self, circuit_pair: NetlistCrossReference.CircuitPairData) -> Iterator[NetlistCrossReference.DevicePairData]: r""" @brief Delivers the device pairs and their status for the given circuit pair. See the class description for details. """ ... + @overload + def each_net_pair(self, circuit: Circuit) -> Iterator[NetlistCrossReference.NetPairData]: + r""" + @brief Delivers the net pairs and their status for the given circuit. + This convenience method looks up the circuit pair from the given circuit. This circuit can be a schematic or layout circuit. + This method has been added in version 0.30.2. + """ + ... + @overload def each_net_pair(self, circuit_pair: NetlistCrossReference.CircuitPairData) -> Iterator[NetlistCrossReference.NetPairData]: r""" @brief Delivers the net pairs and their status for the given circuit pair. See the class description for details. """ ... + @overload + def each_net_pin_pair(self, net: Net) -> Iterator[NetlistCrossReference.NetPinRefPair]: + r""" + @brief Delivers the pin pairs for the given net pair. + This convenience method looks up the net pair from the given net. This net can be a schematic or layout net. + This method has been added in version 0.30.2. + """ + ... + @overload def each_net_pin_pair(self, net_pair: NetlistCrossReference.NetPairData) -> Iterator[NetlistCrossReference.NetPinRefPair]: r""" @brief Delivers the pin pairs for the given net pair. For the net pair, lists the pin pairs identified on this net. """ ... + @overload + def each_net_subcircuit_pin_pair(self, net: Net) -> Iterator[NetlistCrossReference.NetSubcircuitPinRefPair]: + r""" + @brief Delivers the subcircuit pin pairs for the given net pair. + This convenience method looks up the net pair from the given net. This net can be a schematic or layout net. + This method has been added in version 0.30.2. + """ + ... + @overload def each_net_subcircuit_pin_pair(self, net_pair: NetlistCrossReference.NetPairData) -> Iterator[NetlistCrossReference.NetSubcircuitPinRefPair]: r""" @brief Delivers the subcircuit pin pairs for the given net pair. For the net pair, lists the subcircuit pin pairs identified on this net. """ ... + @overload + def each_net_terminal_pair(self, net: Net) -> Iterator[NetlistCrossReference.NetTerminalRefPair]: + r""" + @brief Delivers the device terminal pairs for the given net pair. + This convenience method looks up the net pair from the given net. This net can be a schematic or layout net. + This method has been added in version 0.30.2. + """ + ... + @overload def each_net_terminal_pair(self, net_pair: NetlistCrossReference.NetPairData) -> Iterator[NetlistCrossReference.NetTerminalRefPair]: r""" @brief Delivers the device terminal pairs for the given net pair. For the net pair, lists the device terminal pairs identified on this net. """ ... + @overload + def each_pin_pair(self, circuit: Circuit) -> Iterator[NetlistCrossReference.PinPairData]: + r""" + @brief Delivers the pin pairs and their status for the given circuit pair. + This convenience method looks up the circuit pair from the given circuit. This circuit can be a schematic or layout circuit. + This method has been added in version 0.30.2. + """ + ... + @overload def each_pin_pair(self, circuit_pair: NetlistCrossReference.CircuitPairData) -> Iterator[NetlistCrossReference.PinPairData]: r""" @brief Delivers the pin pairs and their status for the given circuit pair. See the class description for details. """ ... + @overload + def each_subcircuit_pair(self, circuit: Circuit) -> Iterator[NetlistCrossReference.SubCircuitPairData]: + r""" + @brief Delivers the subcircuit pairs and their status for the given circuit pair. + This convenience method looks up the circuit pair from the given circuit. This circuit can be a schematic or layout circuit. + This method has been added in version 0.30.2. + """ + ... + @overload def each_subcircuit_pair(self, circuit_pair: NetlistCrossReference.CircuitPairData) -> Iterator[NetlistCrossReference.SubCircuitPairData]: r""" @brief Delivers the subcircuit pairs and their status for the given circuit pair. @@ -54649,6 +54821,19 @@ class PolygonNeighborhoodVisitor(PolygonNeighborhoodVisitorBase): @brief Configures the result type Use this method to indicate what type of result you want to deliver. You can use the corresponding 'output' method then to deliver result shapes from one the callbacks (\on_edge, \begin_polygon, \end_polygon). Set this attribute when you create the visitor object. This attribute does not need to be set if no output is indended to be delivered. """ + variant_type: VariantType + r""" + Getter: + @brief Gets the variant type + See \variant_type= for a description of this property. + + This property was introduced in version 0.30.2. + Setter: + @brief Configures the variant type + The variant type configures transformation variant formation. The polygons presented to the visitor are normalized to the given variant type. For example, specify \VariantType#Orientation to force orientation variants in the cell tree. Polygons presented to the visitor are normalized to 'as if top' orientation with this variant type. + + This property was introduced in version 0.30.2. + """ def _const_cast(self) -> PolygonNeighborhoodVisitor: r""" @brief Returns a non-const reference to self. @@ -63057,12 +63242,11 @@ class Shape: This method has been introduced in version 0.23. Setter: - @brief Sets the lower left corner of the box with the point being given in micrometer units + @brief Sets the lower left point of the box Applies to boxes only. Changes the lower left point of the box and throws an exception if the shape is not a box. - Translation from micrometer units to database units is done internally. - This method has been introduced in version 0.25. + This method has been introduced in version 0.23. """ box_p2: Point r""" @@ -63232,10 +63416,11 @@ class Shape: Starting with version 0.23, this method returns nil, if the shape does not represent an edge. Setter: - @brief Replaces the shape by the given edge (in micrometer units) - This method replaces the shape by the given edge, like \edge= with a \Edge argument does. This version translates the edge from micrometer units to database units internally. + @brief Replaces the shape by the given edge + This method replaces the shape by the given edge. This method can only be called for editable layouts. It does not change the user properties of the shape. + Calling this method will invalidate any iterators. It should not be called inside a loop iterating over shapes. - This method has been introduced in version 0.25. + This method has been introduced in version 0.22. """ edge_pair: Any r""" @@ -63452,11 +63637,10 @@ class Shape: Starting with version 0.23, this method returns nil, if the shape does not represent a text. Setter: - @brief Replaces the shape by the given text object - This method replaces the shape by the given text object. This method can only be called for editable layouts. It does not change the user properties of the shape. - Calling this method will invalidate any iterators. It should not be called inside a loop iterating over shapes. + @brief Replaces the shape by the given text (in micrometer units) + This method replaces the shape by the given text, like \text= with a \Text argument does. This version translates the text from micrometer units to database units internally. - This method has been introduced in version 0.22. + This method has been introduced in version 0.25. """ text_dpos: DVector r""" @@ -67636,15 +67820,6 @@ class SubCircuit(NetlistObject): """ ... @overload - def circuit(self) -> Circuit: - r""" - @brief Gets the circuit the subcircuit lives in (non-const version). - This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \circuit_ref. - - This constness variant has been introduced in version 0.26.8 - """ - ... - @overload def circuit(self) -> Circuit: r""" @brief Gets the circuit the subcircuit lives in. @@ -67652,9 +67827,12 @@ class SubCircuit(NetlistObject): """ ... @overload - def circuit_ref(self) -> Circuit: + def circuit(self) -> Circuit: r""" - @brief Gets the circuit referenced by the subcircuit. + @brief Gets the circuit the subcircuit lives in (non-const version). + This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \circuit_ref. + + This constness variant has been introduced in version 0.26.8 """ ... @overload @@ -67667,6 +67845,12 @@ class SubCircuit(NetlistObject): """ ... @overload + def circuit_ref(self) -> Circuit: + r""" + @brief Gets the circuit referenced by the subcircuit. + """ + ... + @overload def connect_pin(self, pin: Pin, net: Net) -> None: r""" @brief Connects the given pin to the specified net. @@ -68369,8 +68553,7 @@ class Text: Setter: @brief Sets the vertical alignment - This property specifies how the text is aligned relative to the anchor point. - This property has been introduced in version 0.22 and extended to enums in 0.28. + This is the version accepting integer values. It's provided for backward compatibility. """ x: int r""" @@ -71737,13 +71920,13 @@ class Trans: Getter: @brief Gets to the displacement vector - Staring with version 0.25 the displacement type is a vector. + Starting with version 0.25 the displacement type is a vector. Setter: @brief Sets the displacement @param u The new displacement This method was introduced in version 0.20. - Staring with version 0.25 the displacement type is a vector. + Starting with version 0.25 the displacement type is a vector. """ mirror: bool r""" @@ -74758,6 +74941,251 @@ class VCplxTrans: ... ... +class VariantType: + r""" + @brief This class represents the cell variant type for various methods. + + Cell variants are needed in hierarchical applications, when operations are to be performed on cell level, but the operations are not transformation invariant. + In that case, a variant type needs to be specified in order to make the algorithm separate the cells by their absolute orientation or by their accumulated magnification. + + This enum has been introduced in version 0.30.2. + """ + Magnification: ClassVar[VariantType] + r""" + @brief Scaling variants needed. + For example, distance measurements or the isotropic sizing operations needs this variant type. + """ + MagnificationAndOrientation: ClassVar[VariantType] + r""" + @brief Scaling and orientation variants needed. + For example, the 'move' operation needs this variant type. + """ + NoVariants: ClassVar[VariantType] + r""" + @brief No variants needed. + """ + Orientation: ClassVar[VariantType] + r""" + @brief Orientation variants needed. + For example, the edge orientation selection operation needs this variant type. + """ + Orthogonal: ClassVar[VariantType] + r""" + @brief Orthogonal transformations (rotations by multiples of 90 degree) need variants. + For example, the diagonal edge selection operation needs this variant type. + """ + XYAnisotropyAndMagnification: ClassVar[VariantType] + r""" + @brief Scaling and anisotropy variants needed. + For example, the anisotropic sizing operation needs this variant type. + """ + @overload + @classmethod + def new(cls, i: int) -> VariantType: + r""" + @brief Creates an enum from an integer value + """ + ... + @overload + @classmethod + def new(cls, s: str) -> VariantType: + r""" + @brief Creates an enum from a string value + """ + ... + def __copy__(self) -> VariantType: + r""" + @brief Creates a copy of self + """ + ... + def __deepcopy__(self) -> VariantType: + r""" + @brief Creates a copy of self + """ + ... + @overload + def __eq__(self, other: int) -> bool: + r""" + @brief Compares an enum with an integer value + """ + ... + @overload + def __eq__(self, other: object) -> bool: + r""" + @brief Compares two enums + """ + ... + def __hash__(self) -> int: + r""" + @brief Gets the hash value from the enum + """ + ... + @overload + def __init__(self, i: int) -> None: + r""" + @brief Creates an enum from an integer value + """ + ... + @overload + def __init__(self, s: str) -> None: + r""" + @brief Creates an enum from a string value + """ + ... + def __int__(self) -> int: + r""" + @brief Gets the integer value from the enum + """ + ... + @overload + def __lt__(self, other: VariantType) -> bool: + r""" + @brief Returns true if the first enum is less (in the enum symbol order) than the second + """ + ... + @overload + def __lt__(self, other: int) -> bool: + r""" + @brief Returns true if the enum is less (in the enum symbol order) than the integer value + """ + ... + @overload + def __ne__(self, other: int) -> bool: + r""" + @brief Compares an enum with an integer for inequality + """ + ... + @overload + def __ne__(self, other: object) -> bool: + r""" + @brief Compares two enums for inequality + """ + ... + def __repr__(self) -> str: + r""" + @brief Converts an enum to a visual string + """ + ... + def __str__(self) -> str: + r""" + @brief Gets the symbolic string from an enum + """ + ... + def _const_cast(self) -> VariantType: + r""" + @brief Returns a non-const reference to self. + Basically, this method allows turning a const object reference to a non-const one. This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects. + + This method has been introduced in version 0.29.6. + """ + ... + def _create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def _destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def _destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def _is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def _manage(self) -> None: + r""" + @brief Marks the object as managed by the script side. + After calling this method on an object, the script side will be responsible for the management of the object. This method may be called if an object is returned from a C++ function and the object is known not to be owned by any C++ instance. If necessary, the script side may delete the object if the script's reference is no longer required. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def _to_const_object(self) -> VariantType: + r""" + @hide + """ + ... + def _unmanage(self) -> None: + r""" + @brief Marks the object as no longer owned by the script side. + Calling this method will make this object no longer owned by the script's memory management. Instead, the object must be managed in some other way. Usually this method may be called if it is known that some C++ object holds and manages this object. Technically speaking, this method will turn the script's reference into a weak reference. After the script engine decides to delete the reference, the object itself will still exist. If the object is not managed otherwise, memory leaks will occur. + + Usually it's not required to call this method. It has been introduced in version 0.24. + """ + ... + def assign(self, other: VariantType) -> None: + r""" + @brief Assigns another object to self + """ + ... + def create(self) -> None: + r""" + @brief Ensures the C++ object is created + Use this method to ensure the C++ object is created, for example to ensure that resources are allocated. Usually C++ objects are created on demand and not necessarily when the script object is created. + """ + ... + def destroy(self) -> None: + r""" + @brief Explicitly destroys the object + Explicitly destroys the object on C++ side if it was owned by the script interpreter. Subsequent access to this object will throw an exception. + If the object is not owned by the script, this method will do nothing. + """ + ... + def destroyed(self) -> bool: + r""" + @brief Returns a value indicating whether the object was already destroyed + This method returns true, if the object was destroyed, either explicitly or by the C++ side. + The latter may happen, if the object is owned by a C++ object which got destroyed itself. + """ + ... + def dup(self) -> VariantType: + r""" + @brief Creates a copy of self + """ + ... + def hash(self) -> int: + r""" + @brief Gets the hash value from the enum + """ + ... + def inspect(self) -> str: + r""" + @brief Converts an enum to a visual string + """ + ... + def is_const_object(self) -> bool: + r""" + @brief Returns a value indicating whether the reference is a const reference + This method returns true, if self is a const reference. + In that case, only const methods may be called on self. + """ + ... + def to_i(self) -> int: + r""" + @brief Gets the integer value from the enum + """ + ... + def to_s(self) -> str: + r""" + @brief Gets the symbolic string from an enum + """ + ... + ... + class Vector: r""" @brief A integer vector class diff --git a/src/pymod/distutils_src/klayout/rdbcore.pyi b/src/pymod/distutils_src/klayout/rdbcore.pyi index 73e6d9688..52ce8550e 100644 --- a/src/pymod/distutils_src/klayout/rdbcore.pyi +++ b/src/pymod/distutils_src/klayout/rdbcore.pyi @@ -1270,17 +1270,17 @@ class RdbReference: @overload def database(self) -> ReportDatabase: r""" - @brief Gets the database object that category is associated with (non-const version) + @brief Gets the database object that category is associated with - This method has been introduced in version 0.29. + This method has been introduced in version 0.23. """ ... @overload def database(self) -> ReportDatabase: r""" - @brief Gets the database object that category is associated with + @brief Gets the database object that category is associated with (non-const version) - This method has been introduced in version 0.23. + This method has been introduced in version 0.29. """ ... def destroy(self) -> None: From 20097b5d8d8f75019c59401d837927c4f998df0d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 29 May 2025 11:57:34 +0200 Subject: [PATCH 264/392] Fixed golden test data --- testdata/lvs/flag_missing_ports.lvsdb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testdata/lvs/flag_missing_ports.lvsdb b/testdata/lvs/flag_missing_ports.lvsdb index 34ba019af..10baa7f3a 100644 --- a/testdata/lvs/flag_missing_ports.lvsdb +++ b/testdata/lvs/flag_missing_ports.lvsdb @@ -170,7 +170,7 @@ layout( rect(l11 (-150 -150) (300 300)) ) net(7 name(SUBSTRATE)) - net(8 + net(8 name($I3) rect(l6 (975 1660) (425 950)) rect(l6 (-400 -950) (425 950)) ) From 98834d8e3b785de1386c276cb965344551a41269 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 29 May 2025 12:00:44 +0200 Subject: [PATCH 265/392] Reference new golden testdata for LEFDEF/strm2oas test --- src/buddies/unit_tests/bdConverterTests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/buddies/unit_tests/bdConverterTests.cc b/src/buddies/unit_tests/bdConverterTests.cc index 4dfd7d20e..03ebc7155 100644 --- a/src/buddies/unit_tests/bdConverterTests.cc +++ b/src/buddies/unit_tests/bdConverterTests.cc @@ -199,7 +199,7 @@ TEST(10) std::string def_dir = input_dir + "/def"; std::string gds_dir = input_dir + "/gds"; - std::string input_au = input_dir + "/strm2oas_au.oas"; + std::string input_au = input_dir + "/strm2oas_au_2.oas"; std::string output = this->tmp_file ("strm2oas.oas"); std::string map_arg = "--lefdef-map=" + input_dir + "/sky130.map"; From 769b7e9170577fdb234c87ba37d08d38773f30cf Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 29 May 2025 13:31:36 +0200 Subject: [PATCH 266/392] Typo fixed --- src/pex/pex/gsiDeclRNetExtractor.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pex/pex/gsiDeclRNetExtractor.cc b/src/pex/pex/gsiDeclRNetExtractor.cc index 1b9b02f03..4bbd7ce04 100644 --- a/src/pex/pex/gsiDeclRNetExtractor.cc +++ b/src/pex/pex/gsiDeclRNetExtractor.cc @@ -133,7 +133,7 @@ Class decl_RExtractorTechVia ("pex", "RExtractorTechVia" ), "@brief Describes a via for the network extraction.\n" "This class is used to describe a via type in the context of " - "the \\RExtractorTech#extract method.\n" + "the \\RExtractorTech class.\n" "\n" "This class has been introduced in version 0.30.2." ); @@ -239,7 +239,7 @@ Class decl_RExtractorTechConductor ("pex", "RExtra ), "@brief Describes a conductor layer for the network extraction.\n" "This class is used to describe a conductor layer in the context of " - "the \\RExtractorTech#extract method.\n" + "the \\RExtractorTech class.\n" "\n" "This class has been introduced in version 0.30.2." ); From 5173a2aad77f46a652117028723e99a8f1c4ec93 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 29 May 2025 16:32:28 +0200 Subject: [PATCH 267/392] Fixing Python module sources --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 3ef1c7d19..487f09b2b 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -12,6 +12,7 @@ recursive-include src/pymod *.cc *.h recursive-include src/rbastub *.cc *.h recursive-include src/rdb/rdb *.cc *.h recursive-include src/tl/tl *.cc *.h +recursive-include src/pex/pex *.cc *.h recursive-include src/version *.cc *.h recursive-include src/pymod *.pyi recursive-include src/plugins/*/db_plugin *.cc *.h From f2172d8e2af4e1a372514103467450cfc4c51d18 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 31 May 2025 17:33:44 +0200 Subject: [PATCH 268/392] Extension of the L2N/LVSDB format to support deleted terminals and parameters Previously, when deleting parameters or terminals from device definitions, these parameters or terminals re-appeared in the netlist browser, because they were generated from the template class (e.g. "A" and "B" from "RES"). An additional token is added that indicates whether to remove all entries before adding new ones. This feature is backward-compatible and LVSDB/L2N files have to be generated from new KLayout versions to make use of that feature. --- src/db/db/dbLayoutToNetlistFormatDefs.h | 9 ++++++--- src/db/db/dbLayoutToNetlistReader.cc | 10 ++++++++++ src/db/db/dbLayoutToNetlistWriter.cc | 26 +++++++++++++++++++++---- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/db/db/dbLayoutToNetlistFormatDefs.h b/src/db/db/dbLayoutToNetlistFormatDefs.h index 6c7f4f3fe..bea7dbd60 100644 --- a/src/db/db/dbLayoutToNetlistFormatDefs.h +++ b/src/db/db/dbLayoutToNetlistFormatDefs.h @@ -79,7 +79,10 @@ namespace db * circuit( [circuit-def]) - circuit (cell) [short key: X] * * [class]: - * class(