From 29cc6034665a789e54723d35a7a74fd26af2a561 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 9 Aug 2025 23:54:01 +0200 Subject: [PATCH] WIP --- src/db/db/dbBoxConvert.cc | 13 +-- src/db/db/dbCell.cc | 37 ++++++- src/db/db/dbCell.h | 13 ++- src/db/db/dbCellInst.cc | 6 + src/db/db/dbCellInst.h | 9 ++ src/laybasic/laybasic/layLayoutViewBase.cc | 22 +++- .../laybasic/layRedrawThreadWorker.cc | 103 +++++++++++++----- src/laybasic/laybasic/layRedrawThreadWorker.h | 5 +- 8 files changed, 155 insertions(+), 53 deletions(-) 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 @@ public: */ 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 @@ private: 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 @@ public: */ 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/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc index c87b00efd..a786e7378 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 @@ -4241,7 +4244,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 +4252,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 +4268,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 +4499,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) { diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.cc b/src/laybasic/laybasic/layRedrawThreadWorker.cc index f0dab5a39..5e86f737a 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) { @@ -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,17 +628,23 @@ 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 *text = m_planes[2 + 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, 0, 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, // TODO: apply "real" transformation? - db::DFTrans (m_box_text_transform ? trans.fp_trans ().rot () : db::DFTrans::r0), 0, 0, 0, contour); + db::DFTrans (m_box_text_transform ? trans.fp_trans ().rot () : db::DFTrans::r0), 0, 0, 0, text); } } @@ -673,21 +691,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 +726,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)) { @@ -713,6 +738,12 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co } } +db::Box +RedrawThreadWorker::empty_cell_replacement_box () +{ + return db::Box (db::Point (), db::Point ()); +} + void RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_box, int level) { @@ -721,35 +752,36 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co // For small bboxes, the cell outline can be reduced .. db::Box bbox = cell.bbox (); - + bool empty_cell = false; if (bbox.empty ()) { + bbox = empty_cell_replacement_box (); + empty_cell = true; + } - // 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 ()); @@ -780,6 +812,11 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co db::cell_index_type new_ci = cell_inst.object ().cell_index (); db::Box new_cell_box = mp_layout->cell (new_ci).bbox (); + bool empty_inst_cell = false; + if (new_cell_box.empty ()) { + new_cell_box = empty_cell_replacement_box (); + empty_inst_cell = true; + } if (last_ci != new_ci) { // Hint: don't use any_cell_box on partially visible cells because that will degrade performance @@ -796,7 +833,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 +853,9 @@ 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)) { + if (need_draw_box (mp_layout, mp_layout->cell (new_ci), level + 1)) { - db::box_convert bc (*mp_layout); + db::box_convert bc (*mp_layout); unsigned int plane_group = 2; if (drawing_context) { @@ -827,8 +864,13 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co plane_group = 1; } - lay::CanvasPlane *contour = m_planes[1 + plane_group * (planes_per_layer / 3)]; - r.draw (cell_inst.bbox (bc), trans, contour, 0, 0, 0); + 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); + } else { + lay::CanvasPlane *contour = m_planes[1 + plane_group * (planes_per_layer / 3)]; + r.draw (cell_inst.bbox (bc), trans, contour, 0, 0, 0); + } } @@ -900,7 +942,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)) { @@ -2095,6 +2137,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) { diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.h b/src/laybasic/laybasic/layRedrawThreadWorker.h index 502904fa4..4f18c59d7 100644 --- a/src/laybasic/laybasic/layRedrawThreadWorker.h +++ b/src/laybasic/laybasic/layRedrawThreadWorker.h @@ -186,7 +186,7 @@ private: 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,8 @@ private: 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); + db::Box empty_cell_replacement_box (); RedrawThread *mp_redraw_thread; std::vector m_redraw_region; @@ -232,6 +234,7 @@ private: 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;