diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index b41c243ac..9942a0c2c 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -383,7 +383,7 @@ private: } void -RecursiveShapeIterator::validate () const +RecursiveShapeIterator::validate (RecursiveShapeReceiver *receiver) const { if (! m_needs_reinit) { return; @@ -438,8 +438,8 @@ RecursiveShapeIterator::validate () const } else if (mp_layout && (! m_has_layers || m_current_layer < m_layers.size ())) { // Ensures the trees are built properly - this is important in MT contexts (i.e. TilingProcessor) mp_layout->update (); - new_cell (); - next_shape (); + new_cell (receiver); + next_shape (receiver); } } @@ -519,7 +519,7 @@ RecursiveShapeIterator::select_all_cells () bool RecursiveShapeIterator::at_end () const { - validate (); + validate (0); return m_shape.at_end () || is_inactive (); } @@ -611,7 +611,7 @@ RecursiveShapeIterator::skip_inst_iter_for_complex_region () const } void -RecursiveShapeIterator::next () +RecursiveShapeIterator::next (RecursiveShapeReceiver *receiver) { if (! at_end ()) { @@ -622,14 +622,14 @@ RecursiveShapeIterator::next () } if (! mp_shapes && m_shape.at_end ()) { - next_shape (); + next_shape (receiver); } } } void -RecursiveShapeIterator::next_shape () const +RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const { while (at_end ()) { @@ -674,10 +674,10 @@ RecursiveShapeIterator::next_shape () const if (is_empty) { ++m_inst; - new_inst (); + new_inst (receiver); } else { - down (); + down (receiver); } } else { @@ -688,12 +688,14 @@ RecursiveShapeIterator::next_shape () const } // no more instances: up and next instance - up (); + up (receiver); ++m_inst_array; + new_inst_member (receiver); + if (m_inst_array.at_end ()) { ++m_inst; - new_inst (); + new_inst (receiver); } } @@ -704,7 +706,7 @@ RecursiveShapeIterator::next_shape () const } void -RecursiveShapeIterator::down () const +RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const { m_trans_stack.push_back (m_trans); m_cells.push_back (mp_cell); @@ -761,12 +763,24 @@ RecursiveShapeIterator::down () const } - new_cell (); + if (receiver && ! receiver->enter_cell (this, cell (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ())) { + if (m_has_layers) { + m_current_layer = m_layers.size (); + } + m_shape = shape_iterator (); + m_inst = inst_iterator (); + } else { + new_cell (receiver); + } } void -RecursiveShapeIterator::up () const +RecursiveShapeIterator::up (RecursiveShapeReceiver *receiver) const { + if (receiver) { + receiver->leave_cell (this, cell ()); + } + m_shape = shape_iterator (); m_shape_quad_id = 0; @@ -823,7 +837,7 @@ RecursiveShapeIterator::new_layer () const } void -RecursiveShapeIterator::new_cell () const +RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const { if (m_has_layers) { m_current_layer = 0; @@ -847,11 +861,11 @@ RecursiveShapeIterator::new_cell () const skip_inst_iter_for_complex_region (); } - new_inst (); + new_inst (receiver); } void -RecursiveShapeIterator::new_inst () const +RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const { // look for the next instance with a non-empty array iterator. The latter can be // empty because we use a per-layer box converter for that case what we don't for the @@ -866,12 +880,21 @@ RecursiveShapeIterator::new_inst () const } } - if (m_local_region_stack.back () != box_type::world ()) { + bool all_of_instance = true; + if (m_local_region_stack.back () != box_type::world () && ! m_inst->cell_inst ().bbox (m_box_convert).inside (m_local_region_stack.back ())) { m_inst_array = m_inst->cell_inst ().begin_touching (m_local_region_stack.back (), m_box_convert); + all_of_instance = false; } else { - m_inst_array = m_inst->cell_inst ().begin (); + m_inst_array = m_inst->cell_inst ().begin (); + // TODO: optimization potential: only report all_of_instance == false, if not entirely within the complex region + all_of_instance = m_local_complex_region_stack.empty (); } + if (receiver) { + receiver->new_inst (this, m_inst->cell_inst (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back (), all_of_instance); + } + + new_inst_member (receiver); if (! m_inst_array.at_end ()) { break; } else { @@ -881,6 +904,28 @@ RecursiveShapeIterator::new_inst () const } } +void +RecursiveShapeIterator::new_inst_member (RecursiveShapeReceiver *receiver) const +{ + if (! m_local_complex_region_stack.empty ()) { + + // skip instance array members not part of the complex region + while (! m_inst_array.at_end ()) { + db::Box ia_box = m_inst->complex_trans (*m_inst_array) * m_box_convert (m_inst->cell_inst ().object ()); + if (! is_outside_complex_region (ia_box)) { + break; + } else { + ++m_inst_array; + } + } + + } + + if (! m_inst_array.at_end () && receiver) { + receiver->new_inst_member (this, m_inst->cell_inst (), m_inst->complex_trans (*m_inst_array), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ()); + } +} + bool RecursiveShapeIterator::is_outside_complex_region (const db::Box &box) const { @@ -891,5 +936,23 @@ RecursiveShapeIterator::is_outside_complex_region (const db::Box &box) const } } +void +RecursiveShapeIterator::push (RecursiveShapeReceiver *receiver) +{ + // force reset so we can validate with a receiver + reset (); + + receiver->begin (this); + + validate (receiver); + + while (! at_end ()) { + receiver->shape (this, *m_shape, m_trans); + next (receiver); + } + + receiver->end (this); +} + } diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index 970085b63..de4103630 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -38,6 +38,7 @@ namespace db { class Region; +class RecursiveShapeReceiver; /** * @brief An iterator delivering shapes that touch or overlap the given region recursively @@ -479,7 +480,7 @@ public: */ unsigned int layer () const { - validate (); + validate (0); return m_layer; } @@ -514,7 +515,7 @@ public: */ const cplx_trans_type &trans () const { - validate (); + validate (0); return m_trans; } @@ -525,7 +526,7 @@ public: */ unsigned int depth () const { - validate (); + validate (0); return (unsigned int) m_trans_stack.size (); } @@ -537,7 +538,7 @@ public: */ shape_type shape () const { - validate (); + validate (0); return *m_shape; } @@ -548,7 +549,7 @@ public: */ shape_type operator* () const { - validate (); + validate (0); return *m_shape; } @@ -559,7 +560,7 @@ public: */ const shape_type *operator-> () const { - validate (); + validate (0); return m_shape.operator-> (); } @@ -575,7 +576,7 @@ public: */ db::cell_index_type cell_index () const { - validate (); + validate (0); size_t c = reinterpret_cast (mp_cell); return reinterpret_cast (c - (c & size_t (1)))->cell_index (); } @@ -585,7 +586,7 @@ public: */ const cell_type *cell () const { - validate (); + validate (0); size_t c = reinterpret_cast (mp_cell); return reinterpret_cast (c - (c & size_t (1))); } @@ -595,14 +596,17 @@ public: */ RecursiveShapeIterator &operator++() { - next (); + next (0); return *this; } /** - * @brief Increment the iterator + * @brief Increments the iterator */ - void next (); + void next () + { + next (0); + } /** * @brief Comparison of iterators - equality @@ -638,6 +642,20 @@ public: */ std::vector path () const; + /** + * @brief Push-mode delivery + * + * This method will deliver all shapes to the given receiver. + * In contrast to pull mode, this method allows tailoring the + * traversal of the hierarchy tree during iteration. + * For this purpose, the receiver has methods that receive + * events and to some extend may modify the traversal (e.g. + * return value of enter_cell). + * + * See RecursiveShapeReceiver class for more details. + */ + void push (RecursiveShapeReceiver *receiver); + private: std::vector m_layers; bool m_has_layers; @@ -681,14 +699,16 @@ private: void init_region (const box_type ®ion); void skip_shape_iter_for_complex_region () const; void skip_inst_iter_for_complex_region () const; - void validate () const; + void validate (RecursiveShapeReceiver *receiver) const; void start_shapes () const; - void next_shape () const; - void new_inst () const; - void new_cell () const; + void next (RecursiveShapeReceiver *receiver); + void next_shape (RecursiveShapeReceiver *receiver) const; + void new_inst (RecursiveShapeReceiver *receiver) const; + void new_inst_member (RecursiveShapeReceiver *receiver) const; + void new_cell (RecursiveShapeReceiver *receiver) const; void new_layer () const; - void up () const; - void down () const; + void up (RecursiveShapeReceiver *receiver) const; + void down (RecursiveShapeReceiver *receiver) const; bool is_outside_complex_region (const db::Box &box) const; @@ -705,8 +725,99 @@ private: } }; +/** + * @brief A receiver interface for "push" mode + * + * In push mode, the iterator will deliver the shapes and hierarchy transitions + * to this interface. See "RecursiveShapeIterator::push" for details about this + * mode. + * + * The receiver receives events for the start of the delivery, on each cell + * entry and on each instance (followed by a cell entry). It also receives + * the shapes. + */ +class DB_PUBLIC RecursiveShapeReceiver +{ +public: + typedef RecursiveShapeIterator::box_tree_type box_tree_type; + + RecursiveShapeReceiver () { } + virtual ~RecursiveShapeReceiver () { } + + /** + * @brief Called once when the iterator begins pushing + */ + virtual void begin (const RecursiveShapeIterator * /*iter*/) { } + + /** + * @brief Called once after the iterator pushed everything + */ + virtual void end (const RecursiveShapeIterator * /*iter*/) { } + + /** + * @brief Enters a cell + * + * This method is called when the recursive shape iterator + * enters a new cell. This method can return false. In this + * case, the cell is skipped together with it's subcells. + * + * This method is not called for the top cell. When it is called, "iter->trans()" + * will already be updated. + * + * @param iter The iterator + * @param cell The cell which is entered + * @param region The clip box as seen from "cell" or db::Box::world if there is no clip box + * @param complex_region A complex clip region if one is supplied together with "region" + */ + virtual bool enter_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { return true; } + + /** + * @brief Leaves the current cell + * + * This method is the counterpart for "enter_cell". It is called when traversal of "cell" ended. + */ + virtual void leave_cell (const RecursiveShapeIterator * /*iter*/, const db::Cell * /*cell*/) { } + + /** + * @brief Enters a new instance + * + * This method is called before "enter_cell" and "new_inst_member" is called and will indicate the instance to follow. + * The sequence of events is + * + * new_inst(A) + * new_inst_member(A[0,0]) + * enter_cell(A) + * ... + * leave_cell(A) + * new_inst_member(A[1,0]) + * enter_cell(A) + * ... + * leave_cell(A) + * ... + * new_inst(B) + * ... + * + * The "all" parameter is true, if all instances of the array will be addressed. + */ + virtual void new_inst (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool /*all*/) { } + + /** + * @brief Enters a new array member of the instance + * + * See "new_inst" for a description. This method adds the "trans" parameter + * which holds the complex transformation for this particular instance of + * the array. + */ + virtual void new_inst_member (const RecursiveShapeIterator * /*iter*/, const db::CellInstArray & /*inst*/, const db::ICplxTrans & /*trans*/, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) { } + + /** + * @brief Delivers a shape + * + * @param trans The transformation which maps the shape to the top cell. + */ + virtual void shape (const RecursiveShapeIterator * /*iter*/, const db::Shape & /*shape*/, const db::ICplxTrans & /*trans*/) { } +}; + } // namespace db #endif - - diff --git a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc index 27b6964c1..566d02121 100644 --- a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc +++ b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc @@ -453,7 +453,7 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS gsi::method ("cell_index", &db::RecursiveShapeIterator::cell_index, "@brief Gets the current cell's index \n" ) + - gsi::method ("next", &db::RecursiveShapeIterator::next, + gsi::method ("next", (void (db::RecursiveShapeIterator::*) ()) &db::RecursiveShapeIterator::next, "@brief Increment the iterator\n" "This moves the iterator to the next shape inside the search scope." ) + diff --git a/src/db/unit_tests/dbRecursiveShapeIterator.cc b/src/db/unit_tests/dbRecursiveShapeIterator.cc index f2503e351..fff996a2c 100644 --- a/src/db/unit_tests/dbRecursiveShapeIterator.cc +++ b/src/db/unit_tests/dbRecursiveShapeIterator.cc @@ -709,6 +709,21 @@ static db::Layout boxes2layout (const std::set &boxes) return l; } +class FlatPusher + : public db::RecursiveShapeReceiver +{ +public: + FlatPusher (std::set *boxes) : mp_boxes (boxes) { } + + void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans &trans) + { + mp_boxes->insert (trans * shape.bbox ()); + } + +private: + std::set *mp_boxes; +}; + TEST(4) { // Big fun @@ -751,6 +766,16 @@ TEST(4) EXPECT_EQ (selected_boxes.size () > 100, true); EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + // push mode + { + selected_boxes.clear (); + FlatPusher pusher (&selected_boxes); + db::RecursiveShapeIterator (g, c0, 0, search_box, true).push (&pusher); + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + db::Box search_box2 (500, 500, 1000, 1000); selected_boxes.clear (); @@ -772,6 +797,16 @@ TEST(4) EXPECT_EQ (selected_boxes.size () > 100, true); EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + + // push mode + { + selected_boxes.clear (); + FlatPusher pusher (&selected_boxes); + db::RecursiveShapeIterator (g, c0, 0, reg, true).push (&pusher); + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); } TEST(5) @@ -819,6 +854,16 @@ TEST(5) EXPECT_EQ (selected_boxes.size () > 100, true); EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + // push mode + { + selected_boxes.clear (); + FlatPusher pusher (&selected_boxes); + db::RecursiveShapeIterator (g, c0, 0, search_box, true).push (&pusher); + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + db::Box search_box2 (500, 500, 1000, 1000); selected_boxes.clear (); @@ -840,4 +885,310 @@ TEST(5) EXPECT_EQ (selected_boxes.size () > 100, true); EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); + + // push mode + { + selected_boxes.clear (); + FlatPusher pusher (&selected_boxes); + db::RecursiveShapeIterator (g, c0, 0, reg, true).push (&pusher); + } + + EXPECT_EQ (selected_boxes.size () > 100, true); + EXPECT_EQ (db::compare_layouts (boxes2layout (selected_boxes), boxes2layout (selected_boxes2), db::layout_diff::f_verbose, 0, 100 /*max diff lines*/), true); +} + +class LoggingReceiver + : public db::RecursiveShapeReceiver +{ +public: + LoggingReceiver () { } + + const std::string &text () const { return m_text; } + + virtual void begin (const db::RecursiveShapeIterator * /*iter*/) { m_text += "begin\n"; } + virtual void end (const db::RecursiveShapeIterator * /*iter*/) { m_text += "end\n"; } + + virtual bool enter_cell (const db::RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + { + m_text += std::string ("enter_cell(") + iter->layout ()->cell_name (cell->cell_index ()) + ")\n"; + return true; + } + + virtual void leave_cell (const db::RecursiveShapeIterator *iter, const db::Cell *cell) + { + m_text += std::string ("leave_cell(") + iter->layout ()->cell_name (cell->cell_index ()) + ")\n"; + } + + virtual void new_inst (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all) + { + m_text += std::string ("new_inst(") + iter->layout ()->cell_name (inst.object ().cell_index ()); + if (all) { + m_text += ",all"; + } + m_text += ")\n"; + } + + virtual void new_inst_member (const db::RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::ICplxTrans &trans, const db::Box & /*region*/, const box_tree_type * /*complex_region*/) + { + m_text += std::string ("new_inst_member(") + iter->layout ()->cell_name (inst.object ().cell_index ()) + "," + tl::to_string (trans) + ")\n"; + } + + virtual void shape (const db::RecursiveShapeIterator * /*iter*/, const db::Shape &shape, const db::ICplxTrans &trans) + { + m_text += "shape(" + shape.to_string () + "," + tl::to_string (trans) + ")\n"; + } + +private: + std::string m_text; +}; + +class ReceiverRejectingACell + : public LoggingReceiver +{ +public: + ReceiverRejectingACell (db::cell_index_type rejected) : m_rejected (rejected) { } + + virtual bool enter_cell (const db::RecursiveShapeIterator *iter, const db::Cell *cell, const db::Box ®ion, const box_tree_type *complex_region) + { + LoggingReceiver::enter_cell (iter, cell, region, complex_region); + return cell->cell_index () != m_rejected; + } + +private: + db::cell_index_type m_rejected; +}; + +// Push mode with cells +TEST(10) +{ + db::Manager m; + db::Layout g (&m); + g.insert_layer(0); + + db::Cell &c0 (g.cell (g.add_cell ())); + db::Cell &c1 (g.cell (g.add_cell ())); + db::Cell &c2 (g.cell (g.add_cell ())); + + db::Box b (1000, -500, 2000, 500); + c2.shapes (0).insert (b); + + db::Trans tt; + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt, db::Vector (0, 6000), db::Vector (6000, 0), 2, 2)); + c1.insert (db::CellInstArray (db::CellInst (c2.cell_index ()), tt, db::Vector (0, 2000), db::Vector (3000, 1000), 2, 2)); + + LoggingReceiver lr1; + db::RecursiveShapeIterator i1 (g, c0, 0); + i1.push (&lr1); + + EXPECT_EQ (lr1.text (), + "begin\n" + "new_inst($2,all)\n" + "new_inst_member($2,r0 *1 0,0)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,0)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,2000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,1000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,3000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 0,6000)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,6000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,8000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,7000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,9000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,0)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,0)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,2000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,1000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,3000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,6000)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,6000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 6000,8000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,7000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 9000,9000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "end\n" + ); + + ReceiverRejectingACell rr1 (c2.cell_index ()); + db::RecursiveShapeIterator ir1 (g, c0, 0); + ir1.push (&rr1); + + EXPECT_EQ (rr1.text (), + "begin\n" + "new_inst($2,all)\n" + "new_inst_member($2,r0 *1 0,0)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 0,6000)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,0)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,6000)\n" + "enter_cell($2)\n" + "new_inst($3,all)\n" + "new_inst_member($3,r0 *1 0,0)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000)\n" + "enter_cell($3)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "end\n" + ); + + ReceiverRejectingACell rr2 (c1.cell_index ()); + db::RecursiveShapeIterator ir2 (g, c0, 0); + ir2.push (&rr2); + + EXPECT_EQ (rr2.text (), + "begin\n" + "new_inst($2,all)\n" + "new_inst_member($2,r0 *1 0,0)\n" + "enter_cell($2)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 0,6000)\n" + "enter_cell($2)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,0)\n" + "enter_cell($2)\n" + "leave_cell($2)\n" + "new_inst_member($2,r0 *1 6000,6000)\n" + "enter_cell($2)\n" + "leave_cell($2)\n" + "end\n" + ); + + LoggingReceiver lr2; + db::RecursiveShapeIterator i2 (g, c0, 0, db::Box (0, 0, 5000, 5000)); + i2.push (&lr2); + + EXPECT_EQ (lr2.text (), + "begin\n" + "new_inst($2)\n" + "new_inst_member($2,r0 *1 0,0)\n" + "enter_cell($2)\n" + "new_inst($3)\n" + "new_inst_member($3,r0 *1 0,0)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,0)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 0,2000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 0,2000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,1000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,1000)\n" + "leave_cell($3)\n" + "new_inst_member($3,r0 *1 3000,3000)\n" + "enter_cell($3)\n" + "shape(box (1000,-500;2000,500),r0 *1 3000,3000)\n" + "leave_cell($3)\n" + "leave_cell($2)\n" + "end\n" + ); }