From 59d1aead598571a041d39982bb0b0e465b99bf64 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 2 Dec 2018 01:30:56 +0100 Subject: [PATCH] WIP: new classes for hier network processor. --- src/db/db/dbBoxScanner.h | 44 +++- src/db/db/dbHierNetworkProcessor.cc | 201 +++++++++++++++--- src/db/db/dbHierNetworkProcessor.h | 100 ++++++++- src/db/unit_tests/dbBoxScanner.cc | 65 +++++- .../unit_tests/dbHierNetworkProcessorTests.cc | 28 ++- 5 files changed, 393 insertions(+), 45 deletions(-) diff --git a/src/db/db/dbBoxScanner.h b/src/db/db/dbBoxScanner.h index 4d6ee0f70..5c9372fe8 100644 --- a/src/db/db/dbBoxScanner.h +++ b/src/db/db/dbBoxScanner.h @@ -129,6 +129,14 @@ struct box_scanner_receiver * definition. */ void add (const Obj * /*o1*/, const Prop & /*p1*/, const Obj * /*o2*/, const Prop & /*p2*/) { } + + /** + * @brief Indicates whether the scanner may stop + * + * The scanner will stop if this method returns true. This feature can be used to + * terminate the scan process early if the outcome is known. + */ + bool stop () const { return false; } }; /** @@ -250,9 +258,13 @@ public: * * The box converter must be capable of converting the Obj object into a box. * It must provide a box_type typedef. + * + * The scanner process can be terminated early by making the receiver's + * stop() method return true. In this case, this method will return false. + * Otherwise it will return true. */ template - void process (Rec &rec, typename BoxConvert::box_type::coord_type enl, const BoxConvert &bc = BoxConvert ()) + bool process (Rec &rec, typename BoxConvert::box_type::coord_type enl, const BoxConvert &bc = BoxConvert ()) { typedef typename BoxConvert::box_type box_type; typedef typename box_type::coord_type coord_type; @@ -269,6 +281,9 @@ public: for (iterator_type j = i + 1; j != m_pp.end (); ++j) { if (bs_boxes_overlap (bc (*i->first), bc (*j->first), enl)) { rec.add (i->first, i->second, j->first, j->second); + if (rec.stop ()) { + return false; + } } } } @@ -355,6 +370,9 @@ public: if (seen.insert (std::make_pair (i->first, j->first)).second) { seen.insert (std::make_pair (j->first, i->first)); rec.add (i->first, i->second, j->first, j->second); + if (rec.stop ()) { + return false; + } } } } @@ -379,6 +397,8 @@ public: } + return true; + } private: @@ -421,6 +441,14 @@ struct box_scanner_receiver2 * definition. */ void add (const Obj1 * /*o1*/, const Prop1 & /*p1*/, const Obj2 * /*o2*/, const Prop2 & /*p2*/) { } + + /** + * @brief Indicates whether the scanner may stop + * + * The scanner will stop if this method returns true. This feature can be used to + * terminate the scan process early if the outcome is known. + */ + bool stop () const { return false; } }; /** @@ -558,9 +586,13 @@ public: * The box converter 1 must be capable of converting the Obj1 object into a box. * It must provide a box_type typedef. The box converter 2 must be able to convert Obj2 to * a box. The box type of both box converters must be identical. + * + * The scanner process can be terminated early by making the receiver's + * stop() method return true. In this case, this method will return false. + * Otherwise it will return true. */ template - void process (Rec &rec, typename BoxConvert1::box_type::coord_type enl, const BoxConvert1 &bc1 = BoxConvert1 (), const BoxConvert2 &bc2 = BoxConvert2 ()) + bool process (Rec &rec, typename BoxConvert1::box_type::coord_type enl, const BoxConvert1 &bc1 = BoxConvert1 (), const BoxConvert2 &bc2 = BoxConvert2 ()) { typedef typename BoxConvert1::box_type box_type; // must be same as BoxConvert2::box_type typedef typename box_type::coord_type coord_type; @@ -592,6 +624,9 @@ public: for (iterator_type2 j = m_pp2.begin (); j != m_pp2.end (); ++j) { if (bs_boxes_overlap (bc1 (*i->first), bc2 (*j->first), enl)) { rec.add (i->first, i->second, j->first, j->second); + if (rec.stop ()) { + return false; + } } } } @@ -716,6 +751,9 @@ public: if (seen1.insert (std::make_pair (i->first, j->first)).second) { seen2.insert (std::make_pair (j->first, i->first)); rec.add (i->first, i->second, j->first, j->second); + if (rec.stop ()) { + return false; + } } } } @@ -747,6 +785,8 @@ public: } + return true; + } private: diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 414d248a5..bd48e5800 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -27,7 +27,7 @@ #include "dbInstElement.h" #include "dbPolygon.h" #include "dbPolygonTools.h" -#include "dbBoxConvert.h" +#include "dbBoxScanner.h" #include #include @@ -95,62 +95,193 @@ Connectivity::end_connected (unsigned int layer) } } +template static bool -interaction_test (const db::PolygonRef &a, const db::PolygonRef &b) +interaction_test (const db::PolygonRef &a, const db::PolygonRef &b, const Trans &trans) { // TODO: this could be part of db::interact (including transformation) if (a.obj ().is_box () && b.obj ().is_box ()) { - return db::interact (a.obj ().box ().transformed (b.trans ().inverted () * a.trans ()), b.obj ().box ()); + return db::interact (a.obj ().box ().transformed (b.trans ().inverted () * a.trans ()), b.obj ().box ().transformed (trans)); } else { - return db::interact (a.obj ().transformed (b.trans ().inverted () * a.trans ()), b.obj ()); + return db::interact (a.obj ().transformed (b.trans ().inverted () * a.trans ()), b.obj ().transformed (trans)); } } -template -bool Connectivity::interacts (const T &a, unsigned int la, T &b, unsigned int lb) const +template +bool Connectivity::interacts (const T &a, unsigned int la, const T &b, unsigned int lb, const Trans &trans) const { std::map::const_iterator i = m_connected.find (la); if (i == m_connected.end () || i->second.find (lb) == i->second.end ()) { return false; } else { - return interaction_test (a, b); + return interaction_test (a, b, trans); } } // explicit instantiations -template DB_PUBLIC bool Connectivity::interacts (const db::PolygonRef &a, unsigned int la, db::PolygonRef &b, unsigned int lb) const; +template DB_PUBLIC bool Connectivity::interacts (const db::PolygonRef &a, unsigned int la, const db::PolygonRef &b, unsigned int lb, const db::UnitTrans &trans) const; +template DB_PUBLIC bool Connectivity::interacts (const db::PolygonRef &a, unsigned int la, const db::PolygonRef &b, unsigned int lb, const db::ICplxTrans &trans) const; // ------------------------------------------------------------------------------ -// ... +// local_cluster implementation + +template +local_cluster::local_cluster () + : m_id (0), m_needs_update (false) +{ + // .. nothing yet .. +} + +template +void +local_cluster::add (const T &s, unsigned int la) +{ + m_shapes[la].insert (s); + m_needs_update = true; +} + +template +void +local_cluster::join_with (const local_cluster &other) +{ + for (typename std::map::const_iterator s = other.m_shapes.begin (); s != other.m_shapes.end (); ++s) { + tree_type &other_tree = m_shapes[s->first]; + other_tree.insert (s->second.begin (), s->second.end ()); + } + + m_needs_update = true; +} + +template +void +local_cluster::ensure_sorted () +{ + if (! m_needs_update) { + return; + } + + // sort the shape trees + for (typename std::map::iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) { + s->second.sort (db::box_convert ()); + } + + // recompute bounding box + m_bbox = box_type (); + db::box_convert bc; + for (typename std::map::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) { + for (typename tree_type::const_iterator i = s->second.begin (); i != s->second.end (); ++i) { + m_bbox += bc (*i); + } + } + + m_needs_update = false; +} + +namespace +{ + +template +struct interaction_receiver + : public box_scanner_receiver2 +{ +public: + typedef typename local_cluster::box_type box_type; + + interaction_receiver (const Connectivity &conn, const db::ICplxTrans &trans) + : mp_conn (&conn), m_any (false), m_trans (trans) + { + // .. nothing yet .. + } + + void add (const T *s1, unsigned int l1, const T *s2, unsigned int l2) + { + if (mp_conn->interacts (*s1, l1, *s2, l2, m_trans)) { + m_any = true; + } + } + + bool stop () const + { + return m_any; + } + +private: + const Connectivity *mp_conn; + bool m_any; + db::ICplxTrans m_trans; +}; + +template +struct transformed_box +{ + typedef db::box_convert base_bc; + typedef typename T::box_type box_type; + + transformed_box (const Trans &trans) + : m_trans (trans) + { + // .. nothing yet .. + } + + box_type operator() (const T &t) const + { + return m_bc (t).transformed (m_trans); + } + +private: + base_bc m_bc; + Trans m_trans; +}; + +} + +template +bool +local_cluster::interacts (const local_cluster &other, const db::ICplxTrans &trans, const Connectivity &conn) const +{ + const_cast *> (this)->ensure_sorted (); + + if (! other.bbox ().overlaps (bbox ())) { + return false; + } + + box_type common = other.bbox () & bbox (); + + db::box_scanner2 scanner; + transformed_box bc_t (trans); + db::box_convert bc; + + bool any = false; + for (typename std::map::const_iterator s = m_shapes.begin (); s != m_shapes.end (); ++s) { + for (typename tree_type::overlapping_iterator i = s->second.begin_overlapping (common, bc); ! i.at_end (); ++i) { + scanner.insert1 (i.operator-> (), s->first); + any = true; + } + } + + if (! any) { + return false; + } + + for (typename std::map::const_iterator s = other.m_shapes.begin (); s != other.m_shapes.end (); ++s) { + for (typename tree_type::overlapping_iterator i = s->second.begin_overlapping (common.transformed (trans.inverted ()), bc); ! i.at_end (); ++i) { + scanner.insert2 (i.operator-> (), s->first); + } + } + + interaction_receiver rec (conn, trans); + return ! scanner.process (rec, 0, bc, bc_t); +} + +// explicit instantiations +template class DB_PUBLIC local_cluster; + + +// ------------------------------------------------------------------------------ +// local_cluster implementation #if 0 -/** - * @brief Represents a cluster of shapes - * - * A cluster of shapes is a set of shapes which are connected in terms - * of a given connectivity. - */ -class LocalCluster -{ -public: - typedef size_t id_type; - - LocalCluster (); - - void connect (const db::Shape &a, unsigned int la, const db::Shape &b, unsigned int lb); - id_type id () const; - - // @@@ Trans is the transformation of the cluster to the shape (instance transformation) - void interacts (const db::Shape &s, unsigned int ls, const db::ICplxTrans &trans, const Connectivity &conn); - const db::Box &bbox () const; - -private: - friend class LocalClusters; - - void set_id (id_type id); -}; - /** * @brief Represents a collection of clusters in a cell * diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index de1605328..80e53a1cd 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -25,6 +25,9 @@ #define HDR_dbHierNetworkProcessor #include "dbCommon.h" +#include "dbTrans.h" +#include "dbBoxConvert.h" +#include "dbBoxTree.h" #include #include @@ -81,17 +84,112 @@ public: */ layer_iterator end_connected (unsigned int layer); + /** + * @brief Returns true, if the given shapes on the given layers interact + * + * This method accepts a transformation. This transformation is applied + * to the b shape before checking against a. + */ + template + bool interacts (const T &a, unsigned int la, const T &b, unsigned int lb, const Trans &trans) const; + /** * @brief Returns true, if the given shapes on the given layers interact */ template - bool interacts (const T &a, unsigned int la, T &b, unsigned int lb) const; + bool interacts (const T &a, unsigned int la, const T &b, unsigned int lb) const + { + return interacts (a, la, b, lb, UnitTrans ()); + } private: layers_type m_all_layers; std::map m_connected; }; +/** + * @brief Represents a cluster of shapes + * + * A cluster of shapes is a set of shapes of type T which are connected in terms + * of a given connectivity. The shapes will still be organised in layers. + */ +template +class DB_PUBLIC local_cluster +{ +public: + typedef size_t id_type; + typedef typename T::box_type box_type; + + /** + * @brief Creates an empty cluster + */ + local_cluster (); + + /** + * @brief Adds a shape with the given layer to the cluster + */ + void add (const T &s, unsigned int la); + + /** + * @brief Joins this cluster with the other one + * + * This will copy all shapes from the other cluster into ourself. + */ + void join_with (const local_cluster &other); + + /** + * @brief Gets the cluster's ID + * + * The ID is a unique identifier for the cluster. An ID value of 0 is reserved for + * "no cluster". + */ + id_type id () const + { + return m_id; + } + + /** + * @brief Tests whether this cluster interacts with another cluster + * + * "trans" is the transformation which is applied to the other cluster before + * the test. + */ + bool interacts (const local_cluster &other, const db::ICplxTrans &trans, const Connectivity &conn) const; + + /** + * @brief Gets the bounding box of this cluster + */ + const box_type &bbox () const + { + const_cast *> (this)->ensure_sorted (); // also updates bbox + return m_bbox; + } + +private: + template friend class local_clusters; + template friend class interaction_receiver; + typedef db::unstable_box_tree > tree_type; + + void set_id (id_type id) + { + m_id = id; + } + + const T &shape (unsigned int l, size_t index) const + { + typename std::map::const_iterator s = m_shapes.find (l); + tl_assert (s != m_shapes.end ()); + return s->second.objects () [index]; + } + + void ensure_sorted (); + + id_type m_id; + bool m_needs_update; + std::map m_shapes; + box_type m_bbox; +}; + } #endif diff --git a/src/db/unit_tests/dbBoxScanner.cc b/src/db/unit_tests/dbBoxScanner.cc index 211b5a4fa..5e3e1e591 100644 --- a/src/db/unit_tests/dbBoxScanner.cc +++ b/src/db/unit_tests/dbBoxScanner.cc @@ -35,6 +35,8 @@ struct BoxScannerTestRecorder str += "<" + tl::to_string (p) + ">"; } + bool stop () const { return false; } + void add (const db::Box * /*b1*/, size_t p1, const db::Box * /*b2*/, size_t p2) { str += "(" + tl::to_string (p1) + "-" + tl::to_string (p2) + ")"; @@ -43,10 +45,32 @@ struct BoxScannerTestRecorder std::string str; }; +struct BoxScannerTestRecorderStopping +{ + BoxScannerTestRecorderStopping () : do_stop (false) { } + + void finish (const db::Box * /*box*/, size_t p) { + str += "<" + tl::to_string (p) + ">"; + } + + bool stop () const { return do_stop; } + + void add (const db::Box * /*b1*/, size_t p1, const db::Box * /*b2*/, size_t p2) + { + str += "(" + tl::to_string (p1) + "-" + tl::to_string (p2) + ")"; + do_stop = true; + } + + std::string str; + bool do_stop; +}; + struct BoxScannerTestRecorder2 { void finish (const db::Box *, size_t) { } + bool stop () const { return false; } + void add (const db::Box * /*b1*/, size_t p1, const db::Box * /*b2*/, size_t p2) { interactions.insert (std::make_pair (p1, p2)); @@ -66,6 +90,8 @@ struct BoxScannerTestRecorderTwo str += "<" + tl::to_string (p) + ">"; } + bool stop () const { return false; } + void add (const db::Box * /*b1*/, size_t p1, const db::SimplePolygon * /*b2*/, int p2) { str += "(" + tl::to_string (p1) + "-" + tl::to_string (p2) + ")"; @@ -74,11 +100,37 @@ struct BoxScannerTestRecorderTwo std::string str; }; +struct BoxScannerTestRecorderTwoStopping +{ + BoxScannerTestRecorderTwoStopping () : do_stop (false) { } + + void finish1 (const db::Box * /*box*/, size_t p) { + str += "<" + tl::to_string (p) + ">"; + } + + void finish2 (const db::SimplePolygon * /*poly*/, int p) { + str += "<" + tl::to_string (p) + ">"; + } + + bool stop () const { return do_stop; } + + void add (const db::Box * /*b1*/, size_t p1, const db::SimplePolygon * /*b2*/, int p2) + { + str += "(" + tl::to_string (p1) + "-" + tl::to_string (p2) + ")"; + do_stop = true; + } + + std::string str; + bool do_stop; +}; + struct BoxScannerTestRecorder2Two { void finish1 (const db::Box *, size_t) { } void finish2 (const db::SimplePolygon *, int) { } + bool stop () const { return false; } + void add (const db::Box * /*b1*/, size_t p1, const db::SimplePolygon * /*b2*/, int p2) { interactions.insert (std::make_pair (p1, p2)); @@ -106,8 +158,12 @@ TEST(1) bs.set_fill_factor (0.0); db::box_convert bc; bs.set_scanner_threshold (0); - bs.process (tr, 1, bc); + EXPECT_EQ (bs.process (tr, 1, bc), true); EXPECT_EQ (tr.str, "(4-2)(5-2)(5-4)(3-2)(3-4)(5-3)<2><5><4><3>(1-0)<0><1>"); + + BoxScannerTestRecorderStopping trstop; + EXPECT_EQ (bs.process (trstop, 1, bc), false); + EXPECT_EQ (trstop.str, "(4-2)"); } TEST(1a) @@ -920,8 +976,13 @@ TEST(two_1b) db::box_convert bc1; db::box_convert bc2; bs.set_scanner_threshold (0); - bs.process (tr, 1, bc1, bc2); + EXPECT_EQ (bs.process (tr, 1, bc1, bc2), true); EXPECT_EQ (tr.str, "(1-12)(2-12)(1-11)(2-11)<1><2><11><12>(0-10)<0><10>"); + + + BoxScannerTestRecorderTwoStopping trstop; + EXPECT_EQ (bs.process (trstop, 1, bc1, bc2), false); + EXPECT_EQ (trstop.str, "(1-12)"); } void run_test2_two (tl::TestBase *_this, size_t n, double ff, db::Coord spread, bool touch = true) diff --git a/src/db/unit_tests/dbHierNetworkProcessorTests.cc b/src/db/unit_tests/dbHierNetworkProcessorTests.cc index c049cfd0b..23176b8fd 100644 --- a/src/db/unit_tests/dbHierNetworkProcessorTests.cc +++ b/src/db/unit_tests/dbHierNetworkProcessorTests.cc @@ -80,15 +80,23 @@ TEST(2_ShapeInteractions) tl::from_string ("(0,0;0,1000;1000,1000;1000,0)", poly); db::GenericRepository repo; db::PolygonRef ref1 (poly, repo); - db::PolygonRef ref2 (poly.transformed (db::Trans (db::Vector (0, 10))), repo); - db::PolygonRef ref3 (poly.transformed (db::Trans (db::Vector (0, 2000))), repo); + db::ICplxTrans t2 (db::Trans (db::Vector (0, 10))); + db::PolygonRef ref2 (poly.transformed (t2), repo); + db::ICplxTrans t3 (db::Trans (db::Vector (0, 2000))); + db::PolygonRef ref3 (poly.transformed (t3), repo); EXPECT_EQ (conn.interacts (ref1, 0, ref2, 0), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t2), true); // t2*ref1 == ref2 EXPECT_EQ (conn.interacts (ref1, 0, ref2, 1), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t2), true); EXPECT_EQ (conn.interacts (ref1, 1, ref2, 0), true); + EXPECT_EQ (conn.interacts (ref1, 1, ref1, 0, t2), true); EXPECT_EQ (conn.interacts (ref1, 0, ref3, 0), false); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t3), false); // t3*ref1 == ref3 EXPECT_EQ (conn.interacts (ref1, 0, ref3, 1), false); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t3), false); EXPECT_EQ (conn.interacts (ref1, 1, ref2, 2), false); + EXPECT_EQ (conn.interacts (ref1, 1, ref1, 2, t2), false); } TEST(2_ShapeInteractionsRealPolygon) @@ -103,15 +111,25 @@ TEST(2_ShapeInteractionsRealPolygon) tl::from_string ("(0,0;0,1000;500,1000;500,1500;1000,1500;1000,0)", poly); db::GenericRepository repo; db::PolygonRef ref1 (poly, repo); - db::PolygonRef ref2 (poly.transformed (db::Trans (db::Vector (0, 10))), repo); - db::PolygonRef ref3 (poly.transformed (db::Trans (db::Vector (0, 2000))), repo); - db::PolygonRef ref4 (poly.transformed (db::Trans (db::Vector (0, 1500))), repo); + db::ICplxTrans t2 (db::Trans (db::Vector (0, 10))); + db::PolygonRef ref2 (poly.transformed (t2), repo); + db::ICplxTrans t3 (db::Trans (db::Vector (0, 2000))); + db::PolygonRef ref3 (poly.transformed (t3), repo); + db::ICplxTrans t4 (db::Trans (db::Vector (0, 1500))); + db::PolygonRef ref4 (poly.transformed (t4), repo); EXPECT_EQ (conn.interacts (ref1, 0, ref2, 0), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t2), true); // t2*ref1 == ref2 EXPECT_EQ (conn.interacts (ref1, 0, ref2, 1), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t2), true); EXPECT_EQ (conn.interacts (ref1, 1, ref2, 0), true); + EXPECT_EQ (conn.interacts (ref1, 1, ref1, 0, t2), true); EXPECT_EQ (conn.interacts (ref1, 0, ref3, 0), false); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t3), false); EXPECT_EQ (conn.interacts (ref1, 0, ref4, 0), true); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 0, t4), true); EXPECT_EQ (conn.interacts (ref1, 0, ref3, 1), false); + EXPECT_EQ (conn.interacts (ref1, 0, ref1, 1, t3), false); EXPECT_EQ (conn.interacts (ref1, 1, ref2, 2), false); + EXPECT_EQ (conn.interacts (ref1, 1, ref1, 2, t2), false); }