diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 8c0a4e558..90b03bf16 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -208,7 +208,8 @@ HEADERS = \ dbForceLink.h \ dbPlugin.h \ dbInit.h \ - dbConverters.h + dbConverters.h \ + gsiDeclDbHelpers.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 8a1d0a591..94e5cd184 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -1725,9 +1725,32 @@ public: } } + LayoutLocker (const LayoutLocker &other) + : mp_layout (other.mp_layout) + { + if (mp_layout) { + mp_layout->start_changes (); + } + } + + LayoutLocker &operator= (const LayoutLocker &other) + { + if (this == &other) { + return *this; + } + + if (mp_layout) { + mp_layout->end_changes (); + } + mp_layout = other.mp_layout; + if (mp_layout) { + mp_layout->start_changes (); + } + + return *this; + } + private: - LayoutLocker (const LayoutLocker &); - LayoutLocker &operator= (const LayoutLocker &); db::Layout *mp_layout; }; diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index f0e3a3637..0865ab1e7 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -23,6 +23,8 @@ #include "gsiDecl.h" + +#include "gsiDeclDbHelpers.h" #include "dbLayout.h" #include "dbBoxConvert.h" #include "dbRegion.h" @@ -711,70 +713,70 @@ static void dump_mem_statistics (const db::Cell *cell, bool detailed) ms.print (); } -static db::Shapes::shape_iterator begin_shapes (const db::Cell *s, unsigned int layer_index, unsigned int flags) +static gsi::layout_locking_iterator1 begin_shapes (const db::Cell *s, unsigned int layer_index, unsigned int flags) { - return s->begin (layer_index, flags); + return gsi::layout_locking_iterator1 (s->layout (), s->begin (layer_index, flags)); } -static db::Shapes::shape_iterator begin_shapes_all (const db::Cell *s, unsigned int layer_index) +static gsi::layout_locking_iterator1 begin_shapes_all (const db::Cell *s, unsigned int layer_index) { - return s->begin (layer_index, db::ShapeIterator::All); + return gsi::layout_locking_iterator1 (s->layout (), s->begin (layer_index, db::ShapeIterator::All)); } -static db::Shapes::shape_iterator begin_touching_shapes (const db::Cell *s, unsigned int layer_index, const db::Box &box, unsigned int flags) +static gsi::layout_locking_iterator1 begin_touching_shapes (const db::Cell *s, unsigned int layer_index, const db::Box &box, unsigned int flags) { - return s->begin_touching (layer_index, box, flags); + return gsi::layout_locking_iterator1 (s->layout (), s->begin_touching (layer_index, box, flags)); } -static db::Shapes::shape_iterator begin_touching_shapes_all (const db::Cell *s, unsigned int layer_index, const db::Box &box) +static gsi::layout_locking_iterator1 begin_touching_shapes_all (const db::Cell *s, unsigned int layer_index, const db::Box &box) { - return s->begin_touching (layer_index, box, db::ShapeIterator::All); + return gsi::layout_locking_iterator1 (s->layout (), s->begin_touching (layer_index, box, db::ShapeIterator::All)); } -static db::Shapes::shape_iterator begin_overlapping_shapes (const db::Cell *s, unsigned int layer_index, const db::Box &box, unsigned int flags) +static gsi::layout_locking_iterator1 begin_overlapping_shapes (const db::Cell *s, unsigned int layer_index, const db::Box &box, unsigned int flags) { - return s->begin_overlapping (layer_index, box, flags); + return gsi::layout_locking_iterator1 (s->layout (), s->begin_overlapping (layer_index, box, flags)); } -static db::Shapes::shape_iterator begin_overlapping_shapes_all (const db::Cell *s, unsigned int layer_index, const db::Box &box) +static gsi::layout_locking_iterator1 begin_overlapping_shapes_all (const db::Cell *s, unsigned int layer_index, const db::Box &box) { - return s->begin_overlapping (layer_index, box, db::ShapeIterator::All); + return gsi::layout_locking_iterator1 (s->layout (), s->begin_overlapping (layer_index, box, db::ShapeIterator::All)); } -static db::Shapes::shape_iterator begin_touching_shapes_um (const db::Cell *s, unsigned int layer_index, const db::DBox &box, unsigned int flags) +static gsi::layout_locking_iterator1 begin_touching_shapes_um (const db::Cell *s, unsigned int layer_index, const db::DBox &box, unsigned int flags) { const db::Layout *layout = s->layout (); if (! layout) { throw tl::Exception (tl::to_string (tr ("Cell does not reside inside a layout - cannot use a micrometer search box"))); } - return s->begin_touching (layer_index, db::CplxTrans (layout->dbu ()).inverted () * box, flags); + return gsi::layout_locking_iterator1 (s->layout (), s->begin_touching (layer_index, db::CplxTrans (layout->dbu ()).inverted () * box, flags)); } -static db::Shapes::shape_iterator begin_touching_shapes_all_um (const db::Cell *s, unsigned int layer_index, const db::DBox &box) +static gsi::layout_locking_iterator1 begin_touching_shapes_all_um (const db::Cell *s, unsigned int layer_index, const db::DBox &box) { const db::Layout *layout = s->layout (); if (! layout) { throw tl::Exception (tl::to_string (tr ("Cell does not reside inside a layout - cannot use a micrometer search box"))); } - return s->begin_touching (layer_index, db::CplxTrans (layout->dbu ()).inverted () * box, db::ShapeIterator::All); + return gsi::layout_locking_iterator1 (s->layout (), s->begin_touching (layer_index, db::CplxTrans (layout->dbu ()).inverted () * box, db::ShapeIterator::All)); } -static db::Shapes::shape_iterator begin_overlapping_shapes_um (const db::Cell *s, unsigned int layer_index, const db::DBox &box, unsigned int flags) +static gsi::layout_locking_iterator1 begin_overlapping_shapes_um (const db::Cell *s, unsigned int layer_index, const db::DBox &box, unsigned int flags) { const db::Layout *layout = s->layout (); if (! layout) { throw tl::Exception (tl::to_string (tr ("Cell does not reside inside a layout - cannot use a micrometer search box"))); } - return s->begin_overlapping (layer_index, db::CplxTrans (layout->dbu ()).inverted () * box, flags); + return gsi::layout_locking_iterator1 (s->layout (), s->begin_overlapping (layer_index, db::CplxTrans (layout->dbu ()).inverted () * box, flags)); } -static db::Shapes::shape_iterator begin_overlapping_shapes_all_um (const db::Cell *s, unsigned int layer_index, const db::DBox &box) +static gsi::layout_locking_iterator1 begin_overlapping_shapes_all_um (const db::Cell *s, unsigned int layer_index, const db::DBox &box) { const db::Layout *layout = s->layout (); if (! layout) { throw tl::Exception (tl::to_string (tr ("Cell does not reside inside a layout - cannot use a micrometer search box"))); } - return s->begin_overlapping (layer_index, db::CplxTrans (layout->dbu ()).inverted () * box, db::ShapeIterator::All); + return gsi::layout_locking_iterator1 (s->layout (), s->begin_overlapping (layer_index, db::CplxTrans (layout->dbu ()).inverted () * box, db::ShapeIterator::All)); } static db::Instance insert_inst_with_props (db::Cell *c, const db::Cell::cell_inst_array_type &inst, db::properties_id_type id) @@ -1679,18 +1681,12 @@ static db::DBox cell_dbbox_per_layer (const db::Cell *cell, unsigned int layer_i return cell->bbox (layer_index) * layout->dbu (); } -static db::Cell::overlapping_iterator cell_begin_overlapping_inst_um (const db::Cell *cell, const db::DBox &db) +gsi::layout_locking_iterator1 begin_overlapping_inst (const db::Cell *cell, const db::Cell::box_type &b) { - const db::Layout *layout = cell->layout (); - if (! layout) { - throw tl::Exception (tl::to_string (tr ("Cell does not reside inside a layout - cannot use a micrometer-unit search boxes"))); - } - - db::CplxTrans dbu_trans (layout->dbu ()); - return cell->begin_overlapping (dbu_trans.inverted () * db); + return gsi::layout_locking_iterator1 (cell->layout (), cell->begin_overlapping (b)); } -static db::Cell::touching_iterator cell_begin_touching_inst_um (const db::Cell *cell, const db::DBox &db) +gsi::layout_locking_iterator1 begin_overlapping_inst_um (const db::Cell *cell, const db::DBox &dbox) { const db::Layout *layout = cell->layout (); if (! layout) { @@ -1698,7 +1694,43 @@ static db::Cell::touching_iterator cell_begin_touching_inst_um (const db::Cell * } db::CplxTrans dbu_trans (layout->dbu ()); - return cell->begin_touching (dbu_trans.inverted () * db); + return gsi::layout_locking_iterator1 (cell->layout (), cell->begin_overlapping (dbu_trans.inverted () * dbox)); +} + +gsi::layout_locking_iterator1 begin_touching_inst (const db::Cell *cell, const db::Cell::box_type &b) +{ + return gsi::layout_locking_iterator1 (cell->layout (), cell->begin_touching (b)); +} + +gsi::layout_locking_iterator1 begin_touching_inst_um (const db::Cell *cell, const db::DBox &dbox) +{ + const db::Layout *layout = cell->layout (); + if (! layout) { + throw tl::Exception (tl::to_string (tr ("Cell does not reside inside a layout - cannot use a micrometer-unit search boxes"))); + } + + db::CplxTrans dbu_trans (layout->dbu ()); + return gsi::layout_locking_iterator1 (cell->layout (), cell->begin_touching (dbu_trans.inverted () * dbox)); +} + +gsi::layout_locking_iterator1 begin_child_cells (const db::Cell *cell) +{ + return gsi::layout_locking_iterator1 (cell->layout (), cell->begin_child_cells ()); +} + +gsi::layout_locking_iterator1 begin_parent_insts (const db::Cell *cell) +{ + return gsi::layout_locking_iterator1 (cell->layout (), cell->begin_parent_insts ()); +} + +gsi::layout_locking_iterator2 begin_parent_cells (const db::Cell *cell) +{ + return gsi::layout_locking_iterator2 (cell->layout (), cell->begin_parent_cells (), cell->end_parent_cells ()); +} + +static layout_locking_iterator1 begin_inst (db::Cell *cell) +{ + return layout_locking_iterator1 (cell->layout (), cell->begin ()); } Class decl_Cell ("db", "Cell", @@ -2569,7 +2601,7 @@ Class decl_Cell ("db", "Cell", "\n" "This method has been introduced in version 0.25." ) + - gsi::iterator ("each_overlapping_inst", (db::Cell::overlapping_iterator (db::Cell::*) (const db::Cell::box_type &b) const) &db::Cell::begin_overlapping, gsi::arg ("b"), + gsi::iterator_ext ("each_overlapping_inst", &begin_overlapping_inst, gsi::arg ("b"), "@brief Gets the instances overlapping the given rectangle\n" "\n" "This will iterate over all child cell\n" @@ -2579,7 +2611,7 @@ Class decl_Cell ("db", "Cell", "\n" "Starting with version 0.15, this iterator delivers \\Instance objects rather than \\CellInstArray objects." ) + - gsi::iterator_ext ("each_overlapping_inst", &cell_begin_overlapping_inst_um, gsi::arg ("b"), + gsi::iterator_ext ("each_overlapping_inst", &begin_overlapping_inst_um, gsi::arg ("b"), "@brief Gets the instances overlapping the given rectangle, with the rectangle in micrometer units\n" "\n" "This will iterate over all child cell\n" @@ -2592,7 +2624,7 @@ Class decl_Cell ("db", "Cell", "\n" "This variant has been introduced in version 0.25." ) + - gsi::iterator ("each_touching_inst", (db::Cell::touching_iterator (db::Cell::*) (const db::Cell::box_type &b) const) &db::Cell::begin_touching, gsi::arg ("b"), + gsi::iterator_ext ("each_touching_inst", &begin_touching_inst, gsi::arg ("b"), "@brief Gets the instances touching the given rectangle\n" "\n" "This will iterate over all child cell\n" @@ -2602,7 +2634,7 @@ Class decl_Cell ("db", "Cell", "\n" "Starting with version 0.15, this iterator delivers \\Instance objects rather than \\CellInstArray objects." ) + - gsi::iterator_ext ("each_touching_inst", &cell_begin_touching_inst_um, gsi::arg ("b"), + gsi::iterator_ext ("each_touching_inst", &begin_touching_inst_um, gsi::arg ("b"), "@brief Gets the instances touching the given rectangle, with the rectangle in micrometer units\n" "\n" "This will iterate over all child cell\n" @@ -2615,7 +2647,7 @@ Class decl_Cell ("db", "Cell", "\n" "This variant has been introduced in version 0.25." ) + - gsi::iterator ("each_child_cell", &db::Cell::begin_child_cells, + gsi::iterator_ext ("each_child_cell", &begin_child_cells, "@brief Iterates over all child cells\n" "\n" "This iterator will report the child cell indices, not every instance.\n" @@ -2626,12 +2658,12 @@ Class decl_Cell ("db", "Cell", "The number of child cells (not child instances!) is returned.\n" "CAUTION: this method is SLOW, in particular if many instances are present.\n" ) + - gsi::iterator ("each_inst", (db::Cell::const_iterator (db::Cell::*) () const) &db::Cell::begin, + gsi::iterator_ext ("each_inst", &begin_inst, "@brief Iterates over all child instances (which may actually be instance arrays)\n" "\n" "Starting with version 0.15, this iterator delivers \\Instance objects rather than \\CellInstArray objects." ) + - gsi::iterator ("each_parent_inst", &db::Cell::begin_parent_insts, + gsi::iterator_ext ("each_parent_inst", &begin_parent_insts, "@brief Iterates over the parent instance list (which may actually be instance arrays)\n" "\n" "The parent instances are basically inversions of the instances. Using parent instances " @@ -2642,7 +2674,7 @@ Class decl_Cell ("db", "Cell", "\n" "The number of parent cells (cells which reference our cell) is reported." ) + - gsi::iterator ("each_parent_cell", &db::Cell::begin_parent_cells, &db::Cell::end_parent_cells, + gsi::iterator_ext ("each_parent_cell", &begin_parent_cells, "@brief Iterates over all parent cells\n" "\n" "This iterator will iterate over the parent cells, just returning their\n" diff --git a/src/db/db/gsiDeclDbHelpers.h b/src/db/db/gsiDeclDbHelpers.h new file mode 100644 index 000000000..22b5f5e50 --- /dev/null +++ b/src/db/db/gsiDeclDbHelpers.h @@ -0,0 +1,83 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_gsiDeclDbHelpers +#define HDR_gsiDeclDbHelpers + +#include "dbLayoutUtils.h" + +namespace gsi +{ + /** + * @brief A safe iterator locking the layout while iterating a container within it + */ + template + class layout_locking_iterator2 + : private db::LayoutLocker + { + public: + typedef typename I::value_type value_type; + typedef typename I::reference reference; + typedef typename I::pointer pointer; + typedef typename I::difference_type difference_type; + typedef typename I::iterator_category iterator_category; + + layout_locking_iterator2 (const db::Layout *layout, const I &b, const I &e) : db::LayoutLocker (const_cast (layout)), m_b (b), m_e (e) {} + bool at_end () const { return m_b == m_e; } + void operator++ () { ++m_b; } + + reference operator* () const { return *m_b; } + pointer operator-> () const { return m_b.operator-> (); } + + private: + I m_b, m_e; + }; + + /** + * @brief A safe iterator locking the layout while iterating a container within it + */ + template + class layout_locking_iterator1 + : private db::LayoutLocker + { + public: + typedef typename I::value_type value_type; + typedef typename I::reference reference; + typedef typename I::pointer pointer; + typedef typename I::difference_type difference_type; + typedef typename I::iterator_category iterator_category; + + layout_locking_iterator1 (const db::Layout *layout, const I &i) : db::LayoutLocker (const_cast (layout)), m_i (i) { } + bool at_end () const { return m_i.at_end (); } + void operator++ () { ++m_i; } + + reference operator* () const { return *m_i; } + pointer operator-> () const { return m_i.operator-> (); } + + private: + I m_i; + }; + +} + +#endif diff --git a/src/db/db/gsiDeclDbShapes.cc b/src/db/db/gsiDeclDbShapes.cc index 05d8a5217..19b7661be 100644 --- a/src/db/db/gsiDeclDbShapes.cc +++ b/src/db/db/gsiDeclDbShapes.cc @@ -22,6 +22,8 @@ #include "gsiDecl.h" + +#include "gsiDeclDbHelpers.h" #include "dbShapes.h" #include "dbShape.h" #include "dbLayout.h" @@ -101,54 +103,54 @@ static db::Shape dinsert_with_properties (db::Shapes *s, const Sh &p, db::proper return s->insert (db::object_with_properties (db::CplxTrans (shapes_dbu (s)).inverted () * p, id)); } -static db::Shapes::shape_iterator begin (const db::Shapes *s, unsigned int flags) +static gsi::layout_locking_iterator1 begin (const db::Shapes *s, unsigned int flags) { - return s->begin (flags); + return gsi::layout_locking_iterator1 (s->layout (), s->begin (flags)); } -static db::Shapes::shape_iterator begin_all (const db::Shapes *s) +static gsi::layout_locking_iterator1begin_all (const db::Shapes *s) { - return s->begin (db::ShapeIterator::All); + return gsi::layout_locking_iterator1 (s->layout (), s->begin (db::ShapeIterator::All)); } -static db::Shapes::shape_iterator begin_overlapping (const db::Shapes *s, unsigned int flags, const db::Box ®ion) +static gsi::layout_locking_iterator1begin_overlapping (const db::Shapes *s, unsigned int flags, const db::Box ®ion) { - return s->begin_overlapping (region, flags); + return gsi::layout_locking_iterator1 (s->layout (), s->begin_overlapping (region, flags)); } -static db::Shapes::shape_iterator begin_doverlapping (const db::Shapes *s, unsigned int flags, const db::DBox ®ion) +static gsi::layout_locking_iterator1begin_doverlapping (const db::Shapes *s, unsigned int flags, const db::DBox ®ion) { - return s->begin_overlapping (db::CplxTrans (shapes_dbu (s)).inverted () * region, flags); + return gsi::layout_locking_iterator1 (s->layout (), s->begin_overlapping (db::CplxTrans (shapes_dbu (s)).inverted () * region, flags)); } -static db::Shapes::shape_iterator begin_overlapping_all (const db::Shapes *s, const db::Box ®ion) +static gsi::layout_locking_iterator1begin_overlapping_all (const db::Shapes *s, const db::Box ®ion) { - return s->begin_overlapping (region, db::ShapeIterator::All); + return gsi::layout_locking_iterator1 (s->layout (), s->begin_overlapping (region, db::ShapeIterator::All)); } -static db::Shapes::shape_iterator begin_doverlapping_all (const db::Shapes *s, const db::DBox ®ion) +static gsi::layout_locking_iterator1begin_doverlapping_all (const db::Shapes *s, const db::DBox ®ion) { - return s->begin_overlapping (db::CplxTrans (shapes_dbu (s)).inverted () * region, db::ShapeIterator::All); + return gsi::layout_locking_iterator1 (s->layout (), s->begin_overlapping (db::CplxTrans (shapes_dbu (s)).inverted () * region, db::ShapeIterator::All)); } -static db::Shapes::shape_iterator begin_touching (const db::Shapes *s, unsigned int flags, const db::Box ®ion) +static gsi::layout_locking_iterator1begin_touching (const db::Shapes *s, unsigned int flags, const db::Box ®ion) { - return s->begin_touching (region, flags); + return gsi::layout_locking_iterator1 (s->layout (), s->begin_touching (region, flags)); } -static db::Shapes::shape_iterator begin_dtouching (const db::Shapes *s, unsigned int flags, const db::DBox ®ion) +static gsi::layout_locking_iterator1begin_dtouching (const db::Shapes *s, unsigned int flags, const db::DBox ®ion) { - return s->begin_touching (db::CplxTrans (shapes_dbu (s)).inverted () * region, flags); + return gsi::layout_locking_iterator1 (s->layout (), s->begin_touching (db::CplxTrans (shapes_dbu (s)).inverted () * region, flags)); } -static db::Shapes::shape_iterator begin_touching_all (const db::Shapes *s, const db::Box ®ion) +static gsi::layout_locking_iterator1begin_touching_all (const db::Shapes *s, const db::Box ®ion) { - return s->begin_touching (region, db::ShapeIterator::All); + return gsi::layout_locking_iterator1 (s->layout (), s->begin_touching (region, db::ShapeIterator::All)); } -static db::Shapes::shape_iterator begin_dtouching_all (const db::Shapes *s, const db::DBox ®ion) +static gsi::layout_locking_iterator1begin_dtouching_all (const db::Shapes *s, const db::DBox ®ion) { - return s->begin_touching (db::CplxTrans (shapes_dbu (s)).inverted () * region, db::ShapeIterator::All); + return gsi::layout_locking_iterator1 (s->layout (), s->begin_touching (db::CplxTrans (shapes_dbu (s)).inverted () * region, db::ShapeIterator::All)); } static void transform_shapes (db::Shapes *s, const db::Trans &trans) diff --git a/src/tl/tl/tlReuseVector.h b/src/tl/tl/tlReuseVector.h index f31236a3a..c7cad2b69 100644 --- a/src/tl/tl/tlReuseVector.h +++ b/src/tl/tl/tlReuseVector.h @@ -887,6 +887,21 @@ public: init (); } + /** + * @brief Returns a value indicating whether the given index is valid + */ + bool is_used (size_type n) const + { + if (n >= first () && n < last ()) { + if (mp_rdata) { + return mp_rdata->is_used (n); + } else { + return true; + } + } + return false; + } + /** * @brief For diagnostics purposes only */ @@ -908,18 +923,6 @@ private: mp_rdata = 0; } - bool is_used (size_type n) const - { - if (n >= first () && n < last ()) { - if (mp_rdata) { - return mp_rdata->is_used (n); - } else { - return true; - } - } - return false; - } - size_type first () const { if (mp_rdata) { diff --git a/testdata/gds/t200.gds b/testdata/gds/t200.gds new file mode 100644 index 000000000..08552d3a4 Binary files /dev/null and b/testdata/gds/t200.gds differ diff --git a/testdata/ruby/dbLayout.rb b/testdata/ruby/dbLayout.rb index 04b418eb9..cb89a9e0d 100644 --- a/testdata/ruby/dbLayout.rb +++ b/testdata/ruby/dbLayout.rb @@ -1994,6 +1994,45 @@ END end + # Iterating while flatten + def test_issue200 + + ly = RBA::Layout.new + ly.read(ENV["TESTSRC"] + "/testdata/gds/t200.gds") + l1 = ly.layer(1, 0) + l2 = ly.layer(2, 0) + l3 = ly.layer(3, 0) + + tc_name = ly.top_cell.name + r1 = RBA::Region::new(ly.top_cell.begin_shapes_rec(l1)) + r2 = RBA::Region::new(ly.top_cell.begin_shapes_rec(l2)) + r3 = RBA::Region::new(ly.top_cell.begin_shapes_rec(l3)) + assert_equal(r1.size > 0, true) + assert_equal(r2.size > 0, true) + assert_equal(r3.size == 0, true) + + ly.top_cell.each_inst do |ci| + ci.flatten + end + + tc = ly.cell(tc_name) + assert_equal(ly.top_cells.size, 4) + assert_equal(tc.child_cells, 0) + assert_equal(tc.parent_cells, 0) + + rr1 = RBA::Region::new(tc.begin_shapes_rec(l1)) + rr2 = RBA::Region::new(tc.begin_shapes_rec(l2)) + rr3 = RBA::Region::new(tc.begin_shapes_rec(l3)) + assert_equal(r1.size, rr1.size) + assert_equal(r2.size, rr2.size) + assert_equal(r3.size, rr3.size) + + assert_equal((rr1 ^ r1).is_empty?, true) + assert_equal((rr2 ^ r2).is_empty?, true) + assert_equal((rr3 ^ r3).is_empty?, true) + + end + end load("test_epilogue.rb")