diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 9bd72e860..de1f265e4 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -680,13 +680,17 @@ bool run_tiled_xor (const XORData &xor_data) if (ll->second.first < 0) { proc.input (in_a, db::RecursiveShapeIterator ()); } else { - proc.input (in_a, db::RecursiveShapeIterator (*xor_data.layout_a, xor_data.layout_a->cell (xor_data.cell_a), ll->second.first)); + db::RecursiveShapeIterator si (*xor_data.layout_a, xor_data.layout_a->cell (xor_data.cell_a), ll->second.first); + si.set_for_merged_input (true); + proc.input (in_a, si); } if (ll->second.second < 0) { proc.input (in_b, db::RecursiveShapeIterator ()); } else { - proc.input (in_b, db::RecursiveShapeIterator (*xor_data.layout_b, xor_data.layout_b->cell (xor_data.cell_b), ll->second.second)); + db::RecursiveShapeIterator si (*xor_data.layout_b, xor_data.layout_b->cell (xor_data.cell_b), ll->second.second); + si.set_for_merged_input (true); + proc.input (in_b, si); } std::string expr = "var x=" + in_a + "^" + in_b + "; "; @@ -805,10 +809,12 @@ bool run_deep_xor (const XORData &xor_data) 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)); diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 9ee803c2a..2171c4f37 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -219,6 +219,8 @@ AsIfFlatRegion::area (const db::Box &box) const for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { if (box.empty () || p->box ().inside (box)) { a += p->area (); + } else if (p->is_box ()) { + a += (p->box () & box).area (); } else { std::vector clipped; clip_poly (*p, box, clipped); diff --git a/src/db/db/dbBox.h b/src/db/db/dbBox.h index 94e2ac58c..cf7d4b233 100644 --- a/src/db/db/dbBox.h +++ b/src/db/db/dbBox.h @@ -254,6 +254,27 @@ struct DB_PUBLIC_TEMPLATE box */ box &operator+= (const point &p); + /** + * @brief Subtraction of boxes. + * + * The -= operator subtracts the argument box from *this. + * Subtraction leaves the bounding box of the region resulting + * from the geometrical NOT of *this and the argument box. + * Subtracting a box from itself gives an empty box. + * Subtracting a box that does not cover a full side of + * *this will not modify the box. + * + * @param b The box to subtract from *this. + * + * @return The result box. + */ + box &operator-= (const box &b); + + /** + * @brief A method version for operator- (mainly for automation purposes) + */ + box subtracted (const box &b) const; + /** * @brief Intersection of boxes. * @@ -784,6 +805,50 @@ box::operator+= (const point &p) return *this; } +template +inline box +box::subtracted (const box &b) const +{ + box r (*this); + r -= b; + return r; +} + +template +inline box & +box::operator-= (const box &bx) +{ + if (bx.empty () || empty ()) { + return *this; + } + + coord_type l = m_p1.x (), r = m_p2.x (); + coord_type b = m_p1.y (), t = m_p2.y (); + + if (bx.bottom () <= bottom () && bx.top () >= top ()) { + if (bx.left () <= left ()) { + l = std::max (bx.right (), left ()); + } + if (bx.right () >= right ()) { + r = std::min (bx.left (), right ()); + } + } + + if (bx.left () <= left () && bx.right () >= right ()) { + if (bx.bottom () <= bottom ()) { + b = std::max (bx.top (), bottom ()); + } + if (bx.top () >= top ()) { + t = std::min (bx.bottom (), top ()); + } + } + + m_p1 = point_type (l, b); + m_p2 = point_type (r, t); + + return *this; +} + template inline box & box::operator&= (const box &b) @@ -1363,6 +1428,23 @@ operator+ (const box &b1, const box &b2) return bb; } +/** + * @brief Box subtraction mapped on the - operator + * + * @param b1 The first box + * @param b2 The second box to subtract from the first + * + * @return The bounding box of the region formed but subtracting b2 from b1 + */ +template +inline box +operator- (const box &b1, const box &b2) +{ + box bb (b1); + bb -= b2; + return bb; +} + /** * @brief "Folding" of two boxes * diff --git a/src/db/db/dbEdgePairs.cc b/src/db/db/dbEdgePairs.cc index fcfd405f9..6e0d9059e 100644 --- a/src/db/db/dbEdgePairs.cc +++ b/src/db/db/dbEdgePairs.cc @@ -30,6 +30,9 @@ #include "dbOriginalLayerEdgePairs.h" #include "dbEdges.h" #include "dbRegion.h" +#include "dbLayout.h" +#include "dbWriter.h" +#include "tlStream.h" #include "tlVariant.h" @@ -93,6 +96,23 @@ EdgePairs::EdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss, con mp_delegate = new DeepEdgePairs (si, dss, trans); } +void +EdgePairs::write (const std::string &fn) const +{ + // method provided for debugging purposes + + db::Layout layout; + const db::Cell &top = layout.cell (layout.add_cell ("EDGE_PAIRS")); + unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); + insert_into (&layout, top.cell_index (), li); + + tl::OutputStream os (fn); + db::SaveLayoutOptions opt; + opt.set_format_from_filename (fn); + db::Writer writer (opt); + writer.write (layout, os); +} + template void EdgePairs::insert (const Sh &shape) { diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index 9d1b1aee2..53a499689 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -185,6 +185,14 @@ public: */ explicit EdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans); + /** + * @brief Writes the edge pair collection to a file + * + * This method is provided for debugging purposes. A flat image of the + * region is written to a layout file with a single top cell on layer 0/0. + */ + void write (const std::string &fn) const; + /** * @brief Implementation of the ShapeCollection interface */ diff --git a/src/db/db/dbEdgeProcessor.cc b/src/db/db/dbEdgeProcessor.cc index 9f1c3f11f..4f0534ae8 100644 --- a/src/db/db/dbEdgeProcessor.cc +++ b/src/db/db/dbEdgeProcessor.cc @@ -251,6 +251,13 @@ struct CutPoints } + // do not insert points twice + for (auto c = cut_points.begin (); c != cut_points.end (); ++c) { + if (*c == p) { + return; + } + } + cut_points.push_back (p); } @@ -1057,6 +1064,12 @@ EdgeProcessor::reserve (size_t n) mp_work_edges->reserve (n); } +size_t +EdgeProcessor::count () const +{ + return mp_work_edges->size (); +} + void EdgeProcessor::insert (const db::Edge &e, EdgeProcessor::property_type p) { diff --git a/src/db/db/dbEdgeProcessor.h b/src/db/db/dbEdgeProcessor.h index a6fb2a680..30de5b35d 100644 --- a/src/db/db/dbEdgeProcessor.h +++ b/src/db/db/dbEdgeProcessor.h @@ -695,6 +695,11 @@ public: */ void reserve (size_t n); + /** + * @brief Reports the number of edges stored in the processor + */ + size_t count () const; + /** * @brief Insert an edge */ diff --git a/src/db/db/dbEdges.cc b/src/db/db/dbEdges.cc index e5a6cf007..5cdefe414 100644 --- a/src/db/db/dbEdges.cc +++ b/src/db/db/dbEdges.cc @@ -28,6 +28,9 @@ #include "dbFlatEdges.h" #include "dbEdgesUtils.h" #include "dbRegion.h" +#include "dbLayout.h" +#include "dbWriter.h" +#include "tlStream.h" namespace db { @@ -141,6 +144,23 @@ Edges::set_delegate (EdgesDelegate *delegate, bool keep_attributes) } } +void +Edges::write (const std::string &fn) const +{ + // method provided for debugging purposes + + db::Layout layout; + const db::Cell &top = layout.cell (layout.add_cell ("EDGES")); + unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); + insert_into (&layout, top.cell_index (), li); + + tl::OutputStream os (fn); + db::SaveLayoutOptions opt; + opt.set_format_from_filename (fn); + db::Writer writer (opt); + writer.write (layout, os); +} + void Edges::clear () { diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index 28decbbb6..ffb8ab6ff 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -833,6 +833,14 @@ public: return *this; } + /** + * @brief Writes the edge collection to a file + * + * This method is provided for debugging purposes. A flat image of the + * region is written to a layout file with a single top cell on layer 0/0. + */ + void write (const std::string &fn) const; + /** * @brief Intersections with other edges * Intersections are similar to "AND", but will also report diff --git a/src/db/db/dbFlatRegion.cc b/src/db/db/dbFlatRegion.cc index d96bb8a22..0d79dbe3e 100644 --- a/src/db/db/dbFlatRegion.cc +++ b/src/db/db/dbFlatRegion.cc @@ -43,7 +43,6 @@ FlatRegion::FlatRegion (const FlatRegion &other) : MutableRegion (other), mp_polygons (other.mp_polygons), mp_merged_polygons (other.mp_merged_polygons), mp_properties_repository (other.mp_properties_repository) { init (); - m_is_merged = other.m_is_merged; m_merged_polygons_valid = other.m_merged_polygons_valid; } @@ -52,15 +51,22 @@ FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged) : MutableRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false)), mp_properties_repository (new db::PropertiesRepository ()) { init (); - m_is_merged = is_merged; } +FlatRegion::FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged) + : MutableRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false)), mp_properties_repository (new db::PropertiesRepository ()) +{ + init (); + m_is_merged = is_merged; + transform_generic (trans); + set_merged_semantics (merged_semantics); +} + FlatRegion::FlatRegion (bool is_merged) : MutableRegion (), mp_polygons (new db::Shapes (false)), mp_merged_polygons (new db::Shapes (false)), mp_properties_repository (new db::PropertiesRepository ()) { init (); - m_is_merged = is_merged; } diff --git a/src/db/db/dbFlatRegion.h b/src/db/db/dbFlatRegion.h index d89e53173..55805a8a5 100644 --- a/src/db/db/dbFlatRegion.h +++ b/src/db/db/dbFlatRegion.h @@ -52,7 +52,8 @@ public: typedef polygon_layer_wp_type::iterator polygon_iterator_wp_type; FlatRegion (); - FlatRegion (const db::Shapes &polygons, bool is_merged); + FlatRegion (const db::Shapes &polygons, bool is_merged = false); + FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false); FlatRegion (bool is_merged); FlatRegion (const FlatRegion &other); diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index 9a8f6052f..cc02f75a9 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -48,6 +48,8 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI mp_shape_prop_sel = d.mp_shape_prop_sel; m_shape_inv_prop_sel = d.m_shape_inv_prop_sel; m_overlapping = d.m_overlapping; + m_for_merged_input = d.m_for_merged_input; + m_start = d.m_start; m_stop = d.m_stop; @@ -80,6 +82,7 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI m_cells = d.m_cells; 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_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; @@ -99,6 +102,7 @@ RecursiveShapeIterator::RecursiveShapeIterator () mp_cell = 0; m_current_layer = 0; m_overlapping = false; + m_for_merged_input = false; m_max_depth = std::numeric_limits::max (); // all m_min_depth = 0; m_shape_flags = shape_iterator::All; @@ -116,6 +120,7 @@ RecursiveShapeIterator::RecursiveShapeIterator (const shapes_type &shapes) mp_shapes = &shapes; mp_top_cell = 0; m_overlapping = false; + m_for_merged_input = false; init (); init_region (box_type::world ()); } @@ -127,6 +132,7 @@ RecursiveShapeIterator::RecursiveShapeIterator (const shapes_type &shapes, const mp_shapes = &shapes; mp_top_cell = 0; m_overlapping = overlapping; + m_for_merged_input = false; init (); init_region (region); } @@ -138,11 +144,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const shapes_type &shapes, const mp_shapes = &shapes; mp_top_cell = 0; m_overlapping = overlapping; + m_for_merged_input = false; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const box_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const box_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout, layer) { m_layer = layer; @@ -151,11 +158,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const region_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const region_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout, layer) { m_layer = layer; @@ -164,11 +172,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, bool for_merged_input) : m_box_convert (layout, layer) { m_layer = layer; @@ -177,11 +186,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = false; + m_for_merged_input = for_merged_input; init (); init_region (box_type::world ()); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const box_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const box_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -191,11 +201,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const region_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const region_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -205,11 +216,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -219,11 +231,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = false; + m_for_merged_input = for_merged_input; init (); init_region (box_type::world ()); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const box_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const box_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -233,11 +246,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const region_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const region_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -247,11 +261,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -261,6 +276,7 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = false; + m_for_merged_input = for_merged_input; init (); init_region (box_type::world ()); } @@ -447,6 +463,8 @@ RecursiveShapeIterator::validate (RecursiveShapeReceiver *receiver) const m_local_region_stack.clear (); 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_local_complex_region_stack.clear (); if (mp_complex_region.get ()) { @@ -723,11 +741,21 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const if (is_empty) { + // skip entire cell ++m_inst; new_inst (receiver); - } else { - down (receiver); + } else if (!down (receiver)) { + + // skip this instance array member + ++m_inst_array; + new_inst_member (receiver); + + if (m_inst_array.at_end ()) { + ++m_inst; + new_inst (receiver); + } + } } else { @@ -755,9 +783,42 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const } } -void +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); @@ -779,11 +840,13 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const box_type new_region = box_type::world (); // compute the region inside the new cell - if (new_region != m_region) { - new_region = m_trans.inverted () * m_region; + if (new_region != m_local_region_stack.back ()) { + new_region = m_inst->complex_trans (*m_inst_array).inverted () * m_local_region_stack.back (); new_region &= cell_bbox (cell_index ()); } + m_local_region_stack.push_back (new_region); + m_skip_shapes_stack.push_back (m_skip_shapes_stack.back () || skip_shapes); if (! m_local_complex_region_stack.empty ()) { @@ -817,11 +880,25 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const } - 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 ()); - } + // do not descend if the box is empty - new_cell (receiver); + if (m_local_region_stack.back ().empty ()) { + + pop (); + + return false; + + } else { + + if (receiver) { + receiver->enter_cell (this, cell (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ()); + } + + new_cell (receiver); + + return true; + + } } void @@ -831,6 +908,12 @@ RecursiveShapeIterator::up (RecursiveShapeReceiver *receiver) const receiver->leave_cell (this, cell ()); } + pop (); +} + +void +RecursiveShapeIterator::pop () const +{ m_shape = shape_iterator (); m_shape_quad_id = 0; @@ -846,6 +929,7 @@ RecursiveShapeIterator::up (RecursiveShapeReceiver *receiver) const mp_cell = m_cells.back (); m_cells.pop_back (); m_local_region_stack.pop_back (); + m_skip_shapes_stack.pop_back (); if (! m_local_complex_region_stack.empty ()) { m_local_complex_region_stack.pop_back (); } @@ -870,7 +954,7 @@ RecursiveShapeIterator::start_shapes () const void RecursiveShapeIterator::new_layer () const { - if (int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) { + if (m_skip_shapes_stack.back () || 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); @@ -901,12 +985,16 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const new_layer (); - m_inst = cell ()->begin_touching (m_local_region_stack.back ()); + if (m_overlapping) { + m_inst = cell ()->begin_touching (m_local_region_stack.back ().enlarged (box_type::vector_type (-1, -1))); + } else { + m_inst = cell ()->begin_touching (m_local_region_stack.back ()); + } m_inst_quad_id = 0; // skip instance quad if possible - if (! m_local_complex_region_stack.empty ()) { + if (! m_local_complex_region_stack.empty () && (! receiver || ! receiver->wants_all_cells ())) { skip_inst_iter_for_complex_region (); } @@ -922,7 +1010,7 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const while (! m_inst.at_end ()) { // skip instance quad if possible - if (! m_local_complex_region_stack.empty ()) { + if (! m_local_complex_region_stack.empty () && (! receiver || ! receiver->wants_all_cells ())) { skip_inst_iter_for_complex_region (); if (m_inst.at_end ()) { break; @@ -950,7 +1038,11 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const // a singular iterator m_inst_array = db::CellInstArray::iterator (m_inst->cell_inst ().front (), false); } else if (with_region) { - m_inst_array = m_inst->cell_inst ().begin_touching (m_local_region_stack.back (), m_box_convert); + if (m_overlapping) { + m_inst_array = m_inst->cell_inst ().begin_touching (m_local_region_stack.back ().enlarged (box_type::vector_type (-1, -1)), m_box_convert); + } else { + m_inst_array = m_inst->cell_inst ().begin_touching (m_local_region_stack.back (), m_box_convert); + } } else { m_inst_array = m_inst->cell_inst ().begin (); } diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index 43f913b1a..532fdd247 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -122,12 +122,13 @@ public: * @param layer The layer from which to deliver the shapes * @param region The region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const box_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const box_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor @@ -137,13 +138,14 @@ public: * @param layer The layer from which to deliver the shapes * @param region The complex region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. It allows specification of a complex * search region. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const region_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const region_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor for "world" iteration @@ -153,8 +155,9 @@ public: * @param layout The layout from which to get the cell hierarchy * @param cell The starting cell * @param layer The layer from which to deliver the shapes + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, bool for_merged_input = false); /** * @brief Standard constructor with a layer selection @@ -164,12 +167,13 @@ public: * @param layers The layers from which to deliver the shapes * @param region The region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const box_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const box_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor with a layer selection @@ -179,13 +183,14 @@ public: * @param layers The layers from which to deliver the shapes * @param region The complex region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. It allows specification of a complex * search region. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const region_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const region_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor with a layer selection @@ -195,12 +200,13 @@ public: * @param layers The layers from which to deliver the shapes * @param region The region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const box_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const box_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor with a layer selection @@ -210,13 +216,14 @@ public: * @param layers The layers from which to deliver the shapes * @param region The complex region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. It allows specification of a complex * search region. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const region_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const region_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor for "world" iteration with a layer set @@ -226,8 +233,9 @@ public: * @param layout The layout from which to get the cell hierarchy * @param cell The starting cell * @param layers The layers from which to deliver the shapes + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, bool for_merged_input = false); /** * @brief Standard constructor for "world" iteration with a layer set @@ -237,8 +245,9 @@ public: * @param layout The layout from which to get the cell hierarchy * @param cell The starting cell * @param layers The layers from which to deliver the shapes + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, bool for_merged_input = false); /** * @brief Destructor @@ -427,6 +436,25 @@ public: } } + /** + * @brief Gets a flag indicating whether optimizing for merged input + */ + bool for_merged_input () const + { + return m_for_merged_input; + } + + /** + * @brief Sets a flag indicating whether optimizing for merged input + */ + void set_for_merged_input (bool f) + { + if (m_for_merged_input != f) { + m_for_merged_input = f; + m_needs_reinit = true; + } + } + /** * @brief Sets a global transformation * @@ -812,7 +840,7 @@ private: unsigned int m_shape_flags; const shape_iterator::property_selector *mp_shape_prop_sel; bool m_shape_inv_prop_sel; - bool m_overlapping; + bool m_overlapping, m_for_merged_input; std::set m_start, m_stop; cplx_trans_type m_global_trans; db::PropertiesTranslator m_property_translator; @@ -839,6 +867,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 bool m_needs_reinit; mutable size_t m_inst_quad_id; mutable std::vector m_inst_quad_id_stack; @@ -858,7 +887,8 @@ private: void new_cell (RecursiveShapeReceiver *receiver) const; void new_layer () const; void up (RecursiveShapeReceiver *receiver) const; - void down (RecursiveShapeReceiver *receiver) const; + bool down (RecursiveShapeReceiver *receiver) const; + void pop () const; bool is_outside_complex_region (const db::Box &box) const; diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 488fbc460..2d04a2758 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -31,6 +31,9 @@ #include "dbFlatEdges.h" #include "dbPolygonTools.h" #include "dbCompoundOperation.h" +#include "dbLayout.h" +#include "dbWriter.h" +#include "tlStream.h" #include "tlGlobPattern.h" // NOTE: include this to provide the symbols for "make_variant" @@ -74,14 +77,42 @@ Region &Region::operator= (const Region &other) return *this; } -Region::Region (const RecursiveShapeIterator &si) +Region::Region (const RecursiveShapeIterator &si, bool merged_semantics, bool is_merged) { - mp_delegate = new OriginalLayerRegion (si); + mp_delegate = new OriginalLayerRegion (si, db::ICplxTrans (), merged_semantics, is_merged); } -Region::Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics) +Region::Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged) { - mp_delegate = new OriginalLayerRegion (si, trans, merged_semantics); + mp_delegate = new OriginalLayerRegion (si, trans, merged_semantics, is_merged); +} + +Region::Region (const Shapes &shapes, bool merged_semantics, bool is_merged) +{ + db::FlatRegion *flat_region = new FlatRegion (is_merged); + flat_region->reserve (shapes.size (db::ShapeIterator::Regions)); + + // NOTE: we need to normalize the shapes to polygons because this is what the flat region expects + for (auto s = shapes.begin (db::ShapeIterator::Regions); ! s.at_end (); ++s) { + flat_region->insert (*s); + } + + mp_delegate = flat_region; + mp_delegate->set_merged_semantics (merged_semantics); +} + +Region::Region (const Shapes &shapes, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged) +{ + db::FlatRegion *flat_region = new FlatRegion (is_merged); + flat_region->reserve (shapes.size (db::ShapeIterator::Regions)); + + // NOTE: we need to normalize the shapes to polygons because this is what the flat region expects + for (auto s = shapes.begin (db::ShapeIterator::Regions); ! s.at_end (); ++s) { + flat_region->insert (*s, trans); + } + + mp_delegate = flat_region; + mp_delegate->set_merged_semantics (merged_semantics); } Region::Region (const RecursiveShapeIterator &si, DeepShapeStore &dss, double area_ratio, size_t max_vertex_count) @@ -101,6 +132,23 @@ Region::Region (DeepShapeStore &dss) mp_delegate = new db::DeepRegion (db::DeepLayer (&dss, layout_index, dss.layout (layout_index).insert_layer ())); } +void +Region::write (const std::string &fn) const +{ + // method provided for debugging purposes + + db::Layout layout; + const db::Cell &top = layout.cell (layout.add_cell ("REGION")); + unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); + insert_into (&layout, top.cell_index (), li); + + tl::OutputStream os (fn); + db::SaveLayoutOptions opt; + opt.set_format_from_filename (fn); + db::Writer writer (opt); + writer.write (layout, os); +} + const db::RecursiveShapeIterator & Region::iter () const { diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 39880b2ca..145018ef0 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -199,7 +199,7 @@ public: * Creates a region from a recursive shape iterator. This allows feeding a region * from a hierarchy of cells. */ - explicit Region (const RecursiveShapeIterator &si); + explicit Region (const RecursiveShapeIterator &si, bool merged_semantics = true, bool is_merged = false); /** * @brief Constructor from a RecursiveShapeIterator with a transformation @@ -208,7 +208,23 @@ public: * from a hierarchy of cells. The transformation is useful to scale to a specific * DBU for example. */ - explicit Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics = true); + explicit Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics = true, bool is_merged = false); + + /** + * @brief Constructor from a Shapes container + * + * Creates a region from a shapes container. + */ + explicit Region (const Shapes &si, bool merged_semantics = true, bool is_merged = false); + + /** + * @brief Constructor from a Shapes container with a transformation + * + * Creates a region from a recursive shape iterator. This allows feeding a region + * from a hierarchy of cells. The transformation is useful to scale to a specific + * DBU for example. + */ + explicit Region (const Shapes &si, const db::ICplxTrans &trans, bool merged_semantics = true, bool is_merged = false); /** * @brief Constructor from a RecursiveShapeIterator providing a deep representation @@ -232,6 +248,14 @@ public: */ explicit Region (DeepShapeStore &dss); + /** + * @brief Writes the region to a file + * + * This method is provided for debugging purposes. A flat image of the + * region is written to a layout file with a single top cell on layer 0/0. + */ + void write (const std::string &fn) const; + /** * @brief Implementation of the ShapeCollection interface */ diff --git a/src/db/db/dbShape.cc b/src/db/db/dbShape.cc index 47fc28776..e5c1d7820 100644 --- a/src/db/db/dbShape.cc +++ b/src/db/db/dbShape.cc @@ -807,6 +807,61 @@ Shape::box_type Shape::bbox () const } } +Shape::box_type Shape::rectangle () const +{ + if (is_box ()) { + return box (); + } + + switch (m_type) { + case db::Shape::Polygon: + return polygon ().is_box () ? polygon ().box () : box_type (); + case db::Shape::PolygonRef: + case db::Shape::PolygonPtrArrayMember: + return polygon_ref ().is_box () ? polygon_ref ().box () : box_type (); + case db::Shape::SimplePolygon: + return simple_polygon ().is_box () ? simple_polygon ().box () : box_type (); + case db::Shape::SimplePolygonRef: + case db::Shape::SimplePolygonPtrArrayMember: + return simple_polygon_ref ().is_box () ? simple_polygon_ref ().box () : box_type (); + case db::Shape::Path: + { + const path_type &p = path (); + if (! p.round () && p.points () <= 2 && p.points () > 0) { + point_type p1 = *p.begin (); + point_type p2 = p1; + if (p.points () == 2) { + p2 = *++p.begin (); + } + if (p1.x () == p2.x () || p1.y () == p2.y ()) { + return p.box (); + } + } + } + break; + case db::Shape::PathRef: + case db::Shape::PathPtrArrayMember: + { + const path_ref_type &p = path_ref (); + if (! p.ptr ()->round () && p.ptr ()->points () <= 2 && p.ptr ()->points () > 0) { + point_type p1 = *p.begin (); + point_type p2 = p1; + if (p.ptr ()->points () == 2) { + p2 = *++p.begin (); + } + if (p1.x () == p2.x () || p1.y () == p2.y ()) { + return p.box (); + } + } + } + break; + default: + break; + } + + return box_type (); +} + std::string Shape::to_string () const { diff --git a/src/db/db/dbShape.h b/src/db/db/dbShape.h index 5ab336667..dd003b47e 100644 --- a/src/db/db/dbShape.h +++ b/src/db/db/dbShape.h @@ -2651,6 +2651,16 @@ public: */ box_type bbox () const; + /** + * @brief Returns the box if the object represents a rectangle or an empty box if not + * + * This method returns the rectangle (aka box) the shape represents a polygon + * that is a rectangle, a path with two points and no rounded ends or an actual box. + * + * If not, an empty box is returned. + */ + box_type rectangle () const; + /** * @brief Compute the area of the shape */ diff --git a/src/db/db/dbShapeProcessor.cc b/src/db/db/dbShapeProcessor.cc index 518aaa9e7..974fd469b 100644 --- a/src/db/db/dbShapeProcessor.cc +++ b/src/db/db/dbShapeProcessor.cc @@ -53,6 +53,12 @@ ShapeProcessor::reserve (size_t n) m_processor.reserve (n); } +size_t +ShapeProcessor::count () const +{ + return m_processor.count (); +} + void ShapeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op) { diff --git a/src/db/db/dbShapeProcessor.h b/src/db/db/dbShapeProcessor.h index a7afde10c..333c53b69 100644 --- a/src/db/db/dbShapeProcessor.h +++ b/src/db/db/dbShapeProcessor.h @@ -196,6 +196,11 @@ public: */ void reserve (size_t n); + /** + * @brief Reports the number of edges stored in the processor + */ + size_t count () const; + /** * @brief Sets the base verbosity of the processor (see EdgeProcessor::set_base_verbosity for details) */ diff --git a/src/db/db/dbTexts.cc b/src/db/db/dbTexts.cc index 0b90c8897..388c89696 100644 --- a/src/db/db/dbTexts.cc +++ b/src/db/db/dbTexts.cc @@ -30,7 +30,9 @@ #include "dbOriginalLayerTexts.h" #include "dbEdges.h" #include "dbRegion.h" - +#include "dbLayout.h" +#include "dbWriter.h" +#include "tlStream.h" #include "tlVariant.h" #include @@ -90,6 +92,23 @@ Texts::Texts (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::I mp_delegate = new DeepTexts (si, dss, trans); } +void +Texts::write (const std::string &fn) const +{ + // method provided for debugging purposes + + db::Layout layout; + const db::Cell &top = layout.cell (layout.add_cell ("TEXTS")); + unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); + insert_into (&layout, top.cell_index (), li); + + tl::OutputStream os (fn); + db::SaveLayoutOptions opt; + opt.set_format_from_filename (fn); + db::Writer writer (opt); + writer.write (layout, os); +} + template void Texts::insert (const Sh &shape) { diff --git a/src/db/db/dbTexts.h b/src/db/db/dbTexts.h index 82d017187..cacda8589 100644 --- a/src/db/db/dbTexts.h +++ b/src/db/db/dbTexts.h @@ -181,6 +181,14 @@ public: */ explicit Texts (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans); + /** + * @brief Writes the text collection to a file + * + * This method is provided for debugging purposes. A flat image of the + * region is written to a layout file with a single top cell on layer 0/0. + */ + void write (const std::string &fn) const; + /** * @brief Implementation of the ShapeCollection interface */ diff --git a/src/db/db/gsiDeclDbBox.cc b/src/db/db/gsiDeclDbBox.cc index 50dc0d883..d2c5fcc8f 100644 --- a/src/db/db/gsiDeclDbBox.cc +++ b/src/db/db/gsiDeclDbBox.cc @@ -324,6 +324,21 @@ struct box_defs "\n" "@return The joined box\n" ) + + method ("-", &C::subtracted, gsi::arg ("box"), + "@brief Subtraction of boxes\n" + "\n" + "\n" + "The - operator subtracts the argument box from self.\n" + "This will return the bounding box of the are covered by self, but not by argument box. " + "Subtracting a box from itself will render an empty box. Subtracting another box from " + "self will modify the first box only if the argument box covers one side entirely.\n" + "\n" + "@param box The box to subtract from this box.\n" + "\n" + "@return The result box\n" + "\n" + "This feature has been introduced in version 0.29." + ) + method ("&", &C::intersection, gsi::arg ("box"), "@brief Returns the intersection of this box with another box\n" "\n" diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 4a22f7045..b4f6b5447 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -602,6 +602,12 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This constructor has been introduced in version 0.26." ) + + method ("write", &db::EdgePairs::write, gsi::arg ("filename"), + "@brief Writes the region to a file\n" + "This method is provided for debugging purposes. It writes the object to a flat layer 0/0 in a single top cell.\n" + "\n" + "This method has been introduced in version 0.29." + ) + method ("insert_into", &db::EdgePairs::insert_into, gsi::arg ("layout"), gsi::arg ("cell_index"), gsi::arg ("layer"), "@brief Inserts this edge pairs into the given layout, below the given cell and into the given layer.\n" "If the edge pair collection is a hierarchical one, a suitable hierarchy will be built below the top cell or " diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index d325dfa26..5fc83ec49 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -1567,6 +1567,12 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "This method has been added in version 0.28.\n" ) + + method ("write", &db::Edges::write, gsi::arg ("filename"), + "@brief Writes the region to a file\n" + "This method is provided for debugging purposes. It writes the object to a flat layer 0/0 in a single top cell.\n" + "\n" + "This method has been introduced in version 0.29." + ) + method ("clear", &db::Edges::clear, "@brief Clears the edge collection\n" ) + diff --git a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc index bc702af0f..50f840eee 100644 --- a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc +++ b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc @@ -476,13 +476,29 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS "\n" "This method has been introduced in version 0.23.\n" ) + - gsi::method ("overlapping=", &db::RecursiveShapeIterator::set_overlapping, gsi::arg ("region"), + gsi::method ("overlapping=", &db::RecursiveShapeIterator::set_overlapping, gsi::arg ("flag"), "@brief Sets a flag indicating whether overlapping shapes are selected when a region is used\n" "\n" "If this flag is false, shapes touching the search region are returned.\n" "\n" "This method has been introduced in version 0.23.\n" ) + + gsi::method ("for_merged_input?", &db::RecursiveShapeIterator::for_merged_input, + "@brief Gets a flag indicating whether iterator optimizes for merged input\n" + "\n" + "see \\for_merged_input= for details of this attribute.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + gsi::method ("for_merged_input=", &db::RecursiveShapeIterator::set_for_merged_input, gsi::arg ("flag"), + "@brief Sets a flag indicating whether iterator optimizes for merged input\n" + "\n" + "If this flag is set to true, the iterator is allowed to skip shapes it deems irrelevant " + "because they are covered entirely by other shapes. This allows shortcutting hierarchy traversal in " + "some cases.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + gsi::method ("unselect_all_cells", &db::RecursiveShapeIterator::unselect_all_cells, "@brief Unselects all cells.\n" "\n" diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index f978a7c4f..c5d6c6570 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -270,15 +270,6 @@ static db::Region *new_path (const db::Path &o) return new db::Region (o); } -static db::Region *new_shapes (const db::Shapes &s) -{ - db::Region *r = new db::Region (); - for (db::Shapes::shape_iterator i = s.begin (db::ShapeIterator::All); !i.at_end (); ++i) { - r->insert (*i); - } - return r; -} - 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)); @@ -329,16 +320,26 @@ static db::Region *new_si (const db::RecursiveShapeIterator &si) return new db::Region (si); } -static db::Region *new_sid (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, double area_ratio, size_t max_vertex_count) -{ - return new db::Region (si, dss, area_ratio, max_vertex_count); -} - static db::Region *new_si2 (const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans) { return new db::Region (si, trans); } +static db::Region *new_sis (const db::Shapes &si) +{ + return new db::Region (si); +} + +static db::Region *new_sis2 (const db::Shapes &si, const db::ICplxTrans &trans) +{ + return new db::Region (si, trans); +} + +static db::Region *new_sid (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, double area_ratio, size_t max_vertex_count) +{ + return new db::Region (si, dss, area_ratio, max_vertex_count); +} + static db::Region *new_sid2 (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, const db::ICplxTrans &trans, double area_ratio, size_t max_vertex_count) { return new db::Region (si, dss, trans, true, area_ratio, max_vertex_count); @@ -1088,13 +1089,6 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This constructor creates a region from a path.\n" ) + - constructor ("new", &new_shapes, gsi::arg ("shapes"), - "@brief Shapes constructor\n" - "\n" - "This constructor creates a region from a \\Shapes collection.\n" - "\n" - "This constructor has been introduced in version 0.25." - ) + constructor ("new", &new_si, gsi::arg ("shape_iterator"), "@brief Constructor from a hierarchical shape set\n" "\n" @@ -1126,6 +1120,24 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "r = RBA::Region::new(layout.begin_shapes(cell, layer), RBA::ICplxTrans::new(layout.dbu / dbu))\n" "@/code\n" ) + + constructor ("new", &new_sis, gsi::arg ("shapes"), + "@brief Constructor from a shapes container\n" + "\n" + "This constructor creates a region from the shapes container.\n" + "Text objects and edges are not inserted, because they cannot be converted to polygons.\n" + "This method allows feeding the shapes from a hierarchy of cells into the region.\n" + "\n" + "This constructor has been introduced in version 0.25 and extended in version 0.29." + ) + + constructor ("new", &new_sis2, gsi::arg ("shapes"), gsi::arg ("trans"), + "@brief Constructor from a shapes container with a transformation\n" + "\n" + "This constructor creates a region from the shapes container after applying the transformation.\n" + "Text objects and edges are not inserted, because they cannot be converted to polygons.\n" + "This method allows feeding the shapes from a hierarchy of cells into the region.\n" + "\n" + "This constructor variant has been introduced in version 0.29." + ) + constructor ("new", &new_sid, gsi::arg ("shape_iterator"), gsi::arg ("deep_shape_store"), gsi::arg ("area_ratio", 0.0), gsi::arg ("max_vertex_count", size_t (0)), "@brief Constructor for a deep region from a hierarchical shape set\n" "\n" @@ -1209,6 +1221,12 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This method has been introduced in version 0.26." ) + + method ("write", &db::Region::write, gsi::arg ("filename"), + "@brief Writes the region to a file\n" + "This method is provided for debugging purposes. It writes the object to a flat layer 0/0 in a single top cell.\n" + "\n" + "This method has been introduced in version 0.29." + ) + factory_ext ("texts", &texts_as_boxes1, gsi::arg ("expr", std::string ("*")), gsi::arg ("as_pattern", true), gsi::arg ("enl", 1), "@hide\n" "This method is provided for DRC implementation only." diff --git a/src/db/db/gsiDeclDbShape.cc b/src/db/db/gsiDeclDbShape.cc index c6671d9b7..68f6ff4fb 100644 --- a/src/db/db/gsiDeclDbShape.cc +++ b/src/db/db/gsiDeclDbShape.cc @@ -669,6 +669,26 @@ static tl::Variant get_dbox (const db::Shape *s) } } +static tl::Variant get_rectangle (const db::Shape *s) +{ + db::Shape::box_type b = s->rectangle (); + if (! b.empty ()) { + return tl::Variant (b); + } else { + return tl::Variant (); + } +} + +static tl::Variant get_drectangle (const db::Shape *s) +{ + db::Shape::box_type b = s->rectangle (); + if (! b.empty ()) { + return tl::Variant (db::CplxTrans (shape_dbu (s)) * b); + } else { + return tl::Variant (); + } +} + static tl::Variant get_edge (const db::Shape *s) { db::Shape::edge_type p; @@ -1982,6 +2002,22 @@ Class decl_Shape ("db", "Shape", "\n" "This method has been added in version 0.25.\n" ) + + gsi::method_ext ("rectangle", &get_rectangle, + "@brief Gets the rectangle if the object represents one or nil if not\n" + "\n" + "If the shape represents a rectangle - i.e. a box or box polygon, a path with two points and no round ends - " + "this method returns the box. If not, nil is returned.\n" + "\n" + "This method has been introduced in version 0.29." + ) + + gsi::method_ext ("drectangle", &get_drectangle, + "@brief Gets the rectangle in micron units if the object represents one or nil if not\n" + "\n" + "If the shape represents a rectangle - i.e. a box or box polygon, a path with two points and no round ends - " + "this method returns the box. If not, nil is returned.\n" + "\n" + "This method has been introduced in version 0.29." + ) + gsi::method ("is_user_object?", &db::Shape::is_user_object, "@brief Returns true if the shape is a user defined object\n" ) + diff --git a/src/db/db/gsiDeclDbTexts.cc b/src/db/db/gsiDeclDbTexts.cc index 8be9238b8..359249795 100644 --- a/src/db/db/gsiDeclDbTexts.cc +++ b/src/db/db/gsiDeclDbTexts.cc @@ -436,6 +436,12 @@ Class decl_Texts (decl_dbShapeCollection, "db", "Texts", "r = RBA::Texts::new(layout.begin_shapes(cell, layer), RBA::ICplxTrans::new(layout.dbu / dbu))\n" "@/code\n" ) + + method ("write", &db::Texts::write, gsi::arg ("filename"), + "@brief Writes the region to a file\n" + "This method is provided for debugging purposes. It writes the object to a flat layer 0/0 in a single top cell.\n" + "\n" + "This method has been introduced in version 0.29." + ) + method ("insert_into", &db::Texts::insert_into, gsi::arg ("layout"), gsi::arg ("cell_index"), gsi::arg ("layer"), "@brief Inserts this texts into the given layout, below the given cell and into the given layer.\n" "If the text collection is a hierarchical one, a suitable hierarchy will be built below the top cell or " diff --git a/src/db/unit_tests/dbBoxTests.cc b/src/db/unit_tests/dbBoxTests.cc index d70e942fc..1ac60f799 100644 --- a/src/db/unit_tests/dbBoxTests.cc +++ b/src/db/unit_tests/dbBoxTests.cc @@ -49,6 +49,17 @@ TEST(2) EXPECT_EQ (b & db::Box (110, 220, 120, 250), empty); EXPECT_EQ (b & db::Box (50, 100, 120, 250), db::Box (50, 100, 100, 200)); EXPECT_EQ (b & db::Box (50, 100, 60, 120), db::Box (50, 100, 60, 120)); + EXPECT_EQ (b - b, db::Box ()); + EXPECT_EQ (b - db::Box (), b); + EXPECT_EQ (db::Box () - b, db::Box ()); + EXPECT_EQ (db::Box () - db::Box (), db::Box ()); + EXPECT_EQ (b - db::Box (0, 0, 50, 50), b); + EXPECT_EQ (b - db::Box (0, 0, 50, 200), db::Box (50, 0, 100, 200)); + EXPECT_EQ (b - db::Box (50, 0, 100, 200), db::Box (0, 0, 50, 200)); + EXPECT_EQ (b - db::Box (0, 0, 100, 100), db::Box (0, 100, 100, 200)); + EXPECT_EQ (b - db::Box (0, 100, 100, 200), db::Box (0, 0, 100, 100)); + EXPECT_EQ (db::Box::world () - b, db::Box::world ()); + EXPECT_EQ (b - db::Box::world (), db::Box ()); empty.move (db::Vector (10, 20)); EXPECT_EQ (empty == db::Box (), true); diff --git a/src/db/unit_tests/dbHierProcessorTests.cc b/src/db/unit_tests/dbHierProcessorTests.cc index 23833d2ff..4679c5951 100644 --- a/src/db/unit_tests/dbHierProcessorTests.cc +++ b/src/db/unit_tests/dbHierProcessorTests.cc @@ -23,6 +23,7 @@ #include "tlUnitTest.h" #include "tlStream.h" +#include "tlFileUtils.h" #include "dbHierProcessor.h" #include "dbTestSupport.h" #include "dbReader.h" @@ -32,6 +33,9 @@ #include "dbLocalOperationUtils.h" #include "dbRegionLocalOperations.h" #include "dbPolygon.h" +#include "dbRecursiveInstanceIterator.h" +#include "dbDeepShapeStore.h" +#include "dbRegion.h" static std::string testdata (const std::string &fn) { @@ -1284,3 +1288,87 @@ TEST(Arrays) run_test_bool2 (_this, "hlp18.oas", TMNot, 100); } +TEST(XORTool) +{ + test_is_long_runner (); + + std::string fna (tl::combine_path (tl::testdata_private (), "xor/a.gds.gz")); + std::string fnb (tl::combine_path (tl::testdata_private (), "xor/b.gds.gz")); + std::string fn_au (tl::combine_path (tl::testdata_private (), "xor/xor_au.oas.gz")); + + db::Layout lya, lyb; + + unsigned int l1, l2; + + db::LayerMap lmap; + + lmap.map (db::LDPair (1, 0), l1 = lya.insert_layer ()); + lyb.insert_layer (); + + lmap.map (db::LDPair (2, 0), l2 = lya.insert_layer ()); + lyb.insert_layer (); + + { + tl::InputStream stream (fna); + db::Reader reader (stream); + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + reader.read (lya, options); + } + + { + tl::InputStream stream (fnb); + db::Reader reader (stream); + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + reader.read (lyb, options); + } + + db::Layout ly_out; + db::cell_index_type top_out = ly_out.add_cell ("TOP"); + unsigned int l1_out = ly_out.insert_layer (db::LayerProperties (1, 0)); + unsigned int l2_out = ly_out.insert_layer (db::LayerProperties (2, 0)); + + db::DeepShapeStore dss; + dss.set_wants_all_cells (true); // saves time for less cell mapping operations + + { + db::RecursiveShapeIterator ri_a, ri_b; + + ri_a = db::RecursiveShapeIterator (lya, lya.cell (*lya.begin_top_down ()), l1); + ri_a.set_for_merged_input (true); + + ri_b = db::RecursiveShapeIterator (lyb, lyb.cell (*lyb.begin_top_down ()), l1); + ri_b.set_for_merged_input (true); + + db::Region in_a (ri_a, dss, db::ICplxTrans (1.0)); + db::Region in_b (ri_b, dss, db::ICplxTrans (1.0)); + + db::Region xor_res = in_a ^ in_b; + EXPECT_EQ (xor_res.count (), size_t (12)); + + xor_res.insert_into (&ly_out, top_out, l1_out); + } + + { + db::RecursiveShapeIterator ri_a, ri_b; + + ri_a = db::RecursiveShapeIterator (lya, lya.cell (*lya.begin_top_down ()), l2); + ri_a.set_for_merged_input (true); + + ri_b = db::RecursiveShapeIterator (lyb, lyb.cell (*lyb.begin_top_down ()), l2); + ri_b.set_for_merged_input (true); + + db::Region in_a (ri_a, dss, db::ICplxTrans (1.0)); + db::Region in_b (ri_b, dss, db::ICplxTrans (1.0)); + + db::Region xor_res = in_a ^ in_b; + EXPECT_EQ (xor_res.count (), size_t (15984)); + + xor_res.insert_into (&ly_out, top_out, l2_out); + } + + db::compare_layouts (_this, ly_out, fn_au, db::WriteOAS); +} diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index c89af7605..6184176db 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -26,6 +26,10 @@ #include "dbLayoutDiff.h" #include "tlString.h" #include "tlUnitTest.h" +#include "tlFileUtils.h" +#include "tlStream.h" +#include "dbReader.h" +#include "dbWriter.h" #include @@ -1554,3 +1558,225 @@ TEST(11_LayoutIsWeakPointer) x = collect(i1, *g); EXPECT_EQ (x, ""); } + +TEST(12_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::Cell &c2 (g->cell (g->add_cell ())); + db::Cell &c3 (g->cell (g->add_cell ())); + + db::Box b (0, 100, 1000, 1200); + c0.shapes (0).insert (db::Box (0, 0, 3000, 2000)); + c1.shapes (0).insert (b); + c2.shapes (0).insert (b); + c3.shapes (0).insert (b); + + db::Trans tt; + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt)); + c0.insert (db::CellInstArray (db::CellInst (c2.cell_index ()), db::Trans (db::Vector (100, -100)))); + c0.insert (db::CellInstArray (db::CellInst (c3.cell_index ()), db::Trans (1))); + c2.insert (db::CellInstArray (db::CellInst (c3.cell_index ()), db::Trans (db::Vector (1100, 0)))); + + std::string x; + + 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)"); + + i1.set_for_merged_input (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + + 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)"); + + i1.set_for_merged_input (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + + 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)"); + + 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)"); + + 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)"); + + i1.set_for_merged_input (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + + 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)"); + + i1.set_for_merged_input (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); +} + + +TEST(13_ForMergedPerformance) +{ + test_is_long_runner (); + + std::string fn (tl::combine_path (tl::testdata_private (), "oasis/caravel.oas.gz")); + + db::Layout ly; + + { + tl::InputStream is (fn); + db::Reader reader (is); + reader.read (ly); + } + + unsigned l1 = ly.get_layer (db::LayerProperties (66, 20)); + unsigned l2 = ly.get_layer (db::LayerProperties (235, 4)); + + db::RecursiveShapeIterator si1 (ly, ly.cell (*ly.begin_top_down ()), l1); + db::RecursiveShapeIterator si2 (ly, ly.cell (*ly.begin_top_down ()), l2); + + size_t n1_expected_full = db::default_editable_mode () ? 1203072 : 1203078; + size_t n2_expected_full = 10; + + { + tl::SelfTimer timer ("Standard loop on 66/20"); + size_t n = 0; + while (! si1.at_end ()) { + ++si1; + ++n; + } + tl::info << "Counted " << n << " shapes on 66/20"; + EXPECT_EQ (n, size_t (1218378)); + } + + { + tl::SelfTimer timer ("Standard loop on 235/4"); + size_t n = 0; + while (! si2.at_end ()) { + ++si2; + ++n; + } + tl::info << "Counted " << n << " shapes on 235/4"; + EXPECT_EQ (n, size_t (57462)); + } + + si1.set_for_merged_input (true); + si2.set_for_merged_input (true); + + { + tl::SelfTimer timer ("'for_merged' loop on 66/20"); + size_t n = 0; + while (! si1.at_end ()) { + ++si1; + ++n; + } + tl::info << "Counted " << n << " shapes on 66/20"; + EXPECT_EQ (n, size_t (n1_expected_full)); + } + + { + tl::SelfTimer timer ("'for_merged' loop on 235/4"); + size_t n = 0; + while (! si2.at_end ()) { + ++si2; + ++n; + } + tl::info << "Counted " << n << " shapes on 235/4"; + EXPECT_EQ (n, size_t (n2_expected_full)); + } + + si1.set_for_merged_input (false); + si1.set_region (db::Box (0, 0, 1000000, 1000000)); + si2.set_for_merged_input (false); + si2.set_region (db::Box (0, 0, 1000000, 1000000)); + + { + tl::SelfTimer timer ("Standard loop on 66/20"); + size_t n = 0; + while (! si1.at_end ()) { + ++si1; + ++n; + } + tl::info << "Counted " << n << " shapes on 66/20"; + EXPECT_EQ (n, size_t (218823)); + } + + { + tl::SelfTimer timer ("Standard loop on 235/4"); + size_t n = 0; + while (! si2.at_end ()) { + ++si2; + ++n; + } + tl::info << "Counted " << n << " shapes on 235/4"; + EXPECT_EQ (n, size_t (2578)); + } + + si1.set_for_merged_input (true); + si2.set_for_merged_input (true); + + size_t n1_expected = db::default_editable_mode () ? 218068 : 218069; + size_t n2_expected = 2; + + { + tl::SelfTimer timer ("'for_merged' loop on 66/20"); + size_t n = 0; + while (! si1.at_end ()) { + ++si1; + ++n; + } + tl::info << "Counted " << n << " shapes on 66/20"; + EXPECT_EQ (n, size_t (n1_expected)); + } + + { + tl::SelfTimer timer ("'for_merged' loop on 235/4"); + size_t n = 0; + while (! si2.at_end ()) { + ++si2; + ++n; + } + tl::info << "Counted " << n << " shapes on 235/4"; + EXPECT_EQ (n, size_t (n2_expected)); + } + + { + tl::SelfTimer timer ("XOR on tile of 66/20"); + si1.set_for_merged_input (false); + db::Region r1 (si1); + si1.set_for_merged_input (true); + db::Region r2 (si1); + + EXPECT_EQ (r1.count (), size_t (218823)); + EXPECT_EQ (r2.count (), size_t (n1_expected)); + EXPECT_EQ ((r1 ^ r2).count (), size_t (0)); + } + + { + tl::SelfTimer timer ("XOR on tile of 235/4"); + si2.set_for_merged_input (false); + db::Region r1 (si2); + si2.set_for_merged_input (true); + db::Region r2 (si2); + + EXPECT_EQ (r1.count (), size_t (2578)); + EXPECT_EQ (r2.count (), size_t (n2_expected)); + EXPECT_EQ ((r1 ^ r2).count (), size_t (0)); + } +} + diff --git a/src/db/unit_tests/dbRegionTests.cc b/src/db/unit_tests/dbRegionTests.cc index 554f25956..70ac453b2 100644 --- a/src/db/unit_tests/dbRegionTests.cc +++ b/src/db/unit_tests/dbRegionTests.cc @@ -2537,6 +2537,35 @@ TEST(55_PropertiesFilterFlat) EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)"); } +TEST(56_RegionsFromShapes) +{ + db::Shapes shapes; + + shapes.insert (db::Box (0, 0, 100, 200)); + shapes.insert (db::Box (50, 50, 150, 250)); + + EXPECT_EQ (db::Region (shapes).area (), 32500); + EXPECT_EQ (db::Region (shapes, false).area (), 40000); + EXPECT_EQ (db::Region (shapes, db::ICplxTrans (0.5)).area (), 8125); + EXPECT_EQ (db::Region (shapes, db::ICplxTrans (0.5), false).area (), 10000); + + // for cross-checking: same for RecursiveShapeIterator + + db::Layout layout; + unsigned int l1 = layout.insert_layer (); + db::Cell &top = layout.cell (layout.add_cell ("TOP")); + + top.shapes (l1).insert (db::Box (0, 0, 100, 200)); + top.shapes (l1).insert (db::Box (50, 50, 150, 250)); + + db::RecursiveShapeIterator si (layout, top, l1); + + EXPECT_EQ (db::Region (si).area (), 32500); + EXPECT_EQ (db::Region (si, false).area (), 40000); + EXPECT_EQ (db::Region (si, db::ICplxTrans (0.5)).area (), 8125); + EXPECT_EQ (db::Region (si, db::ICplxTrans (0.5), false).area (), 10000); +} + TEST(100_Processors) { db::Region r; diff --git a/src/db/unit_tests/dbShapeTests.cc b/src/db/unit_tests/dbShapeTests.cc index b6f8b514c..1d4a19003 100644 --- a/src/db/unit_tests/dbShapeTests.cc +++ b/src/db/unit_tests/dbShapeTests.cc @@ -948,3 +948,89 @@ TEST(9) EXPECT_EQ (si.at_end (), true); } +// Rectangle +TEST(10) +{ + db::Manager m (true); + db::Shapes s (&m, 0, db::default_editable_mode ()); + db::ShapeIterator si; + + s.insert (db::Point (100, 200)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::Edge (db::Point (100, 200), db::Point (200, 400))); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::EdgePair (db::Edge (db::Point (100, 200), db::Point (200, 400)), db::Edge (db::Point (0, 300), db::Point (100, 500)))); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::Box (0, 0, 1000, 2000)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (0, 0, 1000, 2000)); + + s.clear (); + s.insert (db::ShortBox (0, 0, 1000, 2000)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (0, 0, 1000, 2000)); + + s.clear (); + s.insert (db::Polygon (db::Box (0, 0, 1000, 2000))); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (0, 0, 1000, 2000)); + + s.clear (); + s.insert (db::Polygon ()); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::SimplePolygon (db::Box (0, 0, 1000, 2000))); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (0, 0, 1000, 2000)); + + s.clear (); + s.insert (db::SimplePolygon ()); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::Path ()); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + db::Point pts1 [1] = { db::Point (0, 0) }; + db::Point pts2 [2] = { db::Point (0, 0), db::Point (1000, 0) }; + db::Point pts2b [2] = { db::Point (0, 0), db::Point (1000, 1000) }; + db::Point pts3 [3] = { db::Point (0, 0), db::Point (1000, 0), db::Point (1000, 1000) }; + + s.clear (); + s.insert (db::Path (pts1 + 0, pts1 + 1, 1000, 500, 500)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (-500, -500, 500, 500)); + + s.clear (); + s.insert (db::Path (pts2 + 0, pts2 + 2, 1000, 500, 500)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (-500, -500, 1500, 500)); + + s.clear (); + s.insert (db::Path (pts2 + 0, pts2 + 2, 1000, 500, 500, true)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::Path (pts2b + 0, pts2b + 2, 1000, 500, 500)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::Path (pts3 + 0, pts3 + 3, 1000, 500, 500)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); +} diff --git a/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc b/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc index d78cbc9cf..83d2c66e1 100644 --- a/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc +++ b/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc @@ -52,6 +52,12 @@ namespace lay { +bool merge_before_bool () +{ + // $KLAYOUT_XOR_MERGE_BEFORE_BOOLEAN + return tl::app_flag ("xor-merge-before-boolean"); +} + std::string cfg_xor_input_mode ("xor-input-mode"); std::string cfg_xor_output_mode ("xor-output-mode"); std::string cfg_xor_nworkers ("xor-num-workers"); @@ -766,6 +772,9 @@ XORWorker::do_perform_deep (const XORTask *xor_task) db::RecursiveShapeIterator s_a (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la, xor_task->region_a ()); db::RecursiveShapeIterator s_b (mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb, xor_task->region_b ()); + s_a.set_for_merged_input (true); + s_b.set_for_merged_input (true); + db::Region ra (s_a, dss, db::ICplxTrans (mp_job->cva ()->layout ().dbu () / mp_job->dbu ())); db::Region rb (s_b, dss, db::ICplxTrans (mp_job->cvb ()->layout ().dbu () / mp_job->dbu ())); @@ -794,6 +803,8 @@ XORWorker::do_perform_deep (const XORTask *xor_task) dbu_scale = db::ICplxTrans (mp_job->cvb ()->layout ().dbu () / mp_job->dbu ()); } + s.set_for_merged_input (true); + rr = db::Region (s, dss, dbu_scale); } @@ -861,40 +872,11 @@ XORWorker::do_perform_tiled (const XORTask *xor_task) if ((!la.empty () && !lb.empty ()) || mp_job->el_handling () == XORJob::EL_process) { - if (! mp_job->has_tiles ()) { + tl::SelfTimer timer (tl::verbosity () >= 31, "Boolean part"); + size_t n; - tl::SelfTimer timer (tl::verbosity () >= 21, "Boolean part"); -#if 0 - // Straightforward implementation - sp.boolean (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la, - mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb, - xor_results_cell.shapes (0), op, true, false, true); -#else - // This implementation is faster when a lot of overlapping shapes are involved - db::Layout merge_helper; - db::Cell &merge_helper_cell = merge_helper.cell (merge_helper.add_cell ()); - merge_helper.insert_layer (0); - merge_helper.insert_layer (1); + if (! merge_before_bool ()) { - if (!la.empty ()) { - sp.merge (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la, - merge_helper_cell.shapes (0), true, 0, false, true); - } - if (!lb.empty ()) { - sp.merge (mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb, - merge_helper_cell.shapes (1), true, 0, false, true); - } - sp.boolean (merge_helper, merge_helper_cell, 0, - merge_helper, merge_helper_cell, 1, - xor_results_cell.shapes (0), mp_job->op (), true, false, true); -#endif - - } else { - - tl::SelfTimer timer (tl::verbosity () >= 31, "Boolean part"); - size_t n; - -#if 0 // Straightforward implementation sp.clear (); @@ -902,20 +884,36 @@ XORWorker::do_perform_tiled (const XORTask *xor_task) db::CplxTrans dbu_scale_b (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); n = 0; - for (db::RecursiveShapeIterator s (mp_job->cva ()->layout (), mp_job->cva ().cell (), la, region_a); ! s.at_end (); ++s, ++n) { - sp.insert (s.shape (), dbu_scale_a * s.trans (), n * 2); + db::RecursiveShapeIterator s_a; + if (mp_job->has_tiles ()) { + s_a = db::RecursiveShapeIterator (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la, xor_task->region_a ()); + } else { + s_a = db::RecursiveShapeIterator (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la); + } + s_a.set_for_merged_input (true); + for ( ; ! s_a.at_end (); ++s_a, ++n) { + sp.insert (s_a.shape (), dbu_scale_a * s_a.trans (), n * 2); } n = 0; - for (db::RecursiveShapeIterator s (mp_job->cvb ()->layout (), mp_job->cvb ().cell (), lb, region_b); ! s.at_end (); ++s, ++n) { - sp.insert (s.shape (), dbu_scale_b * s.trans (), n * 2 + 1); + db::RecursiveShapeIterator s_b; + if (mp_job->has_tiles ()) { + s_b = db::RecursiveShapeIterator (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb, xor_task->region_b ()); + } else { + s_b = db::RecursiveShapeIterator (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb); + } + s_b.set_for_merged_input (true); + for (; ! s_b.at_end (); ++s_b, ++n) { + sp.insert (s_b.shape (), dbu_scale_b * s_b.trans (), n * 2 + 1); } db::BooleanOp bool_op (mp_job->op ()); db::ShapeGenerator sg (xor_results_cell.shapes (0), true /*clear shapes*/); db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); - sp.process (out, mp_job->op ()); -#else + sp.process (out, bool_op); + + } else { + // This implementation is faster when a lot of overlapping shapes are involved db::Layout merge_helper; merge_helper.dbu (mp_job->dbu ()); @@ -931,7 +929,14 @@ XORWorker::do_perform_tiled (const XORTask *xor_task) db::CplxTrans dbu_scale (mp_job->cva ()->layout ().dbu () / xor_results.dbu ()); n = 0; - for (db::RecursiveShapeIterator s (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la, xor_task->region_a ()); ! s.at_end (); ++s, ++n) { + db::RecursiveShapeIterator s; + if (mp_job->has_tiles ()) { + s = db::RecursiveShapeIterator (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la, xor_task->region_a ()); + } else { + s = db::RecursiveShapeIterator (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la); + } + s.set_for_merged_input (true); + for ( ; ! s.at_end (); ++s, ++n) { sp.insert (s.shape (), dbu_scale * s.trans (), n); } @@ -949,7 +954,14 @@ XORWorker::do_perform_tiled (const XORTask *xor_task) db::CplxTrans dbu_scale (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); n = 0; - for (db::RecursiveShapeIterator s (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb, xor_task->region_b ()); ! s.at_end (); ++s, ++n) { + db::RecursiveShapeIterator s; + if (mp_job->has_tiles ()) { + s = db::RecursiveShapeIterator (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb, xor_task->region_b ()); + } else { + s = db::RecursiveShapeIterator (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb); + } + s.set_for_merged_input (true); + for ( ; ! s.at_end (); ++s, ++n) { sp.insert (s.shape (), dbu_scale * s.trans (), n); } @@ -960,10 +972,9 @@ XORWorker::do_perform_tiled (const XORTask *xor_task) } - sp.boolean (merge_helper, merge_helper_cell, 0, - merge_helper, merge_helper_cell, 1, + sp.boolean (merge_helper, merge_helper_cell, 0, + merge_helper, merge_helper_cell, 1, xor_results_cell.shapes (0), mp_job->op (), true, false, true); -#endif } @@ -990,6 +1001,8 @@ XORWorker::do_perform_tiled (const XORTask *xor_task) dbu_scale = db::CplxTrans (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); } + s.set_for_merged_input (true); + for (; ! s.at_end (); ++s) { if (s->is_polygon () || s->is_box () || s->is_path ()) { db::Polygon p; diff --git a/src/tl/tl/tlOptional.cc b/src/tl/tl/tlOptional.cc index ba2774439..d7ee370b2 100644 --- a/src/tl/tl/tlOptional.cc +++ b/src/tl/tl/tlOptional.cc @@ -25,6 +25,6 @@ namespace tl { -extern const nullopt_t nullopt = nullopt_t (); +const nullopt_t nullopt = nullopt_t (); } // namespace tl diff --git a/src/tl/tl/tlOptional.h b/src/tl/tl/tlOptional.h index c30fa6679..3893d6ac0 100644 --- a/src/tl/tl/tlOptional.h +++ b/src/tl/tl/tlOptional.h @@ -34,7 +34,7 @@ namespace tl struct nullopt_t {}; -extern const nullopt_t nullopt; +extern TL_PUBLIC const nullopt_t nullopt; /** * @brief Poor man's partial implementation of C++17's std::optional diff --git a/testdata/ruby/dbBoxTest.rb b/testdata/ruby/dbBoxTest.rb index 8e9a916e9..1a207ef81 100644 --- a/testdata/ruby/dbBoxTest.rb +++ b/testdata/ruby/dbBoxTest.rb @@ -146,6 +146,9 @@ class DBBox_TestClass < TestBase assert_equal( a + b, b ) assert_equal( (b + c).to_s, "(1,-10;22,22)" ) + assert_equal( b - a, b ) + assert_equal( (b - c).to_s, "(1,-1;17,22)" ) + assert_equal( a + RBA::DPoint::new( 1, -5 ), RBA::DBox::new( 1, -5, 1, -5 ) ) assert_equal( (b + RBA::DPoint::new( 1, -5 )).to_s, "(1,-5;17,22)" ) diff --git a/testdata/ruby/dbRecursiveShapeIterator.rb b/testdata/ruby/dbRecursiveShapeIterator.rb index 29724541c..f4d2d9b9b 100644 --- a/testdata/ruby/dbRecursiveShapeIterator.rb +++ b/testdata/ruby/dbRecursiveShapeIterator.rb @@ -322,6 +322,38 @@ END end + def test_3 + + l = RBA::Layout.new + l.insert_layer_at(0, RBA::LayerInfo.new(1, 0)) + l.insert_layer_at(1, RBA::LayerInfo.new(2, 0)) + c0 = l.cell(l.add_cell("c0")) + c1 = l.cell(l.add_cell("c1")) + c2 = l.cell(l.add_cell("c2")) + c3 = l.cell(l.add_cell("c3")) + + b = RBA::Box.new(0, 100, 1000, 1200) + c0.shapes(0).insert(RBA::Box.new(0, 0, 3000, 2000)) + c1.shapes(0).insert(b) + c2.shapes(0).insert(b) + c3.shapes(0).insert(b) + + tt = RBA::Trans.new + c0.insert(RBA::CellInstArray.new(c1.cell_index, tt)) + c0.insert(RBA::CellInstArray.new(c2.cell_index, RBA::Trans.new(RBA::Vector.new(100, -100)))) + c0.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(1))) + c2.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(RBA::Vector.new(1100, 0)))) + + ii = RBA::RecursiveShapeIterator.new(l, c0, 0) + assert_equal(ii.for_merged_input, false) + assert_equal(collect(ii, l), "[c0](0,0;3000,2000)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)/[c3](1200,0;2200,1100)/[c3](-1200,0;-100,1000)") + + ii.for_merged_input = true + assert_equal(ii.for_merged_input, true) + assert_equal(collect(ii, l), "[c0](0,0;3000,2000)/[c3](-1200,0;-100,1000)") + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 0d81b5efd..8c59d587c 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -1066,6 +1066,47 @@ class DBRegion_TestClass < TestBase end + # regions from Shapes + def test_regions_from_shapes + + shapes = RBA::Shapes::new; + + shapes.insert(RBA::Box::new(0, 0, 100, 200)) + shapes.insert(RBA::Box::new(50, 50, 150, 250)) + + assert_equal(RBA::Region::new(shapes).area, 32500) + region = RBA::Region::new(shapes) + region.merged_semantics = false + assert_equal(region.area, 40000) + + assert_equal(RBA::Region::new(shapes, RBA::ICplxTrans::new(0.5)).area, 8125) + region = RBA::Region::new(shapes, RBA::ICplxTrans::new(0.5)) + region.merged_semantics = false + assert_equal(region.area, 10000) + + # for cross-checking: same for RecursiveShapeIterator + + layout = RBA::Layout::new + l1 = layout.insert_layer(RBA::LayerInfo::new(1, 0)) + top = layout.create_cell("TOP") + + top.shapes(l1).insert (RBA::Box::new(0, 0, 100, 200)) + top.shapes(l1).insert (RBA::Box::new(50, 50, 150, 250)) + + si = RBA::RecursiveShapeIterator::new(layout, top, l1) + + assert_equal(RBA::Region::new(si).area, 32500) + region = RBA::Region::new(si) + region.merged_semantics = false + assert_equal(region.area, 40000) + + assert_equal(RBA::Region::new(si, RBA::ICplxTrans::new(0.5)).area, 8125) + region = RBA::Region::new(si, RBA::ICplxTrans::new(0.5)) + region.merged_semantics = false + assert_equal(region.area, 10000) + + end + # deep region tests def test_deep1 diff --git a/testdata/ruby/dbShapesTest.rb b/testdata/ruby/dbShapesTest.rb index 3417c8688..bca7672cf 100644 --- a/testdata/ruby/dbShapesTest.rb +++ b/testdata/ruby/dbShapesTest.rb @@ -178,6 +178,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].is_polygon?, false ) assert_equal( arr[0].is_box?, true ) assert_equal( arr[0].box.to_s, "(10,-10;50,40)" ) + assert_equal( arr[0].rectangle.to_s, "(10,-10;50,40)" ) assert_equal( arr[0].bbox.to_s, "(10,-10;50,40)" ) # edges @@ -198,6 +199,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].edge.to_s, "(-1,2;5,2)" ) assert_equal( arr[0].edge_pair.inspect, "nil" ) assert_equal( arr[0].box.inspect, "nil" ) + assert_equal( arr[0].rectangle.inspect, "nil" ) assert_equal( arr[0].path.inspect, "nil" ) assert_equal( arr[0].text.inspect, "nil" ) assert_equal( arr[0].edge == a, true ) @@ -533,6 +535,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].dedge.inspect, "nil" ) assert_equal( arr[0].dedge_pair.inspect, "nil" ) assert_equal( arr[0].dbox.to_s, "(0.01,-0.01;0.05,0.04)" ) + assert_equal( arr[0].drectangle.to_s, "(0.01,-0.01;0.05,0.04)" ) assert_equal( arr[0].dpath.inspect, "nil" ) assert_equal( arr[0].dtext.inspect, "nil" ) assert_equal( arr[0].is_polygon?, false ) @@ -557,6 +560,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].dedge.to_s, "(-0.001,0.002;0.005,0.002)" ) assert_equal( arr[0].dedge_pair.inspect, "nil" ) assert_equal( arr[0].dbox.inspect, "nil" ) + assert_equal( arr[0].drectangle.inspect, "nil" ) assert_equal( arr[0].dpath.inspect, "nil" ) assert_equal( arr[0].dtext.inspect, "nil" ) assert_equal( arr[0].dbbox.to_s, "(-0.001,0.002;0.005,0.002)" )