Fixing a small glitch: drawing into a ghost cell is possible, but did not turn off the ghost cell flag. Now the flag is still on, but a non-empty cell is not considered a ghost cell for most purposes.

This commit is contained in:
Matthias Koefferlein 2026-02-19 23:26:48 +01:00
parent 95d6b0aca5
commit bc49082955
10 changed files with 63 additions and 41 deletions

View File

@ -945,7 +945,7 @@ std::string
Cell::get_display_name () const
{
tl_assert (layout () != 0);
if (is_ghost_cell () && empty ()) {
if (is_real_ghost_cell ()) {
return std::string ("(") + layout ()->cell_name (cell_index ()) + std::string (")");
} else {
return layout ()->cell_name (cell_index ());
@ -959,6 +959,20 @@ Cell::set_name (const std::string &name)
layout ()->rename_cell (cell_index (), name.c_str ());
}
void
Cell::set_ghost_cell (bool g)
{
// NOTE: this change is not undo managed
if (m_ghost_cell != g) {
m_ghost_cell = g;
tl_assert (layout () != 0);
// To trigger a redraw and cell tree rebuild
layout ()->cell_name_changed ();
}
}
void
Cell::check_locked () const
{

View File

@ -925,15 +925,26 @@ public:
return m_ghost_cell;
}
/**
* @brief Gets a value indicating whether the cell is a "real" ghost cell
*
* A ghost cell is a real ghost cell only if the ghost cell flag is set
* and the cell is empty. Only in that case for example the cell is written
* to GDS files as a ghost cell.
*
* Otherwise, the ghost cell flag is mostly ignored.
*/
bool is_real_ghost_cell () const
{
return m_ghost_cell && empty ();
}
/**
* @brief Sets the "ghost cell" flag
*
* See "is_ghost_cell" for a description of this property.
*/
void set_ghost_cell (bool g)
{
m_ghost_cell = g;
}
void set_ghost_cell (bool g);
/**
* @brief Gets a value indicating whether the cell is locked

View File

@ -1417,7 +1417,7 @@ Layout::add_cell (const char *name)
if (cm != m_cell_map.end ()) {
const db::Cell &c= cell (cm->second);
if (c.is_ghost_cell () && c.empty ()) {
if (c.is_real_ghost_cell ()) {
// ghost cells are available as new cells - the idea is to
// treat them as non-existing.
return cm->second;

View File

@ -183,6 +183,14 @@ public:
return m_busy;
}
/**
* @brief Issue a "cell name changed event"
*/
void cell_name_changed ()
{
cell_name_changed_event ();
}
protected:
friend class PropertiesRepository;
@ -191,14 +199,6 @@ protected:
*/
virtual void do_update () { }
/**
* @brief Issue a "prop id's changed event"
*/
void cell_name_changed ()
{
cell_name_changed_event ();
}
/**
* @brief Issue a "layer properties changed event"
*/

View File

@ -3450,6 +3450,9 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"To satisfy the references inside the layout, a dummy cell is created in this case\n"
"which has the \"ghost cell\" flag set to true.\n"
"\n"
"A ghost cell is a real ghost cell only if the cell is empty. In that case, it is written "
"as a ghost cell to GDS files for example. If a cell is not empty, this flag is ignored.\n"
"\n"
"This method has been introduced in version 0.20.\n"
) +
gsi::method ("ghost_cell=", &db::Cell::set_ghost_cell, gsi::arg ("flag"),

View File

@ -220,7 +220,7 @@ Finder::do_find (const db::Cell &cell, int level, const db::DCplxTrans &vp, cons
&& (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)
&& !cell.is_ghost_cell ()) {
&& !cell.is_real_ghost_cell ()) {
db::ICplxTrans it = t.inverted ();
db::Box scan_box (it * m_scan_region);
@ -846,7 +846,7 @@ InstFinder::reset_counter ()
bool
InstFinder::consider_cell (const db::Cell &cell) const
{
return cell.is_ghost_cell () ? m_consider_ghost_cells : m_consider_normal_cells;
return cell.is_real_ghost_cell () ? m_consider_ghost_cells : m_consider_normal_cells;
}
void
@ -877,7 +877,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 () || inst_cell.is_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) {
if (level == max_level () - 1 || inst_cell.is_proxy () || inst_cell.is_real_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) {
db::box_convert <db::CellInst, false> bc (layout ());
for (db::CellInstArray::iterator p = cell_inst.begin_touching (search_box, bc); ! p.at_end (); ++p) {
@ -885,7 +885,7 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d
checkpoint ();
db::Box ibox;
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)) {
if (! m_visible_layers || level == mp_view->get_max_hier_levels () - 1 || inst_cell.is_real_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
@ -954,7 +954,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 () || inst_cell.is_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) {
if (level == max_level () - 1 || inst_cell.is_proxy () || inst_cell.is_real_ghost_cell () || mp_view->is_cell_hidden (inst_cell.cell_index (), m_cv_index)) {
db::box_convert <db::CellInst, false> bc (layout ());
for (db::CellInstArray::iterator p = cell_inst.begin_touching (search_box, bc); ! p.at_end (); ++p) {
@ -965,7 +965,7 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d
double d = std::numeric_limits<double>::max ();
db::Box ibox;
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)) {
if (! m_visible_layers || level == mp_view->get_max_hier_levels () - 1 || inst_cell.is_real_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

View File

@ -714,7 +714,7 @@ RedrawThreadWorker::setup (LayoutViewBase *view, RedrawThreadCanvas *canvas, con
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 ()) {
if (c->is_real_ghost_cell ()) {
gc.insert (c->cell_index ());
}
}
@ -950,12 +950,12 @@ RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type c
bbox_for_label = bbox;
}
if (for_ghosts && cell.is_ghost_cell ()) {
if (for_ghosts && cell.is_real_ghost_cell ()) {
// paint the box on this level
draw_cell (drawing_context, level, trans, bbox, bbox_for_label, empty_cell, mp_layout->display_name (ci), opt_bitmap);
} else if (! for_ghosts && ! cell.is_ghost_cell () && (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 (! for_ghosts && ! cell.is_real_ghost_cell () && (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 ()))) {
// paint the box on this level
draw_cell (drawing_context, level, trans, bbox, bbox_for_label, empty_cell, mp_layout->display_name (ci), opt_bitmap);
@ -1186,12 +1186,12 @@ RedrawThreadWorker::draw_box_properties_impl (bool drawing_context, db::cell_ind
// small cell dropped
} else if (for_ghosts && cell.is_ghost_cell ()) {
} else if (for_ghosts && cell.is_real_ghost_cell ()) {
// paint the box on this level
draw_cell_properties (drawing_context, level, trans, bbox, prop_id);
} else if (! for_ghosts && ! cell.is_ghost_cell () && (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 (! for_ghosts && ! cell.is_real_ghost_cell () && (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 ()))) {
// paint the box on this level
draw_cell_properties (drawing_context, level, trans, bbox, prop_id);
@ -1317,7 +1317,7 @@ 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 ()) {
if (cell.is_real_ghost_cell ()) {
return false;
}
@ -1359,7 +1359,7 @@ 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 ()) {
if (cell.is_real_ghost_cell ()) {
return true;
}
@ -1400,7 +1400,7 @@ 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 ()) {
if (cell.is_real_ghost_cell ()) {
return false;
}
@ -1503,7 +1503,7 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c
} else if (! bbox.empty ()) {
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 hidden = cell.is_real_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 ();
@ -1637,7 +1637,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 = 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 ()));
bool hidden = mp_layout->cell (new_ci).is_real_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) {
@ -1939,7 +1939,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 = 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 ()));
bool hidden = mp_layout->cell (new_ci).is_real_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) {
@ -2096,7 +2096,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 () || cell.is_ghost_cell ()) {
if (bbox.empty () || cell.is_real_ghost_cell ()) {
return;
}

View File

@ -577,7 +577,7 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
// don't write ghost cells unless they are not empty (any more)
// also don't write proxy cells which are not employed
if ((! cref.is_ghost_cell () || ! cref.empty ()) && (! cref.is_proxy () || ! cref.is_top ())) {
if (! cref.is_real_ghost_cell () && (! cref.is_proxy () || ! cref.is_top ())) {
try {
write_cell (layout, cref, layers, cell_set, sf, time_data);

View File

@ -913,7 +913,7 @@ Writer::make_meta_data (const db::Cell *cell, stream::metaData::MetaData::Builde
void
Writer::write_cell (db::cell_index_type ci, kj::BufferedOutputStream &os)
{
bool needs_layout_view = ! mp_layout->cell (ci).is_ghost_cell ();
bool needs_layout_view = ! mp_layout->cell (ci).is_real_ghost_cell ();
bool needs_meta_data_view = mp_layout->begin_meta (ci) != mp_layout->end_meta (ci);
capnp::MallocMessageBuilder message;

View File

@ -1443,12 +1443,6 @@ static bool must_write_cell (const db::Cell &cref)
return ! cref.is_proxy () || ! cref.is_top ();
}
static bool skip_cell_body (const db::Cell &cref)
{
// Skip cell bodies for ghost cells unless empty (they are not longer ghost cells in this case)
return cref.is_ghost_cell () && cref.empty ();
}
void
OASISWriter::create_cell_nstrings (const db::Layout &layout, const std::set <db::cell_index_type> &cell_set)
{
@ -1670,7 +1664,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
mp_cell = &cref;
// skip cell body if the cell is not to be written
if (skip_cell_body (cref)) {
if (cref.is_real_ghost_cell ()) {
continue;
}