diff --git a/src/db/db/dbBoxConvert.cc b/src/db/db/dbBoxConvert.cc index 7d84fba8f..aab627004 100644 --- a/src/db/db/dbBoxConvert.cc +++ b/src/db/db/dbBoxConvert.cc @@ -36,12 +36,7 @@ DB_PUBLIC db::Box cellinst_box_convert_impl (const db::CellInst &inst, const db: } else if (allow_empty) { return inst.bbox (*layout); } else { - db::Box box = inst.bbox (*layout); - if (box.empty ()) { - return db::Box (db::Point (0, 0), db::Point (0, 0)); - } else { - return box; - } + return inst.bbox_with_empty (*layout); } } @@ -52,11 +47,7 @@ DB_PUBLIC db::Box cell_box_convert_impl (const db::Cell &c, int layer, bool allo } else if (allow_empty) { return c.bbox (); } else { - if (c.bbox ().empty ()) { - return db::Box (db::Point (0, 0), db::Point (0, 0)); - } else { - return c.bbox (); - } + return c.bbox_with_empty (); } } diff --git a/src/db/db/dbCell.cc b/src/db/db/dbCell.cc index 9bce2c24b..1a465f47c 100644 --- a/src/db/db/dbCell.cc +++ b/src/db/db/dbCell.cc @@ -96,7 +96,7 @@ Cell::Cell (cell_index_type ci, db::Layout &l) m_bbox_needs_update (false), m_locked (false), m_ghost_cell (false), mp_last (0), mp_next (0) { - // .. nothing yet + m_bbox_with_empty = box_type (box_type::point_type (), box_type::point_type ()); } Cell::Cell (const Cell &d) @@ -128,6 +128,7 @@ Cell::operator= (const Cell &d) m_locked = d.m_locked; m_instances = d.m_instances; m_bbox = d.m_bbox; + m_bbox_with_empty = d.m_bbox_with_empty; m_bboxes = d.m_bboxes; m_hier_levels = d.m_hier_levels; m_prop_id = d.m_prop_id; @@ -282,6 +283,10 @@ Cell::update_bbox (unsigned int layers) box_type org_bbox = m_bbox; m_bbox = box_type (); + // determine the bounding box with empty cells + box_type org_bbox_with_empty = m_bbox_with_empty; + m_bbox_with_empty = box_type (); + // save the original boxes for simple compare box_map org_bboxes; org_bboxes.swap (m_bboxes); @@ -313,16 +318,21 @@ Cell::update_bbox (unsigned int layers) m_bbox += lbox; box_map::iterator b = m_bboxes.find (l); if (b == m_bboxes.end ()) { - m_bboxes.insert (std::make_pair (l, lbox)); + m_bboxes.insert (std::make_pair (l, lbox)); } else { - b->second += lbox; + b->second += lbox; } } } + + db::box_convert bc_we (*mp_layout); + m_bbox_with_empty += o1_inst->bbox_from_raw_bbox (raw_box, bc_we); } + box_type sbox_all; + // update the bboxes of the shapes lists for (shapes_map::iterator s = m_shapes_map.begin (); s != m_shapes_map.end (); ++s) { @@ -331,7 +341,7 @@ Cell::update_bbox (unsigned int layers) box_type sbox (s->second.bbox ()); if (! sbox.empty ()) { - m_bbox += sbox; + sbox_all += sbox; box_map::iterator b = m_bboxes.find (s->first); if (b == m_bboxes.end ()) { m_bboxes.insert (std::make_pair (s->first, sbox)); @@ -342,12 +352,20 @@ Cell::update_bbox (unsigned int layers) } + // combine shapes in all-layer boxes + m_bbox += sbox_all; + m_bbox_with_empty += sbox_all; + + // no empty box + if (m_bbox_with_empty.empty ()) { + m_bbox_with_empty = box_type (box_type::point_type (), box_type::point_type ()); + } + // reset "dirty child instances" flag m_bbox_needs_update = false; // return true, if anything has changed with the box - return (org_bbox != m_bbox || org_bboxes != m_bboxes); - + return (org_bbox != m_bbox || org_bbox_with_empty != m_bbox_with_empty || org_bboxes != m_bboxes); } void @@ -442,6 +460,13 @@ Cell::prop_id (db::properties_id_type id) } } +const Cell::box_type & +Cell::bbox_with_empty () const +{ + mp_layout->update (); + return m_bbox_with_empty; +} + const Cell::box_type & Cell::bbox () const { diff --git a/src/db/db/dbCell.h b/src/db/db/dbCell.h index 0240ab5a9..044f917f3 100644 --- a/src/db/db/dbCell.h +++ b/src/db/db/dbCell.h @@ -536,6 +536,17 @@ class DB_PUBLIC Cell */ const box_type &bbox () const; + /** + * @brief Retrieve the bounding box of the cell, including empty cells + * + * This method behaves like "bbox", but includes empty cells as single-point + * boxes (0,0;0,0). This bounding box is used for drawing and allows + * including empty cells. + * + * @return The bounding box that was computed by update_bbox + */ + const box_type &bbox_with_empty () const; + /** * @brief Retrieve the per-layer bounding box of the cell * @@ -1098,7 +1109,7 @@ class DB_PUBLIC Cell mutable db::Layout *mp_layout; shapes_map m_shapes_map; instances_type m_instances; - box_type m_bbox; + box_type m_bbox, m_bbox_with_empty; box_map m_bboxes; db::properties_id_type m_prop_id; diff --git a/src/db/db/dbCellInst.cc b/src/db/db/dbCellInst.cc index fdc5ccf1d..c6a89568e 100644 --- a/src/db/db/dbCellInst.cc +++ b/src/db/db/dbCellInst.cc @@ -33,6 +33,12 @@ CellInst::bbox (const db::Layout &g) const return g.cell (m_cell_index).bbox (); } +CellInst::box_type +CellInst::bbox_with_empty (const db::Layout &g) const +{ + return g.cell (m_cell_index).bbox_with_empty (); +} + CellInst::box_type CellInst::bbox (const db::Layout &g, unsigned int l) const { diff --git a/src/db/db/dbCellInst.h b/src/db/db/dbCellInst.h index 0e711020a..362732873 100644 --- a/src/db/db/dbCellInst.h +++ b/src/db/db/dbCellInst.h @@ -90,6 +90,15 @@ class DB_PUBLIC CellInst */ box_type bbox (const Layout &g) const; + /** + * @brief Compute the bounding box, including empty cells + * + * This method computes the bbox of the cell instance. + * As a requirement, the cell's bounding box must have been + * computed before. + */ + box_type bbox_with_empty (const Layout &g) const; + /** * @brief Compute the bounding box * diff --git a/src/db/db/dbInstElement.h b/src/db/db/dbInstElement.h index 850c93e63..b49a67de4 100644 --- a/src/db/db/dbInstElement.h +++ b/src/db/db/dbInstElement.h @@ -85,7 +85,8 @@ struct DB_PUBLIC InstElement * * @param bc The bounding box converter for the cell instance (db::box_convert) */ - db::Box bbox (const db::box_convert &bc) const + template + db::Box bbox (const BoxConvert &bc) const { if (whole_array ()) { // this is the whole array diff --git a/src/db/db/dbInstances.cc b/src/db/db/dbInstances.cc index 02ca1e743..0e43a2293 100644 --- a/src/db/db/dbInstances.cc +++ b/src/db/db/dbInstances.cc @@ -907,6 +907,19 @@ Instance::bbox () const } } +Instance::box_type +Instance::bbox_with_empty () const +{ + const db::Instances *i = instances (); + const db::Cell *c = i ? i->cell () : 0; + const db::Layout *g = c ? c->layout () : 0; + if (g) { + return bbox (db::box_convert (*g)); + } else { + return db::Instance::box_type (); + } +} + // ------------------------------------------------------------------------------------- // Instances implementation diff --git a/src/db/db/dbInstances.h b/src/db/db/dbInstances.h index 17d71ab28..361f705f2 100644 --- a/src/db/db/dbInstances.h +++ b/src/db/db/dbInstances.h @@ -338,6 +338,15 @@ class DB_PUBLIC Instance */ cell_inst_array_type::box_type bbox () const; + /** + * @brief Returns the bounding box of this array, including empty instances + * + * This method uses the pointers provided internally to identify container and cell. + * In constrast to normal "bbox", this bounding box considers empty cells as + * point-like with a box of (0,0;0,0). + */ + cell_inst_array_type::box_type bbox_with_empty () const; + /** * @brief Return the iterator for the instances of the array * diff --git a/src/db/unit_tests/dbCellTests.cc b/src/db/unit_tests/dbCellTests.cc index 80a88e611..8d6ccd540 100644 --- a/src/db/unit_tests/dbCellTests.cc +++ b/src/db/unit_tests/dbCellTests.cc @@ -33,13 +33,18 @@ TEST(1) db::Cell &c1 (g.cell (g.add_cell ())); db::Cell &c2 (g.cell (g.add_cell ())); + EXPECT_EQ (c1.bbox (), db::Box ()); + EXPECT_EQ (c1.bbox_with_empty (), db::Box (db::Point(), db::Point ())); + db::Box b (0, 100, 1000, 1200); c1.shapes (0).insert (b); EXPECT_EQ (c1.bbox (), b); + EXPECT_EQ (c1.bbox_with_empty (), b); db::Box bb (0, -100, 2000, 2200); c1.shapes (1).insert (bb); EXPECT_EQ (c1.bbox (), b + bb); + EXPECT_EQ (c1.bbox_with_empty (), b + bb); EXPECT_EQ (c1.bbox (0), b); EXPECT_EQ (c1.bbox (1), bb); @@ -52,6 +57,7 @@ TEST(1) EXPECT_EQ (c2.bbox (0), t * b); EXPECT_EQ (c2.bbox (1), t * bb); EXPECT_EQ (c1.bbox (), (b + bb)); + EXPECT_EQ (c1.bbox_with_empty (), (b + bb)); // some basic testing of the instance trees int n; @@ -715,6 +721,11 @@ TEST(3a) db::Trans t (db::Vector (100, -100)); db::Instance inst = c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), t)); EXPECT_EQ (inst.to_string (), "cell_index=1 r0 100,-100"); + EXPECT_EQ (inst.bbox ().to_string (), "()"); + EXPECT_EQ (inst.bbox (db::box_convert (g)).to_string (), "(100,-100;100,-100)"); + EXPECT_EQ (inst.bbox_with_empty ().to_string (), "(100,-100;100,-100)"); + EXPECT_EQ (c0.bbox ().to_string (), "()"); + EXPECT_EQ (c0.bbox_with_empty ().to_string (), "(100,-100;100,-100)"); inst = c0.transform (inst, db::Trans (5)); EXPECT_EQ (inst.to_string (), "cell_index=1 m45 -100,100"); diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index d6ff82a2d..e17f5ee71 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -627,7 +627,7 @@ Service::selection_bbox () db::CplxTrans ctx_trans = db::CplxTrans (layout.dbu ()) * cv.context_trans () * r->trans (); - db::box_convert bc (layout); + db::box_convert bc (layout); if (! r->is_cell_inst ()) { const std::vector *tv_list = tv.per_cv_and_layer (r->cv_index (), r->layer ()); @@ -1115,7 +1115,7 @@ Service::click_proximity (const db::DPoint &pos, lay::Editable::SelectionMode mo lay::InstFinder finder (true, view ()->is_editable () && m_top_level_sel, view ()->is_editable () /*full arrays in editable mode*/, true /*enclose_inst*/, exclude, true /*visible layers*/); // go through all cell views - std::set< std::pair > variants = view ()->cv_transform_variants (); + std::set< std::pair > variants = view ()->cv_transform_variants_with_empty(); for (std::set< std::pair >::const_iterator v = variants.begin (); v != variants.end (); ++v) { finder.find (view (), v->second, v->first, search_box); } @@ -1164,7 +1164,7 @@ Service::transient_select (const db::DPoint &pos) lay::InstFinder finder (true, view ()->is_editable () && m_top_level_sel, view ()->is_editable () /*full arrays in editable mode*/, true /*enclose instances*/, &m_previous_selection, true /*visible layers only*/); // go through all transform variants - std::set< std::pair > variants = view ()->cv_transform_variants (); + std::set< std::pair > variants = view ()->cv_transform_variants_with_empty (); for (std::set< std::pair >::const_iterator v = variants.begin (); v != variants.end (); ++v) { finder.find (view (), v->second, v->first, search_box); } @@ -1185,7 +1185,7 @@ Service::transient_select (const db::DPoint &pos) db::Instance inst = r->back ().inst_ptr; - std::vector tv = mp_view->cv_transform_variants (r->cv_index ()); + std::vector tv = mp_view->cv_transform_variants_with_empty (r->cv_index ()); if (view ()->is_editable ()) { #if 0 @@ -1498,7 +1498,7 @@ Service::select (const db::DBox &box, lay::Editable::SelectionMode mode) lay::InstFinder finder (box.is_point (), view ()->is_editable () && m_top_level_sel, view ()->is_editable () /*full arrays in editable mode*/, true /*enclose_inst*/, exclude, true /*only visible layers*/); // go through all cell views - std::set< std::pair > variants = view ()->cv_transform_variants (); + std::set< std::pair > variants = view ()->cv_transform_variants_with_empty (); for (std::set< std::pair >::const_iterator v = variants.begin (); v != variants.end (); ++v) { finder.find (view (), v->second, v->first, search_box); } @@ -1754,6 +1754,10 @@ Service::do_selection_to_view () // build the transformation variants cache TransformationVariants tv (view ()); + // prepare a default transformation for empty variants + std::vector empty_tv; + empty_tv.push_back (db::DCplxTrans ()); + // Build markers for (EditableSelectionIterator r = begin_selection (); ! r.at_end (); ++r) { @@ -1769,39 +1773,39 @@ Service::do_selection_to_view () if (m_cell_inst_service) { const std::vector *tv_list = tv.per_cv (r->cv_index ()); - if (tv_list != 0) { + if (tv_list == 0) { + tv_list = &empty_tv; + } - if (view ()->is_editable ()) { + if (view ()->is_editable ()) { #if 0 - // to show the content of the cell when the instance is selected: - lay::InstanceMarker *marker = new lay::InstanceMarker (view (), r->cv_index (), ! show_shapes_of_instances (), show_shapes_of_instances () ? max_shapes_of_instances () : 0); + // to show the content of the cell when the instance is selected: + lay::InstanceMarker *marker = new lay::InstanceMarker (view (), r->cv_index (), ! show_shapes_of_instances (), show_shapes_of_instances () ? max_shapes_of_instances () : 0); #else - lay::InstanceMarker *marker = new lay::InstanceMarker (view (), r->cv_index ()); + lay::InstanceMarker *marker = new lay::InstanceMarker (view (), r->cv_index ()); #endif - marker->set_vertex_shape (lay::ViewOp::Cross); - marker->set_vertex_size (9 /*cross vertex size*/); - - if (r->seq () > 0 && m_indicate_secondary_selection) { - marker->set_dither_pattern (3); - } - marker->set (r->back ().inst_ptr, gt, *tv_list); - m_markers.push_back (std::make_pair (r.operator-> (), marker)); + marker->set_vertex_shape (lay::ViewOp::Cross); + marker->set_vertex_size (9 /*cross vertex size*/); - } else { + if (r->seq () > 0 && m_indicate_secondary_selection) { + marker->set_dither_pattern (3); + } + marker->set (r->back ().inst_ptr, gt, *tv_list); + m_markers.push_back (std::make_pair (r.operator-> (), marker)); - lay::Marker *marker = new lay::Marker (view (), r->cv_index ()); - marker->set_vertex_shape (lay::ViewOp::Cross); - marker->set_vertex_size (9 /*cross vertex size*/); + } else { - if (r->seq () > 0 && m_indicate_secondary_selection) { - marker->set_dither_pattern (3); - } - db::box_convert bc (cv->layout ()); - marker->set (bc (r->back ().inst_ptr.cell_inst ().object ()), gt * r->back ().inst_ptr.cell_inst ().complex_trans (*r->back ().array_inst), *tv_list); - m_markers.push_back (std::make_pair (r.operator-> (), marker)); + lay::Marker *marker = new lay::Marker (view (), r->cv_index ()); + marker->set_vertex_shape (lay::ViewOp::Cross); + marker->set_vertex_size (9 /*cross vertex size*/); + if (r->seq () > 0 && m_indicate_secondary_selection) { + marker->set_dither_pattern (3); } + db::box_convert bc (cv->layout ()); + marker->set (bc (r->back ().inst_ptr.cell_inst ().object ()), gt * r->back ().inst_ptr.cell_inst ().complex_trans (*r->back ().array_inst), *tv_list); + m_markers.push_back (std::make_pair (r.operator-> (), marker)); } diff --git a/src/edt/edt/edtServiceImpl.cc b/src/edt/edt/edtServiceImpl.cc index bc72ec62e..634a70a86 100644 --- a/src/edt/edt/edtServiceImpl.cc +++ b/src/edt/edt/edtServiceImpl.cc @@ -1686,7 +1686,7 @@ InstService::do_begin_edit (const db::DPoint &p) std::pair ci = make_cell (cv); if (ci.first) { // use the snapped lower left corner of the bbox unless the origin is inside the bbox - db::Box cell_bbox = cv->layout ().cell (ci.second).bbox (); + db::Box cell_bbox = cv->layout ().cell (ci.second).bbox_with_empty (); if (! m_place_origin && ! cell_bbox.contains (db::Point ())) { db::CplxTrans ct (1.0, m_angle, m_mirror, db::DVector ()); m_disp = db::DPoint () + (m_disp - snap (cell_bbox.transformed (ct).lower_left () * cv->layout ().dbu ())); @@ -1830,7 +1830,7 @@ InstService::do_mouse_move (const db::DPoint &p) std::pair ci = make_cell (cv); if (ci.first) { // use the snapped lower left corner of the bbox unless the origin is inside the bbox - db::Box cell_bbox = cv->layout ().cell (ci.second).bbox (); + db::Box cell_bbox = cv->layout ().cell (ci.second).bbox_with_empty (); if (! m_place_origin && ! cell_bbox.contains (db::Point ())) { db::CplxTrans ct (1.0, m_angle, m_mirror, db::DVector ()); m_disp = db::DPoint () + (m_disp - snap (cell_bbox.transformed (ct).lower_left () * cv->layout ().dbu ())); diff --git a/src/laybasic/laybasic/layFinder.cc b/src/laybasic/laybasic/layFinder.cc index 6564e5f7e..72884fa33 100644 --- a/src/laybasic/laybasic/layFinder.cc +++ b/src/laybasic/laybasic/layFinder.cc @@ -100,13 +100,13 @@ Finder::start (lay::LayoutViewBase *view, unsigned int cv_index, const std::vect if (layers.size () == 1) { - m_box_convert = db::box_convert (*mp_layout, (unsigned int) layers [0]); - m_cell_box_convert = db::box_convert ((unsigned int) layers [0]); + m_box_convert = db::box_convert (*mp_layout, (unsigned int) layers [0]); + m_cell_box_convert = db::box_convert ((unsigned int) layers [0]); } else { - m_box_convert = db::box_convert (*mp_layout); - m_cell_box_convert = db::box_convert (); + m_box_convert = db::box_convert (*mp_layout); + m_cell_box_convert = db::box_convert (); } @@ -217,7 +217,8 @@ Finder::do_find (const db::Cell &cell, int level, const db::DCplxTrans &vp, cons } else if (level < m_max_level && (t * m_cell_box_convert (cell)).touches (m_scan_region) && (mp_view->select_inside_pcells_mode () || !cell.is_proxy ()) - && !mp_view->is_cell_hidden (cell.cell_index (), m_cv_index)) { + && !mp_view->is_cell_hidden (cell.cell_index (), m_cv_index) + && !cell.is_ghost_cell ()) { db::ICplxTrans it = t.inverted (); db::Box scan_box (it * m_scan_region); @@ -734,7 +735,7 @@ InstFinder::find (lay::LayoutViewBase *view, const db::DBox ®ion_mu) progress.set_format (""); mp_progress = &progress; - std::set< std::pair > variants = view->cv_transform_variants (); + std::set< std::pair > variants = view->cv_transform_variants_with_empty (); for (std::set< std::pair >::const_iterator v = variants.begin (); v != variants.end (); ++v) { find (view, v->second, v->first, region_mu); } @@ -828,7 +829,7 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d // just consider the instances exactly at the last level of // hierarchy (this is where the boxes are drawn) or of cells that // are hidden. - if (level == max_level () - 1 || inst_cell.is_proxy () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) { + if (level == max_level () - 1 || inst_cell.is_proxy () || inst_cell.is_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) { db::box_convert bc (layout ()); for (db::CellInstArray::iterator p = cell_inst.begin_touching (search_box, bc); ! p.at_end (); ++p) { @@ -836,10 +837,11 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d ++*mp_progress; db::Box ibox; - if (inst_cell.bbox ().empty ()) { - ibox = db::Box (db::Point (0, 0), db::Point (0, 0)); - } else if (! m_visible_layers || level == mp_view->get_max_hier_levels () - 1 || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) { - ibox = inst_cell.bbox (); + if (! m_visible_layers || level == mp_view->get_max_hier_levels () - 1 || inst_cell.is_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) { + ibox = inst_cell.bbox_with_empty (); + } else if (inst_cell.bbox ().empty ()) { + // empty cells cannot be found by visible layers, so we always select them here + ibox = inst_cell.bbox_with_empty (); } else { for (std::vector::const_iterator l = m_visible_layer_indexes.begin (); l != m_visible_layer_indexes.end (); ++l) { ibox += inst_cell.bbox (*l); @@ -903,7 +905,7 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d // just consider the instances exactly at the last level of // hierarchy (this is where the boxes are drawn) or if of cells that // are hidden. - if (level == max_level () - 1 || inst_cell.is_proxy () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) { + if (level == max_level () - 1 || inst_cell.is_proxy () || inst_cell.is_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) { db::box_convert bc (layout ()); for (db::CellInstArray::iterator p = cell_inst.begin_touching (search_box, bc); ! p.at_end (); ++p) { @@ -914,10 +916,11 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d double d = std::numeric_limits::max (); db::Box ibox; - if (inst_cell.bbox ().empty ()) { - ibox = db::Box (db::Point (0, 0), db::Point (0, 0)); - } else if (! m_visible_layers || level == mp_view->get_max_hier_levels () - 1 || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) { - ibox = inst_cell.bbox (); + if (! m_visible_layers || level == mp_view->get_max_hier_levels () - 1 || inst_cell.is_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) { + ibox = inst_cell.bbox_with_empty (); + } else if (inst_cell.bbox ().empty ()) { + // empty cells cannot be found by visible layers, so we always select them here + ibox = inst_cell.bbox_with_empty (); } else { for (std::vector::const_iterator l = m_visible_layer_indexes.begin (); l != m_visible_layer_indexes.end (); ++l) { ibox += inst_cell.bbox (*l); diff --git a/src/laybasic/laybasic/layFinder.h b/src/laybasic/laybasic/layFinder.h index d02aaa09a..d93e0c9f1 100644 --- a/src/laybasic/laybasic/layFinder.h +++ b/src/laybasic/laybasic/layFinder.h @@ -218,8 +218,8 @@ class LAYBASIC_PUBLIC Finder bool m_point_mode; bool m_catch_all; bool m_top_level_sel; - db::box_convert m_box_convert; - db::box_convert m_cell_box_convert; + db::box_convert m_box_convert; + db::box_convert m_cell_box_convert; }; /** diff --git a/src/laybasic/laybasic/layLayerProperties.cc b/src/laybasic/laybasic/layLayerProperties.cc index 54d782194..041bdf668 100644 --- a/src/laybasic/laybasic/layLayerProperties.cc +++ b/src/laybasic/laybasic/layLayerProperties.cc @@ -790,25 +790,42 @@ LayerPropertiesNode::set_parent (const LayerPropertiesNode *parent) mp_parent.reset (const_cast(parent)); } -db::DBox -LayerPropertiesNode::bbox () const +db::DBox +LayerPropertiesNode::overall_bbox () const { tl_assert (mp_view); lay::CellView cv = mp_view->cellview (cellview_index ()); - + if (! cv.is_valid ()) { return db::DBox (); - } else if (is_cell_box_layer ()) { + } else { db::DBox b; double dbu = cv->layout ().dbu (); for (std::vector::const_iterator t = trans ().begin (); t != trans ().end (); ++t) { - b += (*t * db::CplxTrans (dbu) * cv.context_trans ()) * cv.cell ()->bbox (); + b += (*t * db::CplxTrans (dbu) * cv.context_trans ()) * cv.cell ()->bbox_with_empty (); } return b; + } +} + +db::DBox +LayerPropertiesNode::bbox () const +{ + tl_assert (mp_view); + lay::CellView cv = mp_view->cellview (cellview_index ()); + + if (! cv.is_valid ()) { + + return db::DBox (); + + } else if (is_cell_box_layer ()) { + + return overall_bbox (); + } else { db::DBox b; diff --git a/src/laybasic/laybasic/layLayerProperties.h b/src/laybasic/laybasic/layLayerProperties.h index 0c902f4ea..2264b7eee 100644 --- a/src/laybasic/laybasic/layLayerProperties.h +++ b/src/laybasic/laybasic/layLayerProperties.h @@ -1182,7 +1182,17 @@ class LAYBASIC_PUBLIC LayerPropertiesNode * @return A bbox in micron units */ db::DBox bbox () const; - + + /** + * @brief Computes the overall box of this layer + * + * This is not a layer specific box, but an all-layer box, + * including transformations (if specified). + * This box is equivalent to the box delivered by + * a cell frame layer. + */ + db::DBox overall_bbox () const; + /** * @brief Attach to a view * diff --git a/src/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc index c87b00efd..f870f7dc5 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.cc +++ b/src/laybasic/laybasic/layLayoutViewBase.cc @@ -79,6 +79,9 @@ const double zoom_factor = 0.7; // factor by which panning is faster in "fast" (+Shift) mode const double fast_factor = 3.0; +// size of cross +const int mark_size = 9; + // ------------------------------------------------------------- struct OpHideShowCell @@ -3861,8 +3864,13 @@ LayoutViewBase::full_box () const db::DBox bbox; - for (LayerPropertiesConstIterator l = get_properties ().begin_const_recursive (); ! l.at_end (); ++l) { - bbox += l->bbox (); + auto tv = cv_transform_variants_with_empty (); + for (auto i = tv.begin (); i != tv.end (); ++i) { + const lay::CellView &cv = cellview (i->second); + if (cv.is_valid ()) { + double dbu = cv->layout ().dbu (); + bbox += (i->first * db::CplxTrans (dbu) * cv.context_trans ()) * cv.cell ()->bbox_with_empty (); + } } for (lay::AnnotationShapes::iterator a = annotation_shapes ().begin (); ! a.at_end (); ++a) { @@ -4241,7 +4249,7 @@ LayoutViewBase::set_view_ops () // cell boxes if (m_cell_box_visible) { - lay::ViewOp vop; + lay::ViewOp vop, vopv; // context level if (m_ctx_color.is_valid ()) { @@ -4249,12 +4257,15 @@ LayoutViewBase::set_view_ops () } else { vop = lay::ViewOp (lay::LayerProperties::brighter (box_color.rgb (), brightness_for_context), lay::ViewOp::Copy, 0, 0, 0); } + vopv = vop; + vopv.shape (lay::ViewOp::Cross); + vopv.width (mark_size); // fill, frame, text, vertex view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0)); view_ops.push_back (vop); view_ops.push_back (vop); - view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0)); + view_ops.push_back (vopv); // child level if (m_child_ctx_color.is_valid ()) { @@ -4262,21 +4273,27 @@ LayoutViewBase::set_view_ops () } else { vop = lay::ViewOp (lay::LayerProperties::brighter (box_color.rgb (), brightness_for_context), lay::ViewOp::Copy, 0, 0, 0); } + vopv = vop; + vopv.shape (lay::ViewOp::Cross); + vopv.width (mark_size); // fill, frame, text, vertex view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0)); view_ops.push_back (vop); view_ops.push_back (vop); - view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0)); + view_ops.push_back (vopv); // current level vop = lay::ViewOp (box_color.rgb (), lay::ViewOp::Copy, 0, 0, 0); + vopv = vop; + vopv.shape (lay::ViewOp::Cross); + vopv.width (mark_size); // fill, frame, text, vertex view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0)); view_ops.push_back (vop); view_ops.push_back (vop); - view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0)); + view_ops.push_back (vopv); } else { // invisible @@ -4487,7 +4504,7 @@ LayoutViewBase::set_view_ops () view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0)); } // vertex - view_ops.push_back (lay::ViewOp (frame_color, mode, 0, 0, 0, lay::ViewOp::Cross, l->marked (true /*real*/) ? 9/*mark size*/ : 0)); // vertex + view_ops.push_back (lay::ViewOp (frame_color, mode, 0, 0, 0, lay::ViewOp::Cross, l->marked (true /*real*/) ? mark_size : 0)); // vertex } else { for (unsigned int i = 0; i < (unsigned int) planes_per_layer / 3; ++i) { @@ -5974,6 +5991,16 @@ LayoutViewBase::cv_transform_variants (int cv_index) const return std::vector (trns_variants.begin (), trns_variants.end ()); } +std::vector +LayoutViewBase::cv_transform_variants_with_empty (int cv_index) const +{ + std::vector trns_variants = cv_transform_variants (cv_index); + if (trns_variants.empty ()) { + trns_variants.push_back (db::DCplxTrans ()); + } + return trns_variants; +} + std::vector LayoutViewBase::cv_transform_variants (int cv_index, unsigned int layer) const { @@ -6034,7 +6061,32 @@ LayoutViewBase::cv_transform_variants () const return box_variants; } -db::InstElement +std::set< std::pair > +LayoutViewBase::cv_transform_variants_with_empty () const +{ + std::set< std::pair > box_variants = cv_transform_variants (); + + // add a default box variant for the CVs not present in the layer list to + // draw boxes at least. + + std::vector cv_present; + cv_present.resize (m_cellviews.size ()); + for (auto bv = box_variants.begin (); bv != box_variants.end (); ++bv) { + if (bv->second >= 0 && bv->second < int (cv_present.size ())) { + cv_present[bv->second] = true; + } + } + + for (auto i = cv_present.begin (); i != cv_present.end (); ++i) { + if (!*i) { + box_variants.insert (std::make_pair (db::DCplxTrans (), int (i - cv_present.begin ()))); + } + } + + return box_variants; +} + +db::InstElement LayoutViewBase::ascend (int index) { tl_assert (int (m_cellviews.size ()) > index && cellview_iter (index)->is_valid ()); diff --git a/src/laybasic/laybasic/layLayoutViewBase.h b/src/laybasic/laybasic/layLayoutViewBase.h index ddc8a047b..245afbf7c 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.h +++ b/src/laybasic/laybasic/layLayoutViewBase.h @@ -1674,11 +1674,27 @@ class LAYBASIC_PUBLIC LayoutViewBase : */ std::set< std::pair > cv_transform_variants () const; + /** + * @brief Get a list of cellview index and transform variants including empty cellviews + * + * This version delivers a unit-transformation variant for cell views for which + * no layer is present. This version is used for instance box drawing. + */ + std::set< std::pair > cv_transform_variants_with_empty () const; + /** * @brief Get the global transform variants for a given cellview index */ std::vector cv_transform_variants (int cv_index) const; + /** + * @brief Get the global transform variants for a given cellview index including empty cellviews + * + * This version delivers a unit-transformation variant for cell views for which + * no layer is present. This version is used for instance box drawing. + */ + std::vector cv_transform_variants_with_empty (int cv_index) const; + /** * @brief Get the global transform variants for a given cellview index and layer */ diff --git a/src/laybasic/laybasic/layMarker.cc b/src/laybasic/laybasic/layMarker.cc index cab2f5829..d50fe0162 100644 --- a/src/laybasic/laybasic/layMarker.cc +++ b/src/laybasic/laybasic/layMarker.cc @@ -53,7 +53,7 @@ void render_cell_inst (const db::Layout &layout, const db::CellInstArray &inst, const db::Cell &cell = layout.cell (inst.object ().cell_index ()); std::string cell_name = layout.display_name (inst.object ().cell_index ()); - db::Box cell_box = cell.bbox (); + db::Box cell_box = cell.bbox_with_empty (); db::Vector a, b; unsigned long amax = 0, bmax = 0; @@ -589,7 +589,12 @@ InstanceMarker::set_max_shapes (size_t s) db::DBox InstanceMarker::item_bbox () const { - return db::DBox (m_inst.bbox ()); + const db::Layout *ly = layout (); + if (! ly) { + return db::DBox (); + } + + return db::DBox (m_inst.bbox_with_empty ()); } // ------------------------------------------------------------------------ @@ -1059,7 +1064,7 @@ Marker::item_bbox () const } else if (m_type == Instance) { const db::Layout *ly = layout (); if (ly) { - return db::DBox (m_object.inst->bbox (db::box_convert (*ly))); + return db::DBox (m_object.inst->bbox (db::box_convert (*ly))); } } return db::DBox (); diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.cc b/src/laybasic/laybasic/layRedrawThreadWorker.cc index f0dab5a39..d5151f6b6 100644 --- a/src/laybasic/laybasic/layRedrawThreadWorker.cc +++ b/src/laybasic/laybasic/layRedrawThreadWorker.cc @@ -555,6 +555,18 @@ RedrawThreadWorker::setup (LayoutViewBase *view, RedrawThreadCanvas *canvas, con m_hidden_cells = view->hidden_cells (); + // collect the ghost cells + m_ghost_cells.resize (view->cellviews ()); + for (unsigned int i = 0; i < view->cellviews (); ++i) { + std::set &gc = m_ghost_cells [i]; + const db::Layout &ly = view->cellview (i)->layout (); + for (auto c = ly.begin (); c != ly.end (); ++c) { + if (c->is_ghost_cell ()) { + gc.insert (c->cell_index ()); + } + } + } + m_cellviews.clear (); m_cellviews.reserve (view->cellviews ()); for (unsigned int i = 0; i < view->cellviews (); ++i) { @@ -563,7 +575,7 @@ RedrawThreadWorker::setup (LayoutViewBase *view, RedrawThreadCanvas *canvas, con m_nlayers = mp_redraw_thread->num_layers (); - m_box_variants = view->cv_transform_variants (); + m_box_variants = view->cv_transform_variants_with_empty (); } void @@ -601,7 +613,7 @@ RedrawThreadWorker::test_snapshot (const UpdateSnapshotCallback *update_snapshot } void -RedrawThreadWorker::draw_cell (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, const std::string &txt) +RedrawThreadWorker::draw_cell (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, bool empty_cell, const std::string &txt) { lay::Renderer &r = *mp_renderer; @@ -616,15 +628,20 @@ RedrawThreadWorker::draw_cell (bool drawing_context, int level, const db::CplxTr lay::CanvasPlane *fill = m_planes[0 + plane_group * (planes_per_layer / 3)]; lay::CanvasPlane *contour = m_planes[1 + plane_group * (planes_per_layer / 3)]; + lay::CanvasPlane *vertices = m_planes[3 + plane_group * (planes_per_layer / 3)]; - r.draw (box, trans, fill, contour, 0, 0); + if (empty_cell) { + r.draw (dbox, 0, contour, vertices, 0); + } else { + r.draw (dbox, fill, contour, 0, 0); + } - if (! txt.empty () && dbox.width () > m_min_size_for_label && dbox.height () > m_min_size_for_label) { + if (! txt.empty () && (empty_cell || (dbox.width () > m_min_size_for_label && dbox.height () > m_min_size_for_label))) { // Hint: we render to contour because the texts plane is reserved for properties - r.draw (dbox, txt, - db::Font (m_box_font), - db::HAlignCenter, - db::VAlignCenter, + r.draw (dbox, txt, + db::Font (m_box_font), + db::HAlignCenter, + db::VAlignCenter, // TODO: apply "real" transformation? db::DFTrans (m_box_text_transform ? trans.fp_trans ().rot () : db::DFTrans::r0), 0, 0, 0, contour); } @@ -633,10 +650,6 @@ RedrawThreadWorker::draw_cell (bool drawing_context, int level, const db::CplxTr void RedrawThreadWorker::draw_cell_properties (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, db::properties_id_type prop_id) { - if (prop_id == 0 || ! m_show_properties) { - return; - } - lay::Renderer &r = *mp_renderer; unsigned int plane_group = 2; @@ -648,7 +661,9 @@ RedrawThreadWorker::draw_cell_properties (bool drawing_context, int level, const lay::CanvasPlane *texts = m_planes[2 + plane_group * (planes_per_layer / 3)]; - r.draw_propstring (prop_id, (trans * box).p1 (), texts, trans); + if (prop_id != 0 && m_show_properties) { + r.draw_propstring (prop_id, (trans * box).p1 (), texts, trans); + } } static bool @@ -673,21 +688,28 @@ cells_in (const db::Layout *layout, const db::Cell &cell, return false; } -static bool -need_draw_box (const db::Layout *layout, const db::Cell &cell, - int level, int to_level, - const std::vector > &hidden_cells, unsigned int cv_index) +bool +RedrawThreadWorker::need_draw_box (const db::Layout *layout, const db::Cell &cell, int level) { - if (level > to_level) { + if (level > m_to_level) { return false; } - if (hidden_cells.size () > cv_index && ! hidden_cells [cv_index].empty ()) { + + if (m_ghost_cells.size () > (size_t) m_cv_index && ! m_ghost_cells [m_cv_index].empty ()) { std::set > cache; - if (cells_in (layout, cell, hidden_cells [cv_index], to_level - level, cache)) { + if (cells_in (layout, cell, m_ghost_cells [m_cv_index], m_to_level - level, cache)) { return true; } } - return int (cell.hierarchy_levels ()) + level >= to_level; + + if (m_hidden_cells.size () > (size_t) m_cv_index && ! m_hidden_cells [m_cv_index].empty ()) { + std::set > cache; + if (cells_in (layout, cell, m_hidden_cells [m_cv_index], m_to_level - level, cache)) { + return true; + } + } + + return int (cell.hierarchy_levels ()) + level >= m_to_level; } void @@ -701,7 +723,7 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co const db::Cell &cell = mp_layout->cell (ci); // we will never come to a valid level .. - if (! need_draw_box (mp_layout, cell, level, m_to_level, m_hidden_cells, m_cv_index)) { + if (! need_draw_box (mp_layout, cell, level)) { return; } if (cell_var_cached (ci, trans)) { @@ -720,36 +742,33 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co const db::Cell &cell = mp_layout->cell (ci); // For small bboxes, the cell outline can be reduced .. - db::Box bbox = cell.bbox (); - - if (bbox.empty ()) { + db::Box bbox = cell.bbox_with_empty (); + bool empty_cell = cell.bbox ().empty (); - // no shapes there and below .. - - } else if (m_drop_small_cells && drop_cell (cell, trans)) { + if (m_drop_small_cells && drop_cell (cell, trans)) { // small cell dropped - } else if (level == m_to_level || (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ())) { + } else if (level == m_to_level || cell.is_ghost_cell () || (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ())) { // paint the box on this level - draw_cell (drawing_context, level, trans, bbox, mp_layout->display_name (ci)); + draw_cell (drawing_context, level, trans, bbox, empty_cell, mp_layout->display_name (ci)); } else if (level < m_to_level) { db::DBox dbbox = trans * bbox; - if (dbbox.width () < 1.5 && dbbox.height () < 1.5) { + if (!empty_cell && (dbbox.width () < 1.5 && dbbox.height () < 1.5)) { - if (need_draw_box (mp_layout, cell, level, m_to_level, m_hidden_cells, m_cv_index)) { + if (need_draw_box (mp_layout, cell, level)) { // the cell is a very small box and we know there must be // some level at which to draw the boundary: just draw it // here and stop looking further down .. - draw_cell (drawing_context, level, trans, bbox, std::string ()); + draw_cell (drawing_context, level, trans, bbox, empty_cell, std::string ()); } } else { - db::box_convert bc (*mp_layout); + db::box_convert bc (*mp_layout); // create a set of boxes to look into db::Coord aw = db::coord_traits::rounded (m_abstract_mode_width / mp_layout->dbu ()); @@ -779,7 +798,8 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co const db::CellInstArray &cell_inst = inst->cell_inst (); db::cell_index_type new_ci = cell_inst.object ().cell_index (); - db::Box new_cell_box = mp_layout->cell (new_ci).bbox (); + db::Box new_cell_box = cell_inst.bbox (bc); + bool empty_inst_cell = mp_layout->cell (new_ci).bbox ().empty (); if (last_ci != new_ci) { // Hint: don't use any_cell_box on partially visible cells because that will degrade performance @@ -796,7 +816,7 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co db::Vector a, b; unsigned long amax, bmax; bool simplify = false; - if (cell_inst.is_regular_array (a, b, amax, bmax)) { + if (cell_inst.is_regular_array (a, b, amax, bmax) && (amax > 1 || bmax > 1)) { db::DBox inst_box; if (cell_inst.is_complex ()) { @@ -816,9 +836,7 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co if (simplify) { // The array can be simplified if there are levels below to draw - if (need_draw_box (mp_layout, mp_layout->cell (new_ci), level + 1, m_to_level, m_hidden_cells, m_cv_index)) { - - db::box_convert bc (*mp_layout); + if (need_draw_box (mp_layout, mp_layout->cell (new_ci), level + 1)) { unsigned int plane_group = 2; if (drawing_context) { @@ -827,8 +845,13 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co plane_group = 1; } + if (empty_inst_cell) { + lay::CanvasPlane *vertices = m_planes[3 + plane_group * (planes_per_layer / 3)]; + r.draw (cell_inst.bbox (bc), trans, 0, 0, vertices, 0); + } + lay::CanvasPlane *contour = m_planes[1 + plane_group * (planes_per_layer / 3)]; - r.draw (cell_inst.bbox (bc), trans, contour, 0, 0, 0); + r.draw (cell_inst.bbox (bc), trans, contour, contour, 0, 0); } @@ -900,7 +923,7 @@ RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_ty const db::Cell &cell = mp_layout->cell (ci); // we will never come to a valid level .. - if (! need_draw_box (mp_layout, cell, level, m_to_level, m_hidden_cells, m_cv_index)) { + if (! need_draw_box (mp_layout, cell, level)) { return; } if (cell_var_cached (ci, trans)) { @@ -918,17 +941,14 @@ RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_ty const db::Cell &cell = mp_layout->cell (ci); // For small bboxes, the cell outline can be reduced .. - db::Box bbox = cell.bbox (); + db::Box bbox = cell.bbox_with_empty (); + bool empty_cell = cell.bbox ().empty (); - if (bbox.empty ()) { - - // no shapes there and below .. - - } else if (m_drop_small_cells && drop_cell (cell, trans)) { + if (m_drop_small_cells && drop_cell (cell, trans)) { // small cell dropped - } else if (level == m_to_level || (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ())) { + } else if (level == m_to_level || cell.is_ghost_cell () || (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ())) { // paint the box on this level draw_cell_properties (drawing_context, level, trans, bbox, prop_id); @@ -936,13 +956,13 @@ RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_ty } else if (level < m_to_level) { db::DBox dbbox = trans * bbox; - if (dbbox.width () < 1.5 && dbbox.height () < 1.5) { + if (!empty_cell && (dbbox.width () < 1.5 && dbbox.height () < 1.5)) { // ignore cells which are small } else { - db::box_convert bc (*mp_layout); + db::box_convert bc (*mp_layout); // create a set of boxes to look into db::Coord aw = db::coord_traits::rounded (m_abstract_mode_width / mp_layout->dbu ()); @@ -973,7 +993,7 @@ RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_ty db::properties_id_type cell_inst_prop = inst->prop_id (); db::cell_index_type new_ci = cell_inst.object ().cell_index (); - db::Box new_cell_box = mp_layout->cell (new_ci).bbox (); + db::Box new_cell_box = cell_inst.bbox (bc); if (last_ci != new_ci) { // Hint: don't use any_cell_box on partially visible cells because that will degrade performance @@ -990,7 +1010,7 @@ RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_ty db::Vector a, b; unsigned long amax, bmax; bool simplify = false; - if (cell_inst.is_regular_array (a, b, amax, bmax)) { + if (cell_inst.is_regular_array (a, b, amax, bmax) && (amax > 1 || bmax > 1)) { db::DBox inst_box; if (cell_inst.is_complex ()) { @@ -1052,13 +1072,18 @@ RedrawThreadWorker::any_shapes (db::cell_index_type cell_index, unsigned int lev } } + // Ghost cells are not drawn either + const db::Cell &cell = mp_layout->cell (cell_index); + if (cell.is_ghost_cell ()) { + return false; + } + // the cache contains all cells that are visited already RedrawThreadWorker::micro_instance_cache_t::const_iterator c = m_mi_cache.find (std::make_pair (cell_index, levels)); if (c == m_mi_cache.end ()) { int ret = false; - const db::Cell &cell = mp_layout->cell (cell_index); if (! cell.shapes (m_layer).begin (db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Boxes | db::ShapeIterator::Points, mp_prop_sel, m_inv_prop_sel).at_end ()) { ret = true; } else if (levels > 1) { @@ -1089,13 +1114,18 @@ RedrawThreadWorker::any_cell_box (db::cell_index_type cell_index, unsigned int l } } + // ghost cells are also drawn + const db::Cell &cell = mp_layout->cell (cell_index); + if (cell.is_ghost_cell ()) { + return true; + } + // the cache contains all cells that are visited already RedrawThreadWorker::micro_instance_cache_t::const_iterator c = m_mi_cell_box_cache.find (std::make_pair (cell_index, levels)); if (c == m_mi_cell_box_cache.end ()) { int ret = false; if (levels > 1) { - const db::Cell &cell = mp_layout->cell (cell_index); for (db::Cell::child_cell_iterator cc = cell.begin_child_cells (); !cc.at_end () && !ret; ++cc) { ret = any_cell_box (*cc, levels - 1); } @@ -1125,13 +1155,18 @@ RedrawThreadWorker::any_text_shapes (db::cell_index_type cell_index, unsigned in } } + // Ghost cells are not drawn either + const db::Cell &cell = mp_layout->cell (cell_index); + if (cell.is_ghost_cell ()) { + return false; + } + // the cache contains all cells that are visited already RedrawThreadWorker::micro_instance_cache_t::const_iterator c = m_mi_text_cache.find (std::make_pair (cell_index, levels)); if (c == m_mi_text_cache.end ()) { bool ret = false; - const db::Cell &cell = mp_layout->cell (cell_index); if (! cell.shapes (m_layer).begin (db::ShapeIterator::Texts, mp_prop_sel, m_inv_prop_sel).at_end () || (m_show_properties && ! cell.shapes (m_layer).begin (db::ShapeIterator::AllWithProperties, mp_prop_sel, m_inv_prop_sel).at_end ())) { ret = true; @@ -1309,7 +1344,7 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c } else if (! bbox.empty ()) { - bool hidden = (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ()); + bool hidden = cell.is_ghost_cell () || ((m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (ci) != m_hidden_cells [m_cv_index].end ())); bool need_to_dive = (level + 1 < m_to_level) && ! hidden; db::Box cell_bbox = cell.bbox (); @@ -1443,7 +1478,7 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c ++inst; db::cell_index_type new_ci = cell_inst.object ().cell_index (); - bool hidden = (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (new_ci) != m_hidden_cells [m_cv_index].end ()); + bool hidden = mp_layout->cell (new_ci).is_ghost_cell () || ((m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (new_ci) != m_hidden_cells [m_cv_index].end ())); db::Box cell_box = mp_layout->cell (new_ci).bbox (m_layer); if (! cell_box.empty () && ! hidden) { @@ -1745,7 +1780,7 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_ ++inst; db::cell_index_type new_ci = cell_inst.object ().cell_index (); - bool hidden = (m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (new_ci) != m_hidden_cells [m_cv_index].end ()); + bool hidden = mp_layout->cell (new_ci).is_ghost_cell () || ((m_cv_index < int (m_hidden_cells.size ()) && m_hidden_cells [m_cv_index].find (new_ci) != m_hidden_cells [m_cv_index].end ())); db::Box new_cell_box = mp_layout->cell (new_ci).bbox (m_layer); if (! new_cell_box.empty () && ! hidden) { @@ -1902,7 +1937,7 @@ RedrawThreadWorker::draw_layer (int from_level, int to_level, db::cell_index_typ db::Box cell_bbox = cell.bbox (); // Nothing to draw - if (bbox.empty ()) { + if (bbox.empty () || cell.is_ghost_cell ()) { return; } @@ -2095,6 +2130,9 @@ bool RedrawThreadWorker::drop_cell (const db::Cell &cell, const db::CplxTrans &trans) { db::DBox bbox = trans * cell.bbox (); + if (bbox.empty ()) { + return true; + } double value = 0; if (m_drop_small_cells_cond == lay::LayoutViewBase::DSC_Min) { @@ -2237,7 +2275,7 @@ RedrawThreadWorker::iterate_variants_rec (const std::vector &redraw_re db::Coord lim = std::numeric_limits::max (); db::DBox world (trans * db::Box (db::Point (-lim, -lim), db::Point (lim, lim))); db::Box vp = db::Box (trans.inverted () * (world & db::DBox (*rr))); - vp &= mp_layout->cell (ci).bbox (); // this avoids problems when accessing designs through very large viewports + vp &= mp_layout->cell (ci).bbox_with_empty (); // this avoids problems when accessing designs through very large viewports if (! vp.empty ()) { actual_regions.push_back (vp); } diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.h b/src/laybasic/laybasic/layRedrawThreadWorker.h index 502904fa4..a02b8637a 100644 --- a/src/laybasic/laybasic/layRedrawThreadWorker.h +++ b/src/laybasic/laybasic/layRedrawThreadWorker.h @@ -186,7 +186,7 @@ class RedrawThreadWorker void draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level); void draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level, db::properties_id_type prop_id); void draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_box, int level, db::properties_id_type prop_id); - void draw_cell (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, const std::string &txt); + void draw_cell (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, bool empty_cell, const std::string &txt); void draw_cell_properties (bool drawing_context, int level, const db::CplxTrans &trans, const db::Box &box, db::properties_id_type prop_id); void draw_cell_shapes (const db::CplxTrans &trans, const db::Cell &cell, const db::Box &vp, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text); void test_snapshot (const UpdateSnapshotCallback *update_snapshot); @@ -199,6 +199,7 @@ class RedrawThreadWorker bool any_shapes (db::cell_index_type cell_index, unsigned int levels); bool any_text_shapes (db::cell_index_type cell_index, unsigned int levels); bool any_cell_box (db::cell_index_type cell_index, unsigned int levels); + bool need_draw_box (const db::Layout *layout, const db::Cell &cell, int level); RedrawThread *mp_redraw_thread; std::vector m_redraw_region; @@ -232,6 +233,7 @@ class RedrawThreadWorker unsigned int m_cache_hits, m_cache_misses; std::set > m_box_variants; std::vector > m_hidden_cells; + std::vector > m_ghost_cells; std::vector m_cellviews; const db::Layout *mp_layout; int m_cv_index;