This commit is contained in:
Matthias Koefferlein 2025-08-09 23:54:01 +02:00
parent e359f2af20
commit 29cc603466
8 changed files with 155 additions and 53 deletions

View File

@ -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 ();
}
}

View File

@ -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 <cell_inst_type, false> 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
{

View File

@ -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;

View File

@ -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
{

View File

@ -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
*

View File

@ -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) {

View File

@ -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 <lay::LayoutViewBase::cell_index_type> &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 <std::set <db::cell_index_type> > &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 <std::pair <int, db::cell_index_type> > 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 <std::pair <int, db::cell_index_type> > 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 <db::CellInst> bc (*mp_layout);
db::box_convert<db::CellInst, false> bc (*mp_layout);
// create a set of boxes to look into
db::Coord aw = db::coord_traits<db::Coord>::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 <db::CellInst> bc (*mp_layout);
db::box_convert <db::CellInst, false> 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) {

View File

@ -186,7 +186,7 @@ private:
void draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector <db::Box> &redraw_regions, int level);
void draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector <db::Box> &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 <db::Box> m_redraw_region;
@ -232,6 +234,7 @@ private:
unsigned int m_cache_hits, m_cache_misses;
std::set <std::pair <db::DCplxTrans, int> > m_box_variants;
std::vector <std::set <lay::LayoutViewBase::cell_index_type> > m_hidden_cells;
std::vector <std::set <lay::LayoutViewBase::cell_index_type> > m_ghost_cells;
std::vector <lay::CellView> m_cellviews;
const db::Layout *mp_layout;
int m_cv_index;