From 780615ea9d2e377d1f5c8ce00478bd827ee1a46c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 18 Oct 2025 22:02:58 +0200 Subject: [PATCH 01/16] Implemented solution for issue #2176 - On "via" (Key "O"), the application switches into path mode - A selection menu pops up if multiple layers are possible starting points below the cursor As a side effect, "tap" will only display the layer selection menu if there is more than one layer to be selected. --- src/db/db/dbVia.cc | 19 +++++-- src/db/db/dbVia.h | 3 ++ src/edt/edt/edtDialogs.cc | 90 +++++++++++++++++++++++++++++++++- src/edt/edt/edtDialogs.h | 15 +++++- src/edt/edt/edtMainService.cc | 89 ++++----------------------------- src/edt/edt/edtPathService.cc | 49 +++++++++++++----- src/edt/edt/edtPathService.h | 1 + src/edt/edt/edtService.cc | 14 ++---- src/edt/edt/edtShapeService.cc | 9 ++-- 9 files changed, 179 insertions(+), 110 deletions(-) diff --git a/src/db/db/dbVia.cc b/src/db/db/dbVia.cc index 78844b3f6..6d9da1e2a 100644 --- a/src/db/db/dbVia.cc +++ b/src/db/db/dbVia.cc @@ -46,8 +46,8 @@ ViaType::init () // --------------------------------------------------------------------------------------- -std::vector -find_via_definitions_for (const std::string &technology, const db::LayerProperties &layer, int dir) +static std::vector +find_via_definitions_impl (const std::string &technology, const db::LayerProperties &layer, int dir, bool all) { std::vector via_defs; @@ -65,7 +65,8 @@ find_via_definitions_for (const std::string &technology, const db::LayerProperti auto via_types = pcell->via_types (); for (auto vt = via_types.begin (); vt != via_types.end (); ++vt) { - if ((dir >= 0 && vt->bottom.log_equal (layer) && vt->bottom_wired) || + if (all || + (dir >= 0 && vt->bottom.log_equal (layer) && vt->bottom_wired) || (dir <= 0 && vt->top.log_equal (layer) && vt->top_wired)) { via_defs.push_back (SelectedViaDefinition (lib, pc->second, *vt)); } @@ -78,4 +79,16 @@ find_via_definitions_for (const std::string &technology, const db::LayerProperti return via_defs; } +std::vector +get_via_definitions (const std::string &technology) +{ + return find_via_definitions_impl (technology, db::LayerProperties (), 0, true); +} + +std::vector +find_via_definitions_for (const std::string &technology, const db::LayerProperties &layer, int dir) +{ + return find_via_definitions_impl (technology, layer, dir, false); +} + } diff --git a/src/db/db/dbVia.h b/src/db/db/dbVia.h index 492e1333e..d02378df9 100644 --- a/src/db/db/dbVia.h +++ b/src/db/db/dbVia.h @@ -210,6 +210,9 @@ struct SelectedViaDefinition DB_PUBLIC std::vector find_via_definitions_for (const std::string &technology, const db::LayerProperties &layer, int dir); +DB_PUBLIC std::vector +get_via_definitions (const std::string &technology); + } #endif diff --git a/src/edt/edt/edtDialogs.cc b/src/edt/edt/edtDialogs.cc index 120a7495f..cd1553a0f 100644 --- a/src/edt/edt/edtDialogs.cc +++ b/src/edt/edt/edtDialogs.cc @@ -29,8 +29,9 @@ #include "layEditorUtils.h" #include "layObjectInstPath.h" #include "layCellView.h" -#include "layLayoutViewBase.h" #include "layMarker.h" +#include "layFinder.h" +#include "layLayerTreeModel.h" #include "tlException.h" #include "tlExceptions.h" @@ -725,6 +726,93 @@ AreaAndPerimeterDialog::exec_dialog (double area, double perimeter) return exec () != 0; } +// -------------------------------------------------------------------------------- +// popup_tap_layer_menu implementation + +lay::LayerPropertiesConstIterator +popup_tap_layer_menu (lay::LayoutViewBase *view, const std::set *filter, int cv_index) +{ + QWidget *view_widget = lay::widget_from_view (view); + if (! view_widget) { + return lay::LayerPropertiesConstIterator (); + } + + if (! view->canvas ()->mouse_in_window ()) { + return lay::LayerPropertiesConstIterator (); + } + + lay::ShapeFinder finder (true, // point mode + false, // all hierarchy levels + db::ShapeIterator::flags_type (db::ShapeIterator::All - db::ShapeIterator::Texts), // do not consider texts - their bounding box may be too large + 0, // no excludes + true // capture all shapes + ); + + // capture all objects in point mode (default: capture one only) + finder.set_catch_all (true); + + // go through all visible layers of all cellviews + db::DPoint pt = view->canvas ()->mouse_position_um (); + finder.find (view, db::DBox (pt, pt)); + + std::set > layers_in_selection; + + for (lay::ShapeFinder::iterator f = finder.begin (); f != finder.end (); ++f) { + if (cv_index < 0 || f->cv_index () == cv_index) { + const db::Layout &ly = view->cellview (f->cv_index ())->layout (); + // ignore guiding shapes and only provide layers from the filter + if (f->layer () != ly.guiding_shape_layer () + && (! filter || filter->find (ly.get_properties (f->layer ())) != filter->end ())) { + layers_in_selection.insert (std::make_pair (f->cv_index (), f->layer ())); + } + } + } + + std::vector tapped_layers; + for (lay::LayerPropertiesConstIterator lp = view->begin_layers (view->current_layer_list ()); ! lp.at_end (); ++lp) { + const lay::LayerPropertiesNode *ln = lp.operator-> (); + if (layers_in_selection.find (std::make_pair ((unsigned int) ln->cellview_index (), (unsigned int) ln->layer_index ())) != layers_in_selection.end ()) { + tapped_layers.push_back (lp); + } + } + + if (tapped_layers.empty ()) { + return lay::LayerPropertiesConstIterator (); + } else if (tapped_layers.size () == 1) { + return tapped_layers.front (); + } + + // List the layers under the cursor as pop up a menu + +#if QT_VERSION >= 0x050000 + double dpr = view_widget->devicePixelRatio (); +#else + double dpr = 1.0; +#endif + + std::unique_ptr menu (new QMenu (view_widget)); + menu->show (); + + int icon_size = menu->style ()->pixelMetric (QStyle::PM_ButtonIconSize); + + db::DPoint mp_local = view->canvas ()->mouse_position (); + QPoint mp = view->canvas ()->widget ()->mapToGlobal (QPoint (mp_local.x (), mp_local.y ())); + + for (std::vector::const_iterator l = tapped_layers.begin (); l != tapped_layers.end (); ++l) { + QAction *a = menu->addAction (lay::LayerTreeModel::icon_for_layer (*l, view, icon_size, icon_size, dpr, 0, true), tl::to_qstring ((*l)->display_string (view, true, true /*with source*/))); + a->setData (int (l - tapped_layers.begin ())); + } + + QAction *action = menu->exec (mp); + if (action) { + int index = action->data ().toInt (); + return tapped_layers [index]; + } else { + return lay::LayerPropertiesConstIterator (); + } + +} + } #endif diff --git a/src/edt/edt/edtDialogs.h b/src/edt/edt/edtDialogs.h index e844491ff..fa8947a06 100644 --- a/src/edt/edt/edtDialogs.h +++ b/src/edt/edt/edtDialogs.h @@ -34,6 +34,9 @@ #include "dbLayout.h" #include "dbPoint.h" +#include "dbLayerProperties.h" + +#include "layLayoutView.h" #include "ui_InstantiationForm.h" #include "ui_ChangeLayerOptionsDialog.h" @@ -47,7 +50,6 @@ namespace lay { - class LayoutViewBase; class Marker; class ObjectInstPath; } @@ -222,6 +224,17 @@ public: bool exec_dialog (double area, double perimeter); }; +/** + * @brief Obtains an layer iterator for one of the layers present under the mouse cursor + * + * This is not really a dialog, but a popup menu. + * + * @param view The LayoutView object + * @param filter An optional set of layers to show. Only layers from this set are shown. + * @return A layer iterator which is at_end if no specific layer was selected + */ +lay::LayerPropertiesConstIterator popup_tap_layer_menu (lay::LayoutViewBase *view, const std::set *filter = 0, int cv_index = -1); + } // namespace edt #endif diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index 69dd64163..d395d8309 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -47,7 +47,6 @@ #if defined(HAVE_QT) # include "layDialogs.h" -# include "layLayerTreeModel.h" # include "layCellSelectionForm.h" # include "edtDialogs.h" # include "edtEditorOptionsPages.h" @@ -2361,88 +2360,16 @@ void MainService::cm_tap () { #if ! defined(HAVE_QT) - tl_assert (false); // see TODO -#endif - -#if defined(HAVE_QT) - QWidget *view_widget = lay::widget_from_view (view ()); - if (! view_widget) { - return; - } -#endif - - if (! view ()->canvas ()->mouse_in_window ()) { - return; - } - - lay::ShapeFinder finder (true, // point mode - false, // all hierarchy levels - db::ShapeIterator::flags_type (db::ShapeIterator::All - db::ShapeIterator::Texts), // do not consider texts - their bounding box may be too large - 0, // no excludes - true // capture all shapes - ); - - // capture all objects in point mode (default: capture one only) - finder.set_catch_all (true); - - // go through all visible layers of all cellviews - db::DPoint pt = view ()->canvas ()->mouse_position_um (); - finder.find (view (), db::DBox (pt, pt)); - - std::set > layers_in_selection; - - for (lay::ShapeFinder::iterator f = finder.begin (); f != finder.end (); ++f) { - // ignore guiding shapes - if (f->layer () != view ()->cellview (f->cv_index ())->layout ().guiding_shape_layer ()) { - layers_in_selection.insert (std::make_pair (f->cv_index (), f->layer ())); - } - } - - std::vector tapped_layers; - for (lay::LayerPropertiesConstIterator lp = view ()->begin_layers (view ()->current_layer_list ()); ! lp.at_end (); ++lp) { - const lay::LayerPropertiesNode *ln = lp.operator-> (); - if (layers_in_selection.find (std::make_pair ((unsigned int) ln->cellview_index (), (unsigned int) ln->layer_index ())) != layers_in_selection.end ()) { - tapped_layers.push_back (lp); - } - } - - if (tapped_layers.empty ()) { - return; - } - - // List the layers under the cursor as pop up a menu - -#if defined(HAVE_QT) - // TODO: what to do here in Qt-less case? Store results in configuration so they can be retrieved externally? - -#if QT_VERSION >= 0x050000 - double dpr = view_widget->devicePixelRatio (); + tl_assert (false); // TODO #else - double dpr = 1.0; -#endif + lay::LayerPropertiesConstIterator iter = edt::popup_tap_layer_menu (view ()); + if (! iter.at_end ()) { - std::unique_ptr menu (new QMenu (view_widget)); - menu->show (); - - int icon_size = menu->style ()->pixelMetric (QStyle::PM_ButtonIconSize); - - db::DPoint mp_local = view ()->canvas ()->mouse_position (); - QPoint mp = view ()->canvas ()->widget ()->mapToGlobal (QPoint (mp_local.x (), mp_local.y ())); - - for (std::vector::const_iterator l = tapped_layers.begin (); l != tapped_layers.end (); ++l) { - QAction *a = menu->addAction (lay::LayerTreeModel::icon_for_layer (*l, view (), icon_size, icon_size, dpr, 0, true), tl::to_qstring ((*l)->display_string (view (), true, true /*with source*/))); - a->setData (int (l - tapped_layers.begin ())); - } - - QAction *action = menu->exec (mp); - if (action) { - - int index = action->data ().toInt (); - lay::LayerPropertiesConstIterator iter = tapped_layers [index]; view ()->set_current_layer (iter); edt::Service *es = dynamic_cast (view ()->canvas ()->active_service ()); if (es) { + db::DPoint pt = view ()->canvas ()->mouse_position_um (); es->tap (pt); } @@ -2486,9 +2413,11 @@ MainService::via_impl (int dir) return; } - edt::Service *es = dynamic_cast (view ()->canvas ()->active_service ()); - if (es) { - es->via (dir); + // via generation is delegated to the path service always + edt::PathService *ps = view ()->get_plugin (); + if (ps) { + view ()->switch_mode (ps->plugin_declaration ()->id ()); + ps->via (dir); } } diff --git a/src/edt/edt/edtPathService.cc b/src/edt/edt/edtPathService.cc index 6879dec56..65863b591 100644 --- a/src/edt/edt/edtPathService.cc +++ b/src/edt/edt/edtPathService.cc @@ -22,15 +22,12 @@ #include "edtPathService.h" +#include "edtDialogs.h" +#include "edtPropertiesPages.h" #include "layLayoutViewBase.h" #include "layFinder.h" -#if defined(HAVE_QT) -# include "edtPropertiesPages.h" -# include "layLayoutView.h" -#endif - namespace edt { @@ -268,6 +265,35 @@ PathService::via (int dir) #endif } +db::LayerProperties +PathService::get_layer_for_via (unsigned int cv_index) +{ +#if defined(HAVE_QT) + const lay::CellView &cv = view ()->cellview (cv_index); + if (! cv.is_valid ()) { + return db::LayerProperties (); + } + + std::vector via_defs = db::get_via_definitions (cv->layout ().technology_name ()); + + std::set lp_filter; + for (auto vd = via_defs.begin (); vd != via_defs.end (); ++vd) { + lp_filter.insert (vd->via_type.bottom); + lp_filter.insert (vd->via_type.top); + } + + lay::LayerPropertiesConstIterator iter; + iter = edt::popup_tap_layer_menu (view (), &lp_filter, cv_index); + if (iter.at_end ()) { + return db::LayerProperties (); + } + + return cv->layout ().get_properties (iter->layer_index ()); +#else + return db::LayerProperties (); +#endif +} + bool PathService::get_via_for (const db::LayerProperties &lp, unsigned int cv_index, int dir, db::SelectedViaDefinition &via_def) { @@ -364,19 +390,20 @@ PathService::via_initial (int dir) return; } - const lay::CellView &cv = view ()->cellview (r->cv_index ()); - if (! cv.is_valid ()) { + // the first found provides the cellview index + int cv_index = r->cv_index (); + + db::LayerProperties lp = get_layer_for_via (cv_index); + if (lp.is_null ()) { return; } - db::LayerProperties lp = cv->layout ().get_properties (r->layer ()); - db::SelectedViaDefinition via_def; - if (! get_via_for (lp, r->cv_index (), dir, via_def)) { + if (! get_via_for (lp, cv_index, dir, via_def)) { return; } - set_layer (lp, r->cv_index ()); + set_layer (lp, cv_index); bool is_bottom = via_def.via_type.bottom.log_equal (lp); db::LayerProperties lp_new = is_bottom ? via_def.via_type.top : via_def.via_type.bottom; diff --git a/src/edt/edt/edtPathService.h b/src/edt/edt/edtPathService.h index c926101b4..b7321fa53 100644 --- a/src/edt/edt/edtPathService.h +++ b/src/edt/edt/edtPathService.h @@ -89,6 +89,7 @@ private: void via_initial (int dir); void via_editing (int dir); bool get_via_for (const db::LayerProperties &lp, unsigned int cv_index, int dir, db::SelectedViaDefinition &via_def); + db::LayerProperties get_layer_for_via (unsigned int cv_index); void push_segment (const db::Shape &shape, const db::Instance &instance, const db::ViaType &via_type, db::Manager::transaction_id_t transaction_id); void pop_segment (); }; diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index ceab2f786..758f5ecc1 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -26,17 +26,13 @@ #include "dbLibrary.h" #include "edtPlugin.h" #include "edtService.h" -#if defined(HAVE_QT) -# include "edtEditorOptionsPages.h" -# include "edtDialogs.h" -#endif +#include "edtEditorOptionsPages.h" +#include "edtDialogs.h" #include "layFinder.h" #include "layLayoutView.h" #include "laySnap.h" #include "layConverters.h" -#if defined(HAVE_QT) -# include "layEditorOptionsPages.h" -#endif +#include "layEditorOptionsPages.h" #include "tlProgress.h" #include "tlTimer.h" @@ -1709,13 +1705,13 @@ Service::begin_edit (const db::DPoint &p) } void -Service::tap (const db::DPoint & /*initial*/) +Service::tap (const db::DPoint & /*pt*/) { // .. nothing here .. } void -Service::via (int) +Service::via (int /*dir*/) { // .. nothing here .. } diff --git a/src/edt/edt/edtShapeService.cc b/src/edt/edt/edtShapeService.cc index 4664f777b..da8b7590c 100644 --- a/src/edt/edt/edtShapeService.cc +++ b/src/edt/edt/edtShapeService.cc @@ -22,15 +22,14 @@ #include "edtShapeService.h" #include "edtMainService.h" +#include "edtPathService.h" +#include "edtPropertiesPages.h" #include "layLayoutView.h" #include "dbEdgeProcessor.h" #include "dbPolygonTools.h" -#if defined(HAVE_QT) -# include "edtPropertiesPages.h" -# include "layTipDialog.h" -# include "layEditorOptionsPages.h" -#endif +#include "layTipDialog.h" +#include "layEditorOptionsPages.h" namespace edt { From 0016710573435fc4e549cd59d2b347dc9c4930a8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 18 Oct 2025 23:06:23 +0200 Subject: [PATCH 02/16] Preparations: more planes, for ghost cells too --- src/laybasic/laybasic/layLayoutViewBase.cc | 56 ++++++ .../laybasic/layRedrawThreadWorker.cc | 165 +++++++++++++++--- src/laybasic/laybasic/layRedrawThreadWorker.h | 15 +- 3 files changed, 203 insertions(+), 33 deletions(-) diff --git a/src/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc index 50a160e5d..f2ed75b5e 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.cc +++ b/src/laybasic/laybasic/layLayoutViewBase.cc @@ -4327,6 +4327,62 @@ LayoutViewBase::set_view_ops () } } + // ghost cells + if (m_cell_box_visible) { // @@@ + + lay::ViewOp vop, vopv; + + // context level + if (m_ctx_color.is_valid ()) { + vop = lay::ViewOp (m_ctx_color.rgb (), lay::ViewOp::Copy, 0, 0, 0); + } 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 (vopv); + + // child level + if (m_child_ctx_color.is_valid ()) { + vop = lay::ViewOp (m_child_ctx_color.rgb (), lay::ViewOp::Copy, 0, 0, 0); + } 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 (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 (vopv); + + } else { + // invisible + for (unsigned int i = 0; i < (unsigned int) planes_per_layer; ++i) { // frame, fill, vertex, text + view_ops.push_back (lay::ViewOp (0, lay::ViewOp::Or, 0, 0, 0)); + } + } + // sanity check: number of planes defined in layRedrawThreadWorker must match to view_ops layout tl_assert (view_ops.size () == (size_t)cell_box_planes); diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.cc b/src/laybasic/laybasic/layRedrawThreadWorker.cc index d5151f6b6..fe1275a41 100644 --- a/src/laybasic/laybasic/layRedrawThreadWorker.cc +++ b/src/laybasic/laybasic/layRedrawThreadWorker.cc @@ -316,6 +316,73 @@ RedrawThreadWorker::perform_task (tl::Task *task) transfer (); + // Draw the ghost cells + + // HINT: the order in which the planes are delivered (the index stored in the first member of the pair below) + // must correspond with the order by which the ViewOp's are created inside LayoutView::set_view_ops + m_buffers.clear (); + for (unsigned int i = 0; i < (unsigned int) planes_per_layer / 3; ++i) { + + // context level planes + unsigned int i1 = plain_cell_box_planes + i; + mp_canvas->initialize_plane (m_planes[i], i1); + m_buffers.push_back (std::make_pair (i1, m_planes [i])); + + // child level planes (if used) + unsigned int i2 = plain_cell_box_planes + i + planes_per_layer / 3; + mp_canvas->initialize_plane (m_planes [i + planes_per_layer / 3], i2); + m_buffers.push_back (std::make_pair (i2, m_planes [i + planes_per_layer / 3])); + + // current level planes + unsigned int i3 = plain_cell_box_planes + i + 2 * (planes_per_layer / 3); + mp_canvas->initialize_plane (m_planes [i + 2 * (planes_per_layer / 3)], i3); + m_buffers.push_back (std::make_pair (i3, m_planes [i + 2 * (planes_per_layer / 3)])); + + } + + // detect whether the text planes are empty. If not, the whole text plane must be redrawn to account for clipped texts + text_planes_empty = true; + for (unsigned int i = 0; i < (unsigned int) planes_per_layer && text_planes_empty; i += (unsigned int) planes_per_layer / 3) { + lay::Bitmap *text = dynamic_cast (m_planes[i + 2]); + if (text && ! text->empty ()) { + text_planes_empty = false; + } + } + + text_redraw_regions = m_redraw_region; + if (! text_planes_empty) { + // if there are non-empty text planes, redraw the whole area for texts + text_redraw_regions.clear (); + text_redraw_regions.push_back(db::Box(0, 0, mp_canvas->canvas_width (), mp_canvas->canvas_height ())); + for (unsigned int i = 0; i < (unsigned int) planes_per_layer; i += (unsigned int) planes_per_layer / 3) { + lay::Bitmap *text = dynamic_cast (m_planes[i + 2]); + if (text) { + text->clear (); + } + } + } + + for (std::set< std::pair >::const_iterator b = m_box_variants.begin (); b != m_box_variants.end (); ++b) { + + const lay::CellView &cv = m_cellviews [b->second]; + if (cv.is_valid () && ! cv->layout ().under_construction () && ! (cv->layout ().manager () && cv->layout ().manager ()->transacting ())) { + + mp_layout = &cv->layout (); + m_cv_index = b->second; + + db::CplxTrans trans = m_vp_trans * b->first * db::CplxTrans (mp_layout->dbu ()); + + iterate_variants (m_redraw_region, cv.cell_index (), trans, &RedrawThreadWorker::draw_boxes_for_ghosts); + iterate_variants (text_redraw_regions, cv.cell_index (), trans, &RedrawThreadWorker::draw_box_properties_for_ghosts); + + } + + } + + transfer (); + + // draw guiding and error shapes + // HINT: the order in which the planes are delivered (the index stored in the first member of the pair below) // must correspond with the order by which the ViewOp's are created inside LayoutView::set_view_ops m_buffers.clear (); @@ -689,31 +756,51 @@ cells_in (const db::Layout *layout, const db::Cell &cell, } bool -RedrawThreadWorker::need_draw_box (const db::Layout *layout, const db::Cell &cell, int level) +RedrawThreadWorker::need_draw_box (const db::Layout *layout, const db::Cell &cell, int level, bool for_ghosts) { if (level > m_to_level) { + return false; - } - 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, m_ghost_cells [m_cv_index], m_to_level - level, cache)) { - return true; + } else if (for_ghosts) { + + 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, m_ghost_cells [m_cv_index], m_to_level - level, cache)) { + return true; + } } - } - 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 false; + + } else { + + 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; + return int (cell.hierarchy_levels ()) + level >= m_to_level; + + } } void RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level) +{ + draw_boxes_impl (drawing_context, ci, trans, redraw_regions, level, false); +} + +void +RedrawThreadWorker::draw_boxes_for_ghosts (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level) +{ + draw_boxes_impl (drawing_context, ci, trans, redraw_regions, level, true); +} + +void +RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level, bool for_ghosts) { // do not draw, if there is nothing to draw if (mp_layout->cells () <= ci || redraw_regions.empty ()) { @@ -723,7 +810,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)) { + if (! need_draw_box (mp_layout, cell, level, for_ghosts)) { return; } if (cell_var_cached (ci, trans)) { @@ -731,12 +818,12 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co } for (std::vector::const_iterator b = redraw_regions.begin (); b != redraw_regions.end (); ++b) { - draw_boxes (drawing_context, ci, trans, *b, level); + draw_boxes_impl (drawing_context, ci, trans, *b, level, for_ghosts); } } void -RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_box, int level) +RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_box, int level, bool for_ghosts) { lay::Renderer &r = *mp_renderer; const db::Cell &cell = mp_layout->cell (ci); @@ -749,7 +836,12 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co // small cell dropped - } 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 ())) { + } else if (for_ghosts && cell.is_ghost_cell ()) { + + // paint the box on this level + draw_cell (drawing_context, level, trans, bbox, empty_cell, mp_layout->display_name (ci)); + + } else if (! for_ghosts && (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, empty_cell, mp_layout->display_name (ci)); @@ -759,7 +851,7 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co db::DBox dbbox = trans * bbox; if (!empty_cell && (dbbox.width () < 1.5 && dbbox.height () < 1.5)) { - if (need_draw_box (mp_layout, cell, level)) { + if (need_draw_box (mp_layout, cell, level, for_ghosts)) { // 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 .. @@ -836,7 +928,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)) { + if (need_draw_box (mp_layout, mp_layout->cell (new_ci), level + 1, for_ghosts)) { unsigned int plane_group = 2; if (drawing_context) { @@ -865,7 +957,7 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co test_snapshot (0); db::ICplxTrans t (cell_inst.complex_trans (*p)); db::Box new_vp = safe_transformed_box (*v, t.inverted ()); - draw_boxes (drawing_context, new_ci, trans * t, new_vp, level + 1); + draw_boxes_impl (drawing_context, new_ci, trans * t, new_vp, level + 1, for_ghosts); if (p.quad_id () > 0 && p.quad_id () != qid) { @@ -902,18 +994,30 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co } -void +void RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &vp, int level) +{ + draw_box_properties_impl (drawing_context, ci, trans, vp, level, false); +} + +void +RedrawThreadWorker::draw_box_properties_for_ghosts (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &vp, int level) +{ + draw_box_properties_impl (drawing_context, ci, trans, vp, level, false); +} + +void +RedrawThreadWorker::draw_box_properties_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &vp, int level, bool for_ghosts) { if (! m_text_visible) { return; } - draw_box_properties (drawing_context, ci, trans, vp, level, 0); + draw_box_properties_impl (drawing_context, ci, trans, vp, level, 0, for_ghosts); } void -RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &vp, int level, db::properties_id_type prop_id) +RedrawThreadWorker::draw_box_properties_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &vp, int level, db::properties_id_type prop_id, bool for_ghosts) { // do not draw, if there is nothing to draw if (mp_layout->cells () <= ci || vp.empty ()) { @@ -923,7 +1027,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)) { + if (! need_draw_box (mp_layout, cell, level, for_ghosts)) { return; } if (cell_var_cached (ci, trans)) { @@ -931,12 +1035,12 @@ RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_ty } for (std::vector::const_iterator b = vp.begin (); b != vp.end (); ++b) { - draw_box_properties (drawing_context, ci, trans, *b, level, prop_id); + draw_box_properties_impl (drawing_context, ci, trans, *b, level, prop_id, for_ghosts); } } void -RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level, db::properties_id_type prop_id) +RedrawThreadWorker::draw_box_properties_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level, db::properties_id_type prop_id, bool for_ghosts) { const db::Cell &cell = mp_layout->cell (ci); @@ -948,7 +1052,12 @@ RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_ty // small cell dropped - } 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 ())) { + } else if (for_ghosts && cell.is_ghost_cell ()) { + + // paint the box on this level + draw_cell_properties (drawing_context, level, trans, bbox, prop_id); + + } else if (! for_ghosts && (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); @@ -1036,7 +1145,7 @@ RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_ty test_snapshot (0); db::ICplxTrans t (cell_inst.complex_trans (*p)); db::Box new_vp = safe_transformed_box (*v, t.inverted ()); - draw_box_properties (drawing_context, new_ci, trans * t, new_vp, level + 1, cell_inst_prop); + draw_box_properties_impl (drawing_context, new_ci, trans * t, new_vp, level + 1, cell_inst_prop, for_ghosts); } diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.h b/src/laybasic/laybasic/layRedrawThreadWorker.h index a02b8637a..4f108c587 100644 --- a/src/laybasic/laybasic/layRedrawThreadWorker.h +++ b/src/laybasic/laybasic/layRedrawThreadWorker.h @@ -44,7 +44,8 @@ class CanvasPlane; // some helpful constants const int planes_per_layer = 12; -const int cell_box_planes = planes_per_layer; // for cell boxes +const int plain_cell_box_planes = planes_per_layer; // for cell boxes, not including ghost_cells +const int cell_box_planes = planes_per_layer * 2; // for cell boxes, including ghost cells const int guiding_shape_planes = planes_per_layer; // for guiding shapes const int special_planes_before = cell_box_planes + guiding_shape_planes; const int special_planes_after = 1; @@ -181,11 +182,15 @@ private: void draw_layer_wo_cache (int from_level, int to_level, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &vv, int level, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text, const UpdateSnapshotCallback *update_snapshot); void draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level); void draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_region, int level, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text, Bitmap *opt_bitmap); + void draw_boxes_for_ghosts (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level); void draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level); - void draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_region, int level); + void draw_boxes_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level, bool for_ghosts); + void draw_boxes_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_region, int level, bool for_ghosts); + void draw_box_properties_for_ghosts (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); - 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_box_properties_impl (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level, bool for_ghosts); + void draw_box_properties_impl (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, bool for_ghosts); + void draw_box_properties_impl (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, bool for_ghosts); 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); @@ -199,7 +204,7 @@ 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); + bool need_draw_box (const db::Layout *layout, const db::Cell &cell, int level, bool for_ghosts); RedrawThread *mp_redraw_thread; std::vector m_redraw_region; From 19dc5e8edbb56c6e7a25e1405f46fd0902ca7ace Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 18 Oct 2025 23:40:50 +0200 Subject: [PATCH 03/16] Implemented a solution for #2180 Implements a new option to show/hide unresolved references (ghost cells). The option is found in "Display/Cells" in the Setup dialog and also in the View menu. --- src/lay/lay/layMainWindow.cc | 1 + src/laybasic/laybasic/layLayoutViewBase.cc | 21 +++++++++++-- src/laybasic/laybasic/layLayoutViewBase.h | 14 +++++++++ src/laybasic/laybasic/layLayoutViewConfig.cc | 1 + .../laybasic/layRedrawThreadWorker.cc | 6 ++-- src/laybasic/laybasic/laybasicConfig.h | 1 + src/layui/layui/LayoutViewConfigPage2a.ui | 30 +++++++++++++++---- src/layui/layui/layLayoutViewConfigPages.cc | 8 +++-- 8 files changed, 70 insertions(+), 12 deletions(-) diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 72430e410..8e4f976f0 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -4489,6 +4489,7 @@ public: menu_entries.push_back (lay::config_menu_item ("show_markers", at, tl::to_string (QObject::tr ("Show Markers")), cfg_markers_visible, "?")); menu_entries.push_back (lay::config_menu_item ("show_texts", at, tl::to_string (QObject::tr ("Show Texts")), cfg_text_visible, "?")); menu_entries.push_back (lay::config_menu_item ("show_cell_boxes", at, tl::to_string (QObject::tr ("Show Cell Frames")), cfg_cell_box_visible, "?")); + menu_entries.push_back (lay::config_menu_item ("show_ghost_cells", at, tl::to_string (QObject::tr ("Show Unresolved References")), cfg_ghost_cells_visible, "?")); menu_entries.push_back (lay::config_menu_item ("no_stipples", at, tl::to_string (QObject::tr ("Show Layers Without Fill")), cfg_no_stipple, "?")); menu_entries.push_back (lay::config_menu_item ("synchronized_views", at, tl::to_string (QObject::tr ("Synchronized Views")), cfg_synchronized_views, "?")); menu_entries.push_back (lay::config_menu_item ("edit_top_level_selection:edit_mode", at, tl::to_string (QObject::tr ("Select Top Level Objects")), edt::cfg_edit_top_level_selection, "?")); diff --git a/src/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc index f2ed75b5e..8877903bc 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.cc +++ b/src/laybasic/laybasic/layLayoutViewBase.cc @@ -343,6 +343,7 @@ LayoutViewBase::init (db::Manager *mgr) m_box_font = 0; m_min_size_for_label = 16; m_cell_box_visible = true; + m_ghost_cells_visible = true; m_text_visible = true; m_default_font_size = lay::FixedFont::default_font_size (); m_text_lazy_rendering = true; @@ -958,6 +959,13 @@ LayoutViewBase::configure (const std::string &name, const std::string &value) cell_box_visible (flag); return true; + } else if (name == cfg_ghost_cells_visible) { + + bool flag; + tl::from_string (value, flag); + ghost_cells_visible (flag); + return true; + } else if (name == cfg_cell_box_color) { tl::Color color; @@ -4328,7 +4336,7 @@ LayoutViewBase::set_view_ops () } // ghost cells - if (m_cell_box_visible) { // @@@ + if (m_ghost_cells_visible) { lay::ViewOp vop, vopv; @@ -5422,7 +5430,16 @@ LayoutViewBase::cell_box_visible (bool vis) } } -void +void +LayoutViewBase::ghost_cells_visible (bool vis) +{ + if (m_ghost_cells_visible != vis) { + m_ghost_cells_visible = vis; + update_content (); + } +} + +void LayoutViewBase::text_font (unsigned int f) { if (m_text_font != f) { diff --git a/src/laybasic/laybasic/layLayoutViewBase.h b/src/laybasic/laybasic/layLayoutViewBase.h index eeda5619a..1799a5c20 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.h +++ b/src/laybasic/laybasic/layLayoutViewBase.h @@ -1115,6 +1115,19 @@ public: return m_cell_box_visible; } + /** + * @brief Visibility of ghost cells + */ + void ghost_cells_visible (bool vis); + + /** + * @brief Visibility of ghost cells + */ + bool ghost_cells_visible () const + { + return m_ghost_cells_visible; + } + /** * @brief Min instance label size setter */ @@ -2910,6 +2923,7 @@ private: unsigned int m_box_font; int m_min_size_for_label; bool m_cell_box_visible; + bool m_ghost_cells_visible; tl::Color m_marker_color; int m_marker_line_width; diff --git a/src/laybasic/laybasic/layLayoutViewConfig.cc b/src/laybasic/laybasic/layLayoutViewConfig.cc index 30c161f77..7f7908bd8 100644 --- a/src/laybasic/laybasic/layLayoutViewConfig.cc +++ b/src/laybasic/laybasic/layLayoutViewConfig.cc @@ -59,6 +59,7 @@ public: options.push_back (std::pair (cfg_cell_box_text_transform, "true")); options.push_back (std::pair (cfg_cell_box_color, "auto")); options.push_back (std::pair (cfg_cell_box_visible, "true")); + options.push_back (std::pair (cfg_ghost_cells_visible, "true")); options.push_back (std::pair (cfg_text_color, "auto")); options.push_back (std::pair (cfg_text_visible, "true")); options.push_back (std::pair (cfg_text_lazy_rendering, "true")); diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.cc b/src/laybasic/laybasic/layRedrawThreadWorker.cc index fe1275a41..d8a332b50 100644 --- a/src/laybasic/laybasic/layRedrawThreadWorker.cc +++ b/src/laybasic/laybasic/layRedrawThreadWorker.cc @@ -841,7 +841,7 @@ RedrawThreadWorker::draw_boxes_impl (bool drawing_context, db::cell_index_type c // paint the box on this level draw_cell (drawing_context, level, trans, bbox, empty_cell, mp_layout->display_name (ci)); - } else if (! for_ghosts && (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_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, empty_cell, mp_layout->display_name (ci)); @@ -1003,7 +1003,7 @@ RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_ty void RedrawThreadWorker::draw_box_properties_for_ghosts (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &vp, int level) { - draw_box_properties_impl (drawing_context, ci, trans, vp, level, false); + draw_box_properties_impl (drawing_context, ci, trans, vp, level, true); } void @@ -1057,7 +1057,7 @@ RedrawThreadWorker::draw_box_properties_impl (bool drawing_context, db::cell_ind // paint the box on this level draw_cell_properties (drawing_context, level, trans, bbox, prop_id); - } else if (! for_ghosts && (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_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); diff --git a/src/laybasic/laybasic/laybasicConfig.h b/src/laybasic/laybasic/laybasicConfig.h index 47ec7a75f..681c3c8ac 100644 --- a/src/laybasic/laybasic/laybasicConfig.h +++ b/src/laybasic/laybasic/laybasicConfig.h @@ -95,6 +95,7 @@ static const std::string cfg_cell_box_text_font ("inst-label-font"); static const std::string cfg_cell_box_text_transform ("inst-label-transform"); static const std::string cfg_cell_box_color ("inst-color"); static const std::string cfg_cell_box_visible ("inst-visible"); +static const std::string cfg_ghost_cells_visible ("ghost-cells-visible"); static const std::string cfg_text_color ("text-color"); static const std::string cfg_text_visible ("text-visible"); static const std::string cfg_text_lazy_rendering ("text-lazy-rendering"); diff --git a/src/layui/layui/LayoutViewConfigPage2a.ui b/src/layui/layui/LayoutViewConfigPage2a.ui index 2f4e6911a..23bc34c44 100644 --- a/src/layui/layui/LayoutViewConfigPage2a.ui +++ b/src/layui/layui/LayoutViewConfigPage2a.ui @@ -6,8 +6,8 @@ 0 0 - 631 - 320 + 656 + 397 @@ -21,13 +21,33 @@ 9 - - + + Show cell boxes - + true + + + + + + Show unresolved references (ghost cells) + + + true + + + + + + + Cell box appearance + + + false + 9 diff --git a/src/layui/layui/layLayoutViewConfigPages.cc b/src/layui/layui/layLayoutViewConfigPages.cc index da43a394e..55a766913 100644 --- a/src/layui/layui/layLayoutViewConfigPages.cc +++ b/src/layui/layui/layLayoutViewConfigPages.cc @@ -207,7 +207,10 @@ LayoutViewConfigPage2a::setup (lay::Dispatcher *root) mp_ui->cell_xform_text_cbx->setChecked (flag); root->config_get (cfg_cell_box_visible, flag); - mp_ui->cell_group->setChecked (flag); + mp_ui->cell_boxes_visible->setChecked (flag); + + root->config_get (cfg_ghost_cells_visible, flag); + mp_ui->ghost_cells_visible->setChecked (flag); int font = 0; root->config_get (cfg_cell_box_text_font, font); @@ -247,7 +250,8 @@ LayoutViewConfigPage2a::commit (lay::Dispatcher *root) root->config_set (cfg_cell_box_text_transform, mp_ui->cell_xform_text_cbx->isChecked ()); root->config_set (cfg_cell_box_text_font, mp_ui->cell_font_cb->currentIndex ()); root->config_set (cfg_cell_box_color, mp_ui->cell_box_color_pb->get_color (), ColorConverter ()); - root->config_set (cfg_cell_box_visible, mp_ui->cell_group->isChecked ()); + root->config_set (cfg_cell_box_visible, mp_ui->cell_boxes_visible->isChecked ()); + root->config_set (cfg_ghost_cells_visible, mp_ui->ghost_cells_visible->isChecked ()); root->config_set (cfg_guiding_shape_visible, mp_ui->pcell_gs_group->isChecked ()); root->config_set (cfg_guiding_shape_line_width, mp_ui->pcell_gs_lw->value ()); From c9fde4c3cd18eb113eb65ce46da021d91b2481ab Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 19 Oct 2025 17:47:02 +0200 Subject: [PATCH 04/16] Implemented solution for #2147 (merge multiple files into one library) The complication is auto-load: in case one file changed and needs to get merge, the whole library needs to be updated. Solution is to do a rescan of the whole folder in case something has changed there. --- src/lay/lay/layLibraryController.cc | 131 +++++++++++++++++++++------- 1 file changed, 99 insertions(+), 32 deletions(-) diff --git a/src/lay/lay/layLibraryController.cc b/src/lay/lay/layLibraryController.cc index fd5607520..f8a5b32f9 100644 --- a/src/lay/lay/layLibraryController.cc +++ b/src/lay/lay/layLibraryController.cc @@ -30,6 +30,7 @@ #include "dbLibraryManager.h" #include "dbLibrary.h" #include "dbReader.h" +#include "dbCellMapping.h" #include "tlLog.h" #include "tlStream.h" @@ -160,39 +161,64 @@ LibraryController::sync_files () m_file_watcher->add_file (tl::to_string (lp.absolutePath ())); } + tl::log << "Scanning library path '" << tl::to_string (lp.absolutePath ()) << "'"; + QStringList name_filters; name_filters << QString::fromUtf8 ("*"); + // NOTE: this should return a list sorted by name QStringList libs = lp.entryList (name_filters, QDir::Files); - for (QStringList::const_iterator im = libs.begin (); im != libs.end (); ++im) { - std::string filename = tl::to_string (*im); + bool needs_load = false; + + for (QStringList::const_iterator im = libs.begin (); im != libs.end () && ! needs_load; ++im) { + std::string lib_path = tl::to_string (lp.absoluteFilePath (*im)); + QFileInfo fi (tl::to_qstring (lib_path)); - try { + auto ll = m_lib_files.find (lib_path); + if (ll == m_lib_files.end ()) { + needs_load = true; + } else if (fi.lastModified () > ll->second.time) { + needs_load = true; + } - QFileInfo fi (tl::to_qstring (lib_path)); + } - bool needs_load = false; - std::map::iterator ll = m_lib_files.find (lib_path); - if (ll == m_lib_files.end ()) { - needs_load = true; - } else { - if (fi.lastModified () > ll->second.time) { - needs_load = true; - } else { - new_lib_files.insert (*ll); - } + if (! needs_load) { + + // If not reloading, register the existing files as known ones - this allows detecting if + // a file got removed. + for (QStringList::const_iterator im = libs.begin (); im != libs.end () && ! needs_load; ++im) { + + std::string lib_path = tl::to_string (lp.absoluteFilePath (*im)); + auto ll = m_lib_files.find (lib_path); + if (ll != m_lib_files.end ()) { + new_lib_files.insert (*ll); } - if (needs_load) { + } + + } else { + + std::map libs_by_name_here; + + // Reload all files + for (QStringList::const_iterator im = libs.begin (); im != libs.end (); ++im) { + + std::string filename = tl::to_string (*im); + std::string lib_path = tl::to_string (lp.absoluteFilePath (*im)); + QFileInfo fi (tl::to_qstring (lib_path)); + + try { std::unique_ptr lib (new db::Library ()); lib->set_description (filename); if (! p->second.empty ()) { lib->set_technology (p->second); } - lib->set_name (tl::to_string (QFileInfo (*im).baseName ())); + + std::string libname = tl::to_string (QFileInfo (*im).baseName ()); tl::log << "Reading library '" << lib_path << "'"; tl::InputStream stream (lib_path); @@ -203,31 +229,72 @@ LibraryController::sync_files () db::Layout::meta_info_name_id_type libname_name_id = lib->layout ().meta_info_name_id ("libname"); for (db::Layout::meta_info_iterator m = lib->layout ().begin_meta (); m != lib->layout ().end_meta (); ++m) { if (m->first == libname_name_id && ! m->second.value.is_nil ()) { - lib->set_name (m->second.value.to_string ()); + libname = m->second.value.to_string (); break; } } - if (! p->second.empty ()) { - tl::log << "Registering as '" << lib->get_name () << "' for tech '" << p->second << "'"; + // merge with existing lib if there is already one in this folder with the right name + auto il = libs_by_name_here.find (libname); + if (il != libs_by_name_here.end ()) { + + tl::log << "Merging with other library file with the same name: " << libname; + + db::Library *org_lib = il->second; + db::Layout &org_layout = org_lib->layout (); + + std::vector target_cells, source_cells; + + // collect the cells to pull in (all top cells of the library layout) + for (auto c = lib->layout ().begin_top_down (); c != lib->layout ().end_top_cells (); ++c) { + std::string cn = lib->layout ().cell_name (*c); + source_cells.push_back (*c); + target_cells.push_back (org_layout.add_cell (cn.c_str ())); + } + + db::CellMapping cm; + cm.create_multi_mapping_full (org_layout, target_cells, lib->layout (), source_cells); + org_layout.copy_tree_shapes (lib->layout (), cm); + + // now, we can forget the new library as it is included in the first one + } else { - tl::log << "Registering as '" << lib->get_name () << "'"; + + // otherwise register the new library + + if (! p->second.empty ()) { + tl::log << "Registering as '" << libname << "' for tech '" << p->second << "'"; + } else { + tl::log << "Registering as '" << libname << "'"; + } + + LibInfo li; + li.name = libname; + li.time = fi.lastModified (); + if (! p->second.empty ()) { + li.tech.insert (p->second); + } + new_lib_files.insert (std::make_pair (lib_path, li)); + + lib->set_name (libname); + libs_by_name_here.insert (std::make_pair (libname, lib.release ())); + } - LibInfo li; - li.name = lib->get_name (); - li.time = fi.lastModified (); - if (! p->second.empty ()) { - li.tech.insert (p->second); - } - new_lib_files.insert (std::make_pair (lib_path, li)); - - db::LibraryManager::instance ().register_lib (lib.release ()); - + } catch (tl::Exception &ex) { + tl::error << ex.msg (); } - } catch (tl::Exception &ex) { - tl::error << ex.msg (); + } + + // Register the libs (NOTE: this needs to happen after the merge) + for (auto l = libs_by_name_here.begin (); l != libs_by_name_here.end (); ++l) { + try { + db::LibraryManager::instance ().register_lib (l->second); + } catch (tl::Exception &ex) { + tl::error << ex.msg (); + } catch (...) { + } } } From 06aa0c48cde1bee5ddf2c9f59695a8d1f563b5f9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 19 Oct 2025 18:00:43 +0200 Subject: [PATCH 05/16] Commit local changes before merge --- src/lay/lay/layLibraryController.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/lay/lay/layLibraryController.cc b/src/lay/lay/layLibraryController.cc index f8a5b32f9..a1187a5ef 100644 --- a/src/lay/lay/layLibraryController.cc +++ b/src/lay/lay/layLibraryController.cc @@ -320,8 +320,15 @@ LibraryController::sync_files () try { std::pair li = db::LibraryManager::instance ().lib_by_name (lf->second.name, lf->second.tech); if (li.first) { + if (! lf->second.tech.empty ()) { + tl::log << "Unregistering lib '" << lf->second.name << "' for technology '" << *lf->second.tech.begin () << "' as the file no longer exists: " << lf->first; + } else { + tl::log << "Unregistering lib '" << lf->second.name << "' as the file no longer exists: " << lf->first; + } db::LibraryManager::instance ().delete_lib (db::LibraryManager::instance ().lib (li.second)); } + } catch (tl::Exception &ex) { + tl::error << ex.msg (); } catch (...) { } } From 0d09ef6df75158b54aaf0a84525953502cd79e50 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 19 Oct 2025 18:34:54 +0200 Subject: [PATCH 06/16] Refinement: do not merge library cells that are already present --- src/lay/lay/layLibraryController.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lay/lay/layLibraryController.cc b/src/lay/lay/layLibraryController.cc index 7324ead45..bbdcc8869 100644 --- a/src/lay/lay/layLibraryController.cc +++ b/src/lay/lay/layLibraryController.cc @@ -99,10 +99,14 @@ private: std::vector target_cells, source_cells; // collect the cells to pull in (all top cells of the library layout) + // NOTE: cells are not overwritten - the first layout wins, in terms + // of cell names and also in terms of database unit. for (auto c = ly.begin_top_down (); c != ly.end_top_cells (); ++c) { std::string cn = ly.cell_name (*c); - source_cells.push_back (*c); - target_cells.push_back (layout ().add_cell (cn.c_str ())); + if (! layout ().has_cell (cn.c_str ())) { + source_cells.push_back (*c); + target_cells.push_back (layout ().add_cell (cn.c_str ())); + } } db::CellMapping cm; From 48f66104571c1fecb9ffdd6d27084f2ccd955568 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 19 Oct 2025 18:49:08 +0200 Subject: [PATCH 07/16] Fixing Python module builds --- src/edt/edt/edtShapeService.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/edt/edt/edtShapeService.cc b/src/edt/edt/edtShapeService.cc index da8b7590c..5131fb8f2 100644 --- a/src/edt/edt/edtShapeService.cc +++ b/src/edt/edt/edtShapeService.cc @@ -28,7 +28,10 @@ #include "dbEdgeProcessor.h" #include "dbPolygonTools.h" -#include "layTipDialog.h" +#if defined(HAVE_QT) +# include "layTipDialog.h" +#endif + #include "layEditorOptionsPages.h" namespace edt From 6746dad08a0f1436eb95a1df1b82cbf5d7fc7531 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 19 Oct 2025 19:39:46 +0200 Subject: [PATCH 08/16] Fixed display of markers for ghost cells in viewer mode --- src/edt/edt/edtService.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index ceab2f786..8dbabaf7e 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -1208,7 +1208,7 @@ Service::transient_select (const db::DPoint &pos) // In viewer mode, individual instances of arrays can be selected. Since that is not supported by // InstanceMarker, we just indicate the individual instance's bounding box. lay::Marker *marker = new lay::Marker (view (), r->cv_index ()); - db::box_convert bc (cv->layout ()); + 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); marker->set_vertex_size (view ()->default_transient_marker_vertex_size ()); marker->set_line_width (view ()->default_transient_marker_line_width ()); @@ -1806,7 +1806,7 @@ Service::do_selection_to_view () if (r->seq () > 0 && m_indicate_secondary_selection) { marker->set_dither_pattern (3); } - db::box_convert bc (cv->layout ()); + 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)); From a1d6ff9a3cb9bb72a1c331db9d7e1faa5ee32880 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 20 Oct 2025 22:57:03 +0200 Subject: [PATCH 09/16] Selectability follows visibility --- src/edt/edt/edtService.cc | 4 ++++ src/laybasic/laybasic/layFinder.cc | 30 ++++++++++++++++++++---------- src/laybasic/laybasic/layFinder.h | 13 +++++++++++++ 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index 8dbabaf7e..e9fd9b520 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -1159,6 +1159,8 @@ Service::transient_select (const db::DPoint &pos) if (m_cell_inst_service) { 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*/); + finder.consider_ghost_cells (view ()->ghost_cells_visible ()); + finder.consider_normal_cells (view ()->cell_box_visible ()); // go through all transform variants std::set< std::pair > variants = view ()->cv_transform_variants_with_empty (); @@ -1493,6 +1495,8 @@ Service::select (const db::DBox &box, lay::Editable::SelectionMode mode) } else if (m_cell_inst_service) { 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*/); + finder.consider_ghost_cells (view ()->ghost_cells_visible ()); + finder.consider_normal_cells (view ()->cell_box_visible ()); // go through all cell views std::set< std::pair > variants = view ()->cv_transform_variants_with_empty (); diff --git a/src/laybasic/laybasic/layFinder.cc b/src/laybasic/laybasic/layFinder.cc index 72884fa33..f3083e2d8 100644 --- a/src/laybasic/laybasic/layFinder.cc +++ b/src/laybasic/laybasic/layFinder.cc @@ -721,6 +721,8 @@ InstFinder::InstFinder (bool point_mode, bool top_level_sel, bool full_arrays, b m_full_arrays (full_arrays), m_enclose_insts (enclose_inst), m_visible_layers (visible_layers), + m_consider_ghost_cells (true), + m_consider_normal_cells (true), mp_view (0), mp_progress (0) { @@ -805,6 +807,12 @@ InstFinder::checkpoint () } } +bool +InstFinder::consider_cell (const db::Cell &cell) const +{ + return cell.is_ghost_cell () ? m_consider_ghost_cells : m_consider_normal_cells; +} + void InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const db::Box & /*scan_box*/, const db::DCplxTrans &vp, const db::ICplxTrans &t, int level) { @@ -818,15 +826,18 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d ++*mp_progress; // look for instances to check here .. - db::Cell::touching_iterator inst = cell.begin_touching (search_box); - while (! inst.at_end ()) { + for (db::Cell::touching_iterator inst = cell.begin_touching (search_box); ! inst.at_end (); ++inst) { const db::CellInstArray &cell_inst = inst->cell_inst (); const db::Cell &inst_cell = layout ().cell (cell_inst.object ().cell_index ()); ++*mp_progress; - // just consider the instances exactly at the last level of + if (! consider_cell (inst_cell)) { + continue; + } + + // 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)) { @@ -887,21 +898,22 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d } - ++inst; - } } else { // look for instances to check here .. - db::Cell::touching_iterator inst = cell.begin_touching (search_box); - while (! inst.at_end ()) { + for (db::Cell::touching_iterator inst = cell.begin_touching (search_box); ! inst.at_end (); ++inst) { checkpoint (); const db::CellInstArray &cell_inst = inst->cell_inst (); const db::Cell &inst_cell = layout ().cell (cell_inst.object ().cell_index ()); + if (! consider_cell (inst_cell)) { + continue; + } + // 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. @@ -909,7 +921,7 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d db::box_convert bc (layout ()); for (db::CellInstArray::iterator p = cell_inst.begin_touching (search_box, bc); ! p.at_end (); ++p) { - + checkpoint (); bool match = false; @@ -1000,8 +1012,6 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d } - ++inst; - } } diff --git a/src/laybasic/laybasic/layFinder.h b/src/laybasic/laybasic/layFinder.h index d93e0c9f1..f0ecdcc46 100644 --- a/src/laybasic/laybasic/layFinder.h +++ b/src/laybasic/laybasic/layFinder.h @@ -343,7 +343,17 @@ public: bool find (LayoutViewBase *view, unsigned int cv_index, const db::DCplxTrans &trans, const db::DBox ®ion_mu); bool find (LayoutViewBase *view, const db::DBox ®ion_mu); + + void consider_ghost_cells (bool f) + { + m_consider_ghost_cells = f; + } + void consider_normal_cells (bool f) + { + m_consider_normal_cells = f; + } + iterator begin () const { return m_founds.begin (); @@ -360,6 +370,7 @@ private: virtual void visit_cell (const db::Cell &cell, const db::Box &hit_box, const db::Box &scan_box, const db::DCplxTrans &vp, const db::ICplxTrans &t, int level); bool find_internal (LayoutViewBase *view, unsigned int cv_index, const db::DCplxTrans &trans_mu, const db::DBox ®ion_mu); + bool consider_cell (const db::Cell &cell) const; unsigned int m_cv_index; db::cell_index_type m_topcell; @@ -369,6 +380,8 @@ private: bool m_full_arrays; bool m_enclose_insts; bool m_visible_layers; + bool m_consider_ghost_cells; + bool m_consider_normal_cells; std::vector m_visible_layer_indexes; LayoutViewBase *mp_view; tl::AbsoluteProgress *mp_progress; From b4371d14ca4801b213f5fea266a18e55ce69f533 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 23 Oct 2025 19:19:06 +0200 Subject: [PATCH 10/16] Fixing TextInfo bbox in case of oversampling --- src/laybasic/laybasic/gsiDeclLayTextInfo.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/laybasic/laybasic/gsiDeclLayTextInfo.cc b/src/laybasic/laybasic/gsiDeclLayTextInfo.cc index a60d1a21c..1f1c1f3a6 100644 --- a/src/laybasic/laybasic/gsiDeclLayTextInfo.cc +++ b/src/laybasic/laybasic/gsiDeclLayTextInfo.cc @@ -139,7 +139,7 @@ public: } - db::DCplxTrans vp_trans = mp_view->viewport ().trans () * tv_trans; + db::DCplxTrans vp_trans = db::DCplxTrans (double (mp_view->canvas ()->oversampling ())) * mp_view->viewport ().trans () * tv_trans; db::DBox box = m_textinfo.bbox (ctx_trans * dtext, vp_trans); double b = m_border / vp_trans.mag (); From e6e85ab3b34ccf69399db19caa8160814a2d72ca Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 23 Oct 2025 21:49:24 +0200 Subject: [PATCH 11/16] FIxing issue #2194 (can't attach key binding to 'forward'/'backward') --- src/laybasic/laybasic/layAbstractMenu.h | 5 +++++ src/laybasic/laybasic/layPlugin.cc | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/src/laybasic/laybasic/layAbstractMenu.h b/src/laybasic/laybasic/layAbstractMenu.h index 59a16cd6c..f66334425 100644 --- a/src/laybasic/laybasic/layAbstractMenu.h +++ b/src/laybasic/laybasic/layAbstractMenu.h @@ -621,6 +621,11 @@ struct LAYBASIC_PUBLIC AbstractMenuItem return m_primary; } + void set_primary (bool p) + { + m_primary = p; + } + std::list children; private: diff --git a/src/laybasic/laybasic/layPlugin.cc b/src/laybasic/laybasic/layPlugin.cc index 842351536..48a29f9cd 100644 --- a/src/laybasic/laybasic/layPlugin.cc +++ b/src/laybasic/laybasic/layPlugin.cc @@ -197,6 +197,12 @@ PluginDeclaration::init_menu (lay::Dispatcher *dispatcher) if (! m->copy_from.empty ()) { + // As a general strategy for now we take primary ownership from the copy source as pass it to + // the copy. This is important for "next/prev_display_state" as the first registry is @toolbar + // (which is not accessible by Setup menu) and the second one is "zoom_menu" which should be + // the primary one. For later, we may want to make this configurable (move/leave primary flag). + menu.find_item_exact (m->copy_from)->set_primary (false); + menu.insert_item (m->insert_pos, m->menu_name, menu.action (m->copy_from)); } else if (m->separator) { From e8e2858af30692fd6772aa2695af9a565d8f0997 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 25 Oct 2025 00:21:04 +0200 Subject: [PATCH 12/16] Implemented a solution for #2195 A new PCell method cell_name/cell_name_impl that delivers a cell name, which is used for "convert to static cell" and as cell name in general. Needs testing. --- .../pcell_declaration_helper.lym | 30 +++++++++++++++++ .../pcell_declaration_helper.lym | 12 +++++++ src/db/db/dbCell.cc | 6 ++++ src/db/db/dbCell.h | 10 +++++- src/db/db/dbColdProxy.cc | 2 +- src/db/db/dbLayout.cc | 25 ++++++++++++--- src/db/db/dbLayout.h | 13 ++++++-- src/db/db/dbLibraryProxy.cc | 18 ++++++++++- src/db/db/dbLibraryProxy.h | 17 ++++++---- src/db/db/dbPCellDeclaration.h | 12 +++++++ src/db/db/dbPCellVariant.cc | 14 +++++++- src/db/db/dbPCellVariant.h | 32 ++++++++----------- src/db/db/gsiDeclDbLibrary.cc | 23 +++++++++++++ .../klayout/db/pcell_declaration_helper.py | 21 ++++++++++++ 14 files changed, 199 insertions(+), 36 deletions(-) diff --git a/src/db/db/built-in-macros/pcell_declaration_helper.lym b/src/db/db/built-in-macros/pcell_declaration_helper.lym index 6a9c21f9d..1db044b21 100644 --- a/src/db/db/built-in-macros/pcell_declaration_helper.lym +++ b/src/db/db/built-in-macros/pcell_declaration_helper.lym @@ -149,6 +149,18 @@ created as well. This method must be reimplemented in a PCell class to identify the PCell in human-readable form. This text is shown in the cell tree for the PCell for example. +@method cell_name_impl + +@brief Delivers the cell name to be used for the PCell variant + +A PCell variant is represented in the cell tree by a placeholder cell. By +default, the name of this cell is the PCell name. Since multiple variants +may exist, usually a disambiguator is added to the name (e.g. "..$1"). + +This method allows encoding the PCell parameters into that cell name, +so the PCell variant is easier to identify in the cell tree - for example +in the GDS file - instead of the unspecific disambiguator. + @method produce_impl @brief Produces the layout @@ -466,6 +478,19 @@ module RBA text end + # implementation of display_text + def cell_name(parameters) + self._start + @param_values = parameters + text = "" + begin + text = cell_name_impl + ensure + self._finish + end + text + end + # get the parameters def get_parameters @param_decls @@ -568,6 +593,11 @@ module RBA "" end + # default implementation + def cell_name_impl + self.name + end + # default implementation def coerce_parameters_impl end diff --git a/src/db/db/built-in-pymacros/pcell_declaration_helper.lym b/src/db/db/built-in-pymacros/pcell_declaration_helper.lym index 39b4c4807..8b763e6e7 100644 --- a/src/db/db/built-in-pymacros/pcell_declaration_helper.lym +++ b/src/db/db/built-in-pymacros/pcell_declaration_helper.lym @@ -158,6 +158,18 @@ created as well. This method must be reimplemented in a PCell class to identify the PCell in human-readable form. This text is shown in the cell tree for the PCell for example. +@method cell_name_impl + +@brief Delivers the cell name to be used for the PCell variant + +A PCell variant is represented in the cell tree by a placeholder cell. By +default, the name of this cell is the PCell name. Since multiple variants +may exist, usually a disambiguator is added to the name (e.g. "..$1"). + +This method allows encoding the PCell parameters into that cell name, +so the PCell variant is easier to identify in the cell tree - for example +in the GDS file - instead of the unspecific disambiguator. + @method produce_impl @brief Produces the layout diff --git a/src/db/db/dbCell.cc b/src/db/db/dbCell.cc index 629ae4e92..cb56e7354 100644 --- a/src/db/db/dbCell.cc +++ b/src/db/db/dbCell.cc @@ -862,6 +862,12 @@ Cell::get_basic_name () const return layout ()->cell_name (cell_index ()); } +std::string +Cell::get_variant_name () const +{ + return get_basic_name (); +} + std::string Cell::get_qualified_name () const { diff --git a/src/db/db/dbCell.h b/src/db/db/dbCell.h index 2a64cdf23..7ba54fc71 100644 --- a/src/db/db/dbCell.h +++ b/src/db/db/dbCell.h @@ -890,7 +890,7 @@ public: void set_name (const std::string &name); /** - * @brief Get the basic name + * @brief Gets the basic name * * The basic name of the cell is either the cell name or the cell name in the * target library (for library proxies) or the PCell name (for PCell proxies). @@ -898,6 +898,14 @@ public: */ virtual std::string get_basic_name () const; + /** + * @brief Gets the variant name + * + * The variant name is the PCell's "cell_name" - which may encode the PCell parameters + * into a formal name. Usually that is identical to the PCell name. + */ + virtual std::string get_variant_name () const; + /** * @brief Gets the display name * diff --git a/src/db/db/dbColdProxy.cc b/src/db/db/dbColdProxy.cc index 3ccb5806a..e83c712eb 100644 --- a/src/db/db/dbColdProxy.cc +++ b/src/db/db/dbColdProxy.cc @@ -89,7 +89,7 @@ ColdProxy::get_basic_name () const } } -std::string +std::string ColdProxy::get_display_name () const { if (! mp_context_info->lib_name.empty ()) { diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index bf2a84e0e..e7c93b663 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -2462,7 +2462,7 @@ Layout::get_pcell_variant_dict (pcell_id_type pcell_id, const std::mapget_variant (*this, parameters); if (! variant) { - std::string b (header->get_name ()); + std::string b (header->declaration ()->get_cell_name (parameters)); if (m_cell_map.find (b.c_str ()) != m_cell_map.end ()) { b = uniquify_cell_name (b.c_str ()); } @@ -2501,7 +2501,7 @@ Layout::get_pcell_variant (pcell_id_type pcell_id, const std::vectorget_variant (*this, parameters); if (! variant) { - std::string b (header->get_name ()); + std::string b (header->declaration ()->get_cell_name (parameters)); if (m_cell_map.find (b.c_str ()) != m_cell_map.end ()) { b = uniquify_cell_name (b.c_str ()); } @@ -2627,9 +2627,18 @@ Layout::convert_cell_to_static (db::cell_index_type ci) const cell_type &org_cell = cell (ci); - // Note: convert to static cell by explicitly cloning to the db::Cell class - ret_ci = add_cell (org_cell.get_basic_name ().c_str ()); + + std::string vn = org_cell.get_variant_name (); + if (vn == cell_name (ci)) { + // there is a cell name conflict: give priority to the static cell, so it + // will see the variant name + std::string rename_org = uniquify_cell_name (cell_name (ci)); + rename_cell (ci, rename_org.c_str ()); + } + + ret_ci = add_cell (vn.c_str ()); cell_type &new_cell = cell (ret_ci); + // Note: we convert to static cell by explicitly cloning to the db::Cell class new_cell = org_cell; new_cell.set_cell_index (ret_ci); @@ -3126,6 +3135,12 @@ Layout::basic_name (cell_index_type cell_index) const return cell (cell_index).get_basic_name (); } +std::string +Layout::variant_name (cell_index_type cell_index) const +{ + return cell (cell_index).get_variant_name (); +} + void Layout::register_lib_proxy (db::LibraryProxy *lib_proxy) { @@ -3161,7 +3176,7 @@ Layout::get_lib_proxy (Library *lib, cell_index_type cell_index) } else { // create a new unique name - std::string b (lib->layout ().basic_name (cell_index)); + std::string b (lib->layout ().variant_name (cell_index)); if (m_cell_map.find (b.c_str ()) != m_cell_map.end ()) { b = uniquify_cell_name (b.c_str ()); } diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 4c0d0078a..9104d758d 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -675,7 +675,7 @@ public: std::string display_name (cell_index_type cell_index) const; /** - * @brief Return the basic name for the given cell + * @brief Returns the basic name for the given cell * * This method is forwarded to the respective method of the cell. * The basic name is the "original" cell name within the library or @@ -684,7 +684,16 @@ public: */ std::string basic_name (cell_index_type cell_index) const; - /** + /** + * @brief Returns the variant name for the given cell + * + * The variant name usually is the basic name. For PCells, this name + * can encode PCell parameters, depending on the definition of the + * PCell. + */ + std::string variant_name (cell_index_type cell_index) const; + + /** * @brief Add a cell object with the given ID and name * * This method is basically supposed to be used for "undo" and "redo". diff --git a/src/db/db/dbLibraryProxy.cc b/src/db/db/dbLibraryProxy.cc index 87db31672..c44a319c8 100644 --- a/src/db/db/dbLibraryProxy.cc +++ b/src/db/db/dbLibraryProxy.cc @@ -257,7 +257,23 @@ LibraryProxy::get_basic_name () const } } -std::string +std::string +LibraryProxy::get_variant_name () const +{ + Library *lib = LibraryManager::instance ().lib (lib_id ()); + if (lib) { + if (! lib->layout ().is_valid_cell_index (library_cell_index ())) { + return ""; + } else { + const db::Cell &lib_cell = lib->layout ().cell (library_cell_index ()); + return lib_cell.get_variant_name (); + } + } else { + return Cell::get_variant_name (); + } +} + +std::string LibraryProxy::get_display_name () const { Library *lib = LibraryManager::instance ().lib (lib_id ()); diff --git a/src/db/db/dbLibraryProxy.h b/src/db/db/dbLibraryProxy.h index e1f026ed7..8c72787cc 100644 --- a/src/db/db/dbLibraryProxy.h +++ b/src/db/db/dbLibraryProxy.h @@ -96,25 +96,28 @@ public: /** * @brief Gets the basic name * - * The basic name of the cell is either the cell name or the cell name in the - * target library (for library proxies) or the PCell name (for PCell proxies). - * The actual name may be different by a extension to make it unique. + * This returns the basic name of the proxy target */ virtual std::string get_basic_name () const; + /** + * @brief Gets the variant name + * + * This returns the basic name of the proxy target + */ + virtual std::string get_variant_name () const; + /** * @brief Gets the display name * - * The display name is some "nice" descriptive name of the cell (variant) - * For normal cells this name is equivalent to the normal cell name. + * This returns the basic name of the proxy target */ virtual std::string get_display_name () const; /** * @brief Gets the qualified name * - * The qualified name for a library proxy is made from the library name, a - * dot and the cell's name. + * Gets a combination of the library name and the target cell's qualified name */ virtual std::string get_qualified_name () const; diff --git a/src/db/db/dbPCellDeclaration.h b/src/db/db/dbPCellDeclaration.h index 7db690f4c..a75718d83 100644 --- a/src/db/db/dbPCellDeclaration.h +++ b/src/db/db/dbPCellDeclaration.h @@ -673,6 +673,18 @@ public: return std::string (); } + /** + * @brief Gets a cell name for the PCell, which can depend on the parameters + * + * The actual cell name in the layout may differ by disambiguation. This method + * delivers a proposal for a cell name. + * By default, the PCell name is returned. + */ + virtual std::string get_cell_name (const pcell_parameters_type &) const + { + return m_name; + } + /** * @brief Returns the description text of the PCell * diff --git a/src/db/db/dbPCellVariant.cc b/src/db/db/dbPCellVariant.cc index b401c8471..bebda8921 100644 --- a/src/db/db/dbPCellVariant.cc +++ b/src/db/db/dbPCellVariant.cc @@ -86,7 +86,18 @@ PCellVariant::get_basic_name () const } } -std::string +std::string +PCellVariant::get_variant_name () const +{ + const PCellHeader *header = pcell_header (); + if (header) { + return m_variant_name; + } else { + return Cell::get_basic_name (); + } +} + +std::string PCellVariant::get_display_name () const { const PCellHeader *header = pcell_header (); @@ -172,6 +183,7 @@ PCellVariant::update (ImportLayerMapping *layer_mapping) header->declaration ()->produce (*layout (), layer_ids, plist, *this); m_display_name = header->declaration ()->get_display_name (plist); + m_variant_name = header->declaration ()->get_cell_name (plist); } catch (tl::Exception &ex) { diff --git a/src/db/db/dbPCellVariant.h b/src/db/db/dbPCellVariant.h index b4de1a1f5..c20aafeaf 100644 --- a/src/db/db/dbPCellVariant.h +++ b/src/db/db/dbPCellVariant.h @@ -94,42 +94,37 @@ public: } /** - * @brief Get the basic name - * - * The basic name of the cell is either the cell name or the cell name in the - * target library (for library proxies) or the PCell name (for PCell proxies). - * The actual name may be different by a extension to make it unique. + * @brief Gets the basic name */ virtual std::string get_basic_name () const; /** - * @brief Get the display name - * - * The display name is some "nice" descriptive name of the cell (variant) - * For normal cells this name is equivalent to the normal cell name. + * @brief Gets the variant name + */ + virtual std::string get_variant_name () const; + + /** + * @brief Gets the display name */ virtual std::string get_display_name () const; /** - * @brief Unregister a cell from it's context. + * @brief Unregisters a cell from it's context. */ virtual void unregister (); /** - * @brief Reregister a cell inside it's context. + * @brief Re-registers a cell inside it's context. */ virtual void reregister (); /** - * @brief Update the layout + * @brief Updates the layout */ virtual void update (ImportLayerMapping *layer_mapping = 0); /** - * @brief Tell, if this cell is a proxy cell - * - * Proxy cells are such whose layout represents a snapshot of another entity. - * Such cells can be PCell variants or library references for example. + * @brief Gets a value indicating if this cell is a proxy cell */ virtual bool is_proxy () const { @@ -138,7 +133,7 @@ public: protected: /** - * @brief Get the PCell header for this variant + * @brief Gets the PCell header for this variant */ PCellHeader *pcell_header () { @@ -146,7 +141,7 @@ protected: } /** - * @brief Get the PCell header for this variant + * @brief Gets the PCell header for this variant */ const PCellHeader *pcell_header () const { @@ -156,6 +151,7 @@ protected: private: pcell_parameters_type m_parameters; mutable std::string m_display_name; + mutable std::string m_variant_name; db::pcell_id_type m_pcell_id; bool m_registered; }; diff --git a/src/db/db/gsiDeclDbLibrary.cc b/src/db/db/gsiDeclDbLibrary.cc index d06caba5f..2693b0bb5 100644 --- a/src/db/db/gsiDeclDbLibrary.cc +++ b/src/db/db/gsiDeclDbLibrary.cc @@ -368,6 +368,7 @@ Class decl_PCellDeclaration_Native ("db", "PCellDeclaratio gsi::method ("via_types", &db::PCellDeclaration::via_types) + gsi::method ("description", &db::PCellDeclaration::get_description) + gsi::method ("display_text", &db::PCellDeclaration::get_display_name, gsi::arg ("parameters")) + + gsi::method ("cell_name", &db::PCellDeclaration::get_cell_name, gsi::arg ("parameters")) + gsi::method ("layout", &db::PCellDeclaration::layout, "@brief Gets the Layout object the PCell is registered in or nil if it is not registered yet.\n" "This attribute has been added in version 0.27.5." @@ -659,6 +660,20 @@ public: } } + std::string get_cell_name_fb (const db::pcell_parameters_type ¶meters) const + { + return db::PCellDeclaration::get_cell_name (parameters); + } + + virtual std::string get_cell_name (const db::pcell_parameters_type ¶meters) const + { + if (cb_get_cell_name.can_issue ()) { + return cb_get_cell_name.issue (&db::PCellDeclaration::get_cell_name, parameters); + } else { + return db::PCellDeclaration::get_cell_name (parameters); + } + } + gsi::Callback cb_get_layer_declarations; gsi::Callback cb_get_parameter_declarations; gsi::Callback cb_produce; @@ -669,6 +684,7 @@ public: gsi::Callback cb_coerce_parameters; gsi::Callback cb_callback; gsi::Callback cb_get_display_name; + gsi::Callback cb_get_cell_name; gsi::Callback cb_get_description; gsi::Callback cb_via_types; }; @@ -809,6 +825,13 @@ Class decl_PCellDeclaration (decl_PCellDeclaration_Native, "@brief Returns the display text for this PCell given a certain parameter set\n" "Reimplement this method to create a distinct display text for a PCell variant with \n" "the given parameter set. If this method is not implemented, a default text is created. \n" + ) + + gsi::callback ("cell_name", &PCellDeclarationImpl::get_cell_name, &PCellDeclarationImpl::cb_get_cell_name, gsi::arg ("parameters"), + "@brief Returns a cell name used for the PCell variant\n" + "Reimplement this method to create a cell name the system uses for the PCell variant. By default that is the PCell name.\n" + "This feature allows encoding the PCell parameters into the cell name for easier identification of the PCell variant in a cell tree.\n" + "\n" + "This feature has been added in version 0.30.5.\n" ), "@brief A PCell declaration providing the parameters and code to produce the PCell\n" "\n" diff --git a/src/pymod/distutils_src/klayout/db/pcell_declaration_helper.py b/src/pymod/distutils_src/klayout/db/pcell_declaration_helper.py index 773ff03c7..6a89fc446 100644 --- a/src/pymod/distutils_src/klayout/db/pcell_declaration_helper.py +++ b/src/pymod/distutils_src/klayout/db/pcell_declaration_helper.py @@ -138,6 +138,21 @@ class _PCellDeclarationHelperMixin: self._finish() return text + def cell_name(self, parameters): + """ + Reimplementation of PCellDeclaration.cell_name + + This function delegates the implementation to self.cell_name_impl + after configuring the PCellDeclaration object. + """ + self._start() + self._param_values = parameters + try: + text = self.cell_name_impl() + finally: + self._finish() + return text + def get_parameters(self): """ Reimplementation of PCellDeclaration.get_parameters @@ -333,6 +348,12 @@ class _PCellDeclarationHelperMixin: """ return "" + def cell_name_impl(self): + """ + default implementation + """ + return self.name() + def coerce_parameters_impl(self): """ default implementation From b523f05f8084441712632944218dfc937bb9160a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 25 Oct 2025 00:33:25 +0200 Subject: [PATCH 13/16] Some typos fixed --- src/db/db/built-in-macros/pcell_declaration_helper.lym | 2 +- src/db/db/dbPCellVariant.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/db/db/built-in-macros/pcell_declaration_helper.lym b/src/db/db/built-in-macros/pcell_declaration_helper.lym index 1db044b21..774a33028 100644 --- a/src/db/db/built-in-macros/pcell_declaration_helper.lym +++ b/src/db/db/built-in-macros/pcell_declaration_helper.lym @@ -478,7 +478,7 @@ module RBA text end - # implementation of display_text + # implementation of cell_name def cell_name(parameters) self._start @param_values = parameters diff --git a/src/db/db/dbPCellVariant.h b/src/db/db/dbPCellVariant.h index c20aafeaf..a949bd0a2 100644 --- a/src/db/db/dbPCellVariant.h +++ b/src/db/db/dbPCellVariant.h @@ -109,12 +109,12 @@ public: virtual std::string get_display_name () const; /** - * @brief Unregisters a cell from it's context. + * @brief Unregisters a cell from its context. */ virtual void unregister (); /** - * @brief Re-registers a cell inside it's context. + * @brief Re-registers a cell inside its context. */ virtual void reregister (); From fc3185165fe329ff60a7a447bda640b2d40d0c10 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 25 Oct 2025 17:16:13 +0200 Subject: [PATCH 14/16] Added some tests --- src/db/db/dbLayout.cc | 10 +-- src/db/db/gsiDeclDbLibrary.cc | 1 + src/db/unit_tests/dbPCellsTests.cc | 118 +++++++++++++++++++++++++++++ testdata/gds/issue_1835_au.gds | Bin 660 -> 658 bytes testdata/gds/pcell_test20.gds | Bin 0 -> 1138 bytes testdata/python/dbPCells.py | 12 +++ testdata/ruby/dbPCells.rb | 46 +++++++++++ 7 files changed, 182 insertions(+), 5 deletions(-) create mode 100644 testdata/gds/pcell_test20.gds diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index e7c93b663..fbf1abf14 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -2627,18 +2627,18 @@ Layout::convert_cell_to_static (db::cell_index_type ci) const cell_type &org_cell = cell (ci); - std::string vn = org_cell.get_variant_name (); - if (vn == cell_name (ci)) { + if (vn == std::string (cell_name (ci), vn.size ())) { // there is a cell name conflict: give priority to the static cell, so it - // will see the variant name - std::string rename_org = uniquify_cell_name (cell_name (ci)); + // will see the variant name or at least the original disambiguated name + std::string rename_org = uniquify_cell_name (vn.c_str ()); + vn = cell_name (ci); rename_cell (ci, rename_org.c_str ()); } ret_ci = add_cell (vn.c_str ()); cell_type &new_cell = cell (ret_ci); - // Note: we convert to static cell by explicitly cloning to the db::Cell class + // Note: we convert to static cell by explicitly converting to the db::Cell class new_cell = org_cell; new_cell.set_cell_index (ret_ci); diff --git a/src/db/db/gsiDeclDbLibrary.cc b/src/db/db/gsiDeclDbLibrary.cc index 2693b0bb5..774e92d40 100644 --- a/src/db/db/gsiDeclDbLibrary.cc +++ b/src/db/db/gsiDeclDbLibrary.cc @@ -698,6 +698,7 @@ Class decl_PCellDeclaration (decl_PCellDeclaration_Native, gsi::method ("parameters_from_shape", &PCellDeclarationImpl::parameters_from_shape_fb, "@hide") + gsi::method ("transformation_from_shape", &PCellDeclarationImpl::transformation_from_shape_fb, "@hide") + gsi::method ("display_text", &PCellDeclarationImpl::get_display_name_fb, "@hide") + + gsi::method ("cell_name", &PCellDeclarationImpl::get_cell_name_fb, "@hide") + gsi::method ("wants_lazy_evaluation", &PCellDeclarationImpl::wants_lazy_evaluation_fb, "@hide") + gsi::method ("description", &PCellDeclarationImpl::get_description_fb, "@hide") + gsi::method ("via_types", &PCellDeclarationImpl::via_types_fb, "@hide") + diff --git a/src/db/unit_tests/dbPCellsTests.cc b/src/db/unit_tests/dbPCellsTests.cc index 9304b9aff..dc41a4c8b 100644 --- a/src/db/unit_tests/dbPCellsTests.cc +++ b/src/db/unit_tests/dbPCellsTests.cc @@ -88,6 +88,13 @@ class PD cell.shapes (l_metal0).insert (db::Box (0, 0, width, height)); } + + virtual std::string get_display_name (const db::pcell_parameters_type ¶meters) const + { + db::Coord width = db::coord_traits::rounded (parameters[0].to_double () * 1000.0); + db::Coord height = db::coord_traits::rounded (parameters[1].to_double () * 1000.0); + return tl::sprintf ("PD(W=%d,H=%d)", width, height); + } }; TEST(0) @@ -252,3 +259,114 @@ TEST(1) } } +// PCell names, convert PCell to static + +class PD2 + : public PD +{ +public: + virtual std::string get_cell_name (const db::pcell_parameters_type ¶meters) const + { + db::Coord width = db::coord_traits::rounded (parameters[0].to_double () * 1000.0); + db::Coord height = db::coord_traits::rounded (parameters[1].to_double () * 1000.0); + return tl::sprintf ("PD_W%d_H%d", width, height); + } +}; + +TEST(2) +{ + db::Manager m (true); + db::Layout layout(&m); + layout.dbu (0.001); + + db::LayerProperties p; + + p.layer = 23; + p.datatype = 0; + unsigned int l_cont = layout.insert_layer (p); + + p.layer = 16; + p.datatype = 0; + unsigned int l_gate = layout.insert_layer (p); + + db::Cell &cell_a = layout.cell (layout.add_cell ("A")); + cell_a.shapes(l_cont).insert(db::Box (50, 50, 150, 150)); + cell_a.shapes(l_gate).insert(db::Box (0, 0, 200, 1000)); + + db::Cell &top = layout.cell (layout.add_cell ("TOP")); + + db::pcell_id_type pd = layout.register_pcell ("PD", new PD2 ()); + + std::vector parameters; + parameters.push_back (tl::Variant ()); + parameters.push_back (tl::Variant ()); + parameters.push_back (tl::Variant ()); + tl::Variant &width = parameters[0]; + tl::Variant &height = parameters[1]; + tl::Variant &orientation = parameters[2]; + + width = 0.5; + height = 1.0; + orientation = long (0); + + db::cell_index_type pd1 = layout.get_pcell_variant (pd, parameters); + top.insert (db::CellInstArray (db::CellInst (pd1), db::Trans (db::Vector (0, 0)))); + + EXPECT_EQ (layout.display_name (pd1), "PD(W=500,H=1000)"); + EXPECT_EQ (layout.cell_name (pd1), "PD_W500_H1000"); + EXPECT_EQ (layout.variant_name (pd1), "PD_W500_H1000"); + + width = 0.4; + height = 0.8; + + db::cell_index_type pd2 = layout.get_pcell_variant (pd, parameters); + top.insert (db::CellInstArray (db::CellInst (pd2), db::Trans (db::Vector (0, 2000)))); + + EXPECT_EQ (layout.display_name (pd2), "PD(W=400,H=800)"); + EXPECT_EQ (layout.cell_name (pd2), "PD_W400_H800"); + EXPECT_EQ (layout.variant_name (pd2), "PD_W400_H800"); + + EXPECT_NE (pd1, pd2); + + width = 0.4; + height = 0.8; + orientation = long (1); + + db::cell_index_type pd3 = layout.get_pcell_variant (pd, parameters); + auto i3 = top.insert (db::CellInstArray (db::CellInst (pd3), db::Trans (db::Vector (2000, 0)))); + + EXPECT_EQ (layout.display_name (pd3), "PD(W=400,H=800)"); + EXPECT_EQ (layout.cell_name (pd3), "PD_W400_H800$1"); + EXPECT_EQ (layout.variant_name (pd3), "PD_W400_H800"); + + EXPECT_NE (pd2, pd3); + + auto pd3_org = pd3; + pd3 = layout.convert_cell_to_static (pd3); + EXPECT_NE (pd3, pd3_org); + + auto ci3 = i3.cell_inst (); + ci3.object ().cell_index (pd3); + top.replace (i3, ci3); + + EXPECT_EQ (layout.cell (pd3_org).is_proxy (), true); + EXPECT_EQ (layout.cell (pd3).is_proxy (), false); + + EXPECT_EQ (layout.display_name (pd3_org), "PD(W=400,H=800)"); + EXPECT_EQ (layout.cell_name (pd3_org), "PD_W400_H800$2"); + EXPECT_EQ (layout.variant_name (pd3_org), "PD_W400_H800"); + + layout.do_cleanup (true); + layout.cleanup (); + + EXPECT_EQ (layout.is_valid_cell_index (pd3_org), false); + + EXPECT_EQ (layout.display_name (pd3), "PD_W400_H800$1"); + EXPECT_EQ (layout.cell_name (pd3), "PD_W400_H800$1"); + EXPECT_EQ (layout.variant_name (pd3), "PD_W400_H800$1"); + + + + CHECKPOINT (); + db::compare_layouts (_this, layout, tl::testdata () + "/gds/pcell_test20.gds", db::NoNormalization); +} diff --git a/testdata/gds/issue_1835_au.gds b/testdata/gds/issue_1835_au.gds index 8770350b017a47331bde24b3dd860fe9c725415c..6d0a52e9a4fbeb98874bc7b34a0367231725382e 100644 GIT binary patch delta 115 zcmbQjI*B!lfsKKQDS|{L4=vr&au{L4=vr&auiN#URPR&tS$Njm&1?U}E#}bYfr-VP>^+>@@d2w)}&o%MSeo zv!g;7WLR9rIjMQ+Fij>53@m~`40MAK#0{n(Hy9cK9YK^U1W;UINRAs! zp>AMjz#DpOY;2A|l^j67@G>!oGqCY70fR^f98yL=77R}VvXL=N+z8@RsD1$y{jjhG z(#$V_#0dnABn1vB0M;{fPWu&H>V36`gUDFN&+pb8Lxxr>E`0RR%7d|Lnj literal 0 HcmV?d00001 diff --git a/testdata/python/dbPCells.py b/testdata/python/dbPCells.py index 41881f415..a7283c9cc 100644 --- a/testdata/python/dbPCells.py +++ b/testdata/python/dbPCells.py @@ -27,6 +27,10 @@ class BoxPCell(pya.PCellDeclaration): # provide a descriptive text for the cell return "Box(L=" + str(parameters[0]) + ",W=" + ('%.3f' % parameters[1]) + ",H=" + ('%.3f' % parameters[2]) + ")" + def cell_name(self, parameters): + # provide a descriptive text for the cell + return "Box_L" + str(parameters[0]).replace("/", "d") + "_W" + ('%.3f' % parameters[1]).replace(".", "p") + "_H" + ('%.3f' % parameters[2]).replace(".", "p") + def get_parameters(self): # prepare a set of parameter declarations @@ -100,6 +104,10 @@ if "PCellDeclarationHelper" in pya.__dict__: # provide a descriptive text for the cell return "Box2(L=" + str(self.layer) + ",W=" + ('%.3f' % self.width) + ",H=" + ('%.3f' % self.height) + ")" + def cell_name_impl(self): + # provide a descriptive text for the cell + return "Box2_L" + str(self.layer).replace("/", "d") + "_W" + ('%.3f' % self.width).replace(".", "p") + "_H" + ('%.3f' % self.height).replace(".", "p") + def wants_lazy_evaluation(self): return True @@ -260,6 +268,8 @@ class DBPCellTests(unittest.TestCase): self.assertEqual(pcell_var.is_pcell_variant(), True) self.assertEqual(pcell_var.display_title(), "PCellTestLib.Box(L=1/0,W=1.000,H=1.000)") self.assertEqual(pcell_var.basic_name(), "Box") + self.assertEqual(pcell_var.qname(), "PCellTestLib.Box") + self.assertEqual(pcell_var.name, "Box_L1d0_W1p000_H1p000") self.assertEqual(pcell_var.pcell_declaration().wants_lazy_evaluation(), False) self.assertEqual(c1.is_pcell_variant(), False) self.assertEqual(c1.is_pcell_variant(pcell_inst), True) @@ -394,6 +404,8 @@ class DBPCellTests(unittest.TestCase): pcell_var = ly.cell(pcell_var_id) pcell_inst = c1.insert(pya.CellInstArray(pcell_var_id, pya.Trans())) self.assertEqual(pcell_var.basic_name(), "Box2") + self.assertEqual(pcell_var.name, "Box2_L1d0_W1p000_H1p000") + self.assertEqual(pcell_var.qname(), "PCellTestLib2.Box2") self.assertEqual(pcell_var.pcell_parameters().__repr__(), "[<1/0>, 1.0, 1.0]") self.assertEqual(pcell_var.display_title(), "PCellTestLib2.Box2(L=1/0,W=1.000,H=1.000)") self.assertEqual(nh(pcell_var.pcell_parameters_by_name()), "{'height': 1.0, 'layer': <1/0>, 'width': 1.0}") diff --git a/testdata/ruby/dbPCells.rb b/testdata/ruby/dbPCells.rb index 61ca6450c..e15989b4b 100644 --- a/testdata/ruby/dbPCells.rb +++ b/testdata/ruby/dbPCells.rb @@ -39,6 +39,11 @@ class BoxPCell < RBA::PCellDeclaration # provide a descriptive text for the cell return "Box(L=#{parameters[0].to_s},W=#{'%.3f' % parameters[1].to_s},H=#{'%.3f' % parameters[2].to_s})" end + + def cell_name(parameters) + # provide a cell name for the PCell + return "Box_L#{parameters[0].to_s.gsub('/','d')}_W#{('%.3f' % parameters[1]).gsub('.','p')}_H#{('%.3f' % parameters[2]).gsub('.','p')}" + end def get_parameters @@ -129,6 +134,11 @@ if RBA.constants.member?(:PCellDeclarationHelper) return "Box2(L=" + layer.to_s + ",W=" + ('%.3f' % width) + ",H=" + ('%.3f' % height) + ")" end + def cell_name_impl + # provide a cell name for the PCell + return "Box2_L" + layer.to_s.gsub('/', 'd') + "_W" + ('%.3f' % width).gsub('.', 'p') + "_H" + ('%.3f' % height).gsub('.', 'p') + end + def produce_impl # fetch the parameters @@ -371,6 +381,9 @@ class DBPCell_TestClass < TestBase param = [ RBA::LayerInfo::new(1, 0) ] # rest is filled with defaults pcell_var_id = ly.add_pcell_variant(lib, pcell_decl_id, param) pcell_var = ly.cell(pcell_var_id) + assert_equal(pcell_var.name, "Box_L1d0_W1p000_H1p000") + assert_equal(pcell_var.qname, "PCellTestLib.Box") + assert_equal(pcell_var.basic_name, "Box") pcell_inst = c1.insert(RBA::CellInstArray::new(pcell_var_id, RBA::Trans::new)) assert_equal(pcell_var.layout.inspect, ly.inspect) assert_equal(pcell_var.library.inspect, lib.inspect) @@ -512,6 +525,8 @@ class DBPCell_TestClass < TestBase pcell_var = ly.cell(pcell_var_id) pcell_inst = c1.insert(RBA::CellInstArray::new(pcell_var_id, RBA::Trans::new)) assert_equal(pcell_var.basic_name, "Box2") + assert_equal(pcell_var.name, "Box2_L1d0_W1p000_H1p000") + assert_equal(pcell_var.qname, "PCellTestLib2.Box2") assert_equal(pcell_var.pcell_parameters().inspect, "[<1/0>, 1.0, 1.0, 0]") assert_equal(pcell_var.display_title(), "PCellTestLib2.Box2(L=1/0,W=1.000,H=1.000)") assert_equal(norm_hash(pcell_var.pcell_parameters_by_name()), "{\"height\"=>1.0, \"layer\"=><1/0>, \"secret\"=>0, \"width\"=>1.0}") @@ -908,6 +923,37 @@ class DBPCell_TestClass < TestBase end + # convert to static cell + def test_13 + + if !RBA.constants.member?(:PCellDeclarationHelper) + return + end + + # instantiate and register the library + tl = PCellTestLib2::new + + ly = RBA::Layout::new(true) + ly.dbu = 0.01 + + ci1 = ly.add_cell("c1") + c1 = ly.cell(ci1) + + lib = RBA::Library.library_by_name("PCellTestLib2") + assert_equal(lib != nil, true) + pcell_decl = lib.layout().pcell_declaration("Box2") + + param = [ RBA::LayerInfo::new(1, 0) ] # rest is filled with defaults + pcell_var_id = ly.add_pcell_variant(lib, pcell_decl.id(), param) + static_id = ly.convert_cell_to_static(pcell_var_id) + static = ly.cell(static_id) + assert_equal(static.basic_name, "Box2_L1d0_W1p000_H1p000") + assert_equal(static.name, "Box2_L1d0_W1p000_H1p000") + assert_equal(static.qname, "Box2_L1d0_W1p000_H1p000") + assert_equal(static.is_proxy?, false) + + end + end class DBPCellParameterStates_TestClass < TestBase From b9906180e8104bc2cf62fa65675d51294ebc1c36 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 25 Oct 2025 17:54:26 +0200 Subject: [PATCH 15/16] Implementing request from issue #2183 For strmxor, --drop-empty-cells now is default. To explicitly turn it OFF, use strmxor --drop-empty-cells=false ... --- src/buddies/src/bd/bdWriterOptions.cc | 18 +++++++- src/buddies/src/bd/bdWriterOptions.h | 8 ++++ src/buddies/src/bd/strmxor.cc | 6 ++- src/buddies/unit_tests/bdStrmxorTests.cc | 50 +++++++++++++++++++++-- testdata/bd/strmxor_au1d2.oas | Bin 0 -> 714 bytes 5 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 testdata/bd/strmxor_au1d2.oas diff --git a/src/buddies/src/bd/bdWriterOptions.cc b/src/buddies/src/bd/bdWriterOptions.cc index 87d519ff6..2886aef92 100644 --- a/src/buddies/src/bd/bdWriterOptions.cc +++ b/src/buddies/src/bd/bdWriterOptions.cc @@ -30,9 +30,23 @@ namespace bd { GenericWriterOptions::GenericWriterOptions () - : m_scale_factor (1.0) { - db::SaveLayoutOptions save_options; + db::SaveLayoutOptions options; + init_from_options (options); +} + +GenericWriterOptions::GenericWriterOptions (const db::SaveLayoutOptions &options) +{ + init_from_options (options); +} + +void +GenericWriterOptions::init_from_options (const db::SaveLayoutOptions &save_options_nc) +{ + // const_cast needed because "get_option_by_name" is not const as it should be + db::SaveLayoutOptions &save_options = const_cast (save_options_nc); + + m_scale_factor = 1.0; m_dbu = save_options.get_option_by_name ("dbu").to_double (); m_libname = save_options.get_option_by_name ("libname").to_string (); diff --git a/src/buddies/src/bd/bdWriterOptions.h b/src/buddies/src/bd/bdWriterOptions.h index a5811bdb5..6ebf61f99 100644 --- a/src/buddies/src/bd/bdWriterOptions.h +++ b/src/buddies/src/bd/bdWriterOptions.h @@ -53,6 +53,13 @@ public: */ GenericWriterOptions (); + /** + * @brief Constructor + * + * The "options" object specifies the defaults to be used. + */ + GenericWriterOptions (const db::SaveLayoutOptions &options); + /** * @brief Adds the generic options to the command line parser object * The format string gives a hint about the target format. Certain options will be @@ -142,6 +149,7 @@ private: int m_dxf_polygon_mode; void set_oasis_substitution_char (const std::string &text); + void init_from_options (const db::SaveLayoutOptions &options); }; } diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 1a5965e83..7df5be64b 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -380,7 +380,9 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) generic_reader_options_a.add_options (cmd); generic_reader_options_b.add_options (cmd); - bd::GenericWriterOptions writer_options; + db::SaveLayoutOptions def_writer_options; + def_writer_options.set_dont_write_empty_cells (true); + bd::GenericWriterOptions writer_options (def_writer_options); writer_options.add_options (cmd); cmd << tl::arg ("input_a", &infile_a, "The first input file (any format, may be gzip compressed)") @@ -447,7 +449,7 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) cmd.parse (argc, argv); if (top_a.empty () != top_b.empty ()) { - throw tl::Exception ("Both -ta|--top-a and -tb|--top-b top cells must be given"); + throw tl::Exception ("Both -ta|--top-a and -tb|--top-b top cells must be given, not just one of them"); } if (tolerances.empty ()) { diff --git a/src/buddies/unit_tests/bdStrmxorTests.cc b/src/buddies/unit_tests/bdStrmxorTests.cc index c065bbb43..bb06a9428 100644 --- a/src/buddies/unit_tests/bdStrmxorTests.cc +++ b/src/buddies/unit_tests/bdStrmxorTests.cc @@ -129,6 +129,48 @@ TEST(1A_Deep) std::string output = this->tmp_file ("tmp.oas"); + const char *argv[] = { "x", "--deep", "--drop-empty-cells=false", input_a.c_str (), input_b.c_str (), output.c_str () }; + + EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons)); + EXPECT_EQ (cap.captured_text (), + "Layer 10/0 is not present in first layout, but in second\n" + "Result summary (layers without differences are not shown):\n" + "\n" + " Layer Output Differences (hierarchical shape count)\n" + " ----------------------------------------------------------------\n" + " 3/0 3/0 3\n" + " 6/0 6/0 314\n" + " 8/1 8/1 1\n" + " 10/0 - (no such layer in first layout)\n" + "\n" + ); +} + +TEST(1A_DeepNoEmptyCells) +{ + tl::CaptureChannel cap; + + std::string input_a = tl::testdata (); + input_a += "/bd/strmxor_in1.gds"; + + std::string input_b = tl::testdata (); + input_b += "/bd/strmxor_in2.gds"; + + std::string au = tl::testdata (); + au += "/bd/strmxor_au1d2.oas"; + + std::string output = this->tmp_file ("tmp.oas"); + const char *argv[] = { "x", "--deep", input_a.c_str (), input_b.c_str (), output.c_str () }; EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1); @@ -342,7 +384,7 @@ TEST(2_Deep) std::string output = this->tmp_file ("tmp.oas"); - const char *argv[] = { "x", "-u", "--no-summary", "-l", input_a.c_str (), input_b.c_str (), output.c_str () }; + const char *argv[] = { "x", "-u", "--no-summary", "--drop-empty-cells=false", "-l", input_a.c_str (), input_b.c_str (), output.c_str () }; EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1); @@ -508,7 +550,7 @@ TEST(3_Deep) std::string output = this->tmp_file ("tmp.oas"); // NOTE: -p is ignored in deep mode - const char *argv[] = { "x", "-u", "--no-summary", "-p=1.0", "-n=4", input_a.c_str (), input_b.c_str (), output.c_str () }; + const char *argv[] = { "x", "-u", "--drop-empty-cells=false", "--no-summary", "-p=1.0", "-n=4", input_a.c_str (), input_b.c_str (), output.c_str () }; EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1); @@ -607,7 +649,7 @@ TEST(4_Deep) std::string output = this->tmp_file ("tmp.oas"); - const char *argv[] = { "x", "-u", "--no-summary", "-p=1.0", "-n=4", "-t=0.0,0.005,0.01,0.02,0.09,0.1", input_a.c_str (), input_b.c_str (), output.c_str () }; + const char *argv[] = { "x", "-u", "--drop-empty-cells=false", "--no-summary", "-p=1.0", "-n=4", "-t=0.0,0.005,0.01,0.02,0.09,0.1", input_a.c_str (), input_b.c_str (), output.c_str () }; EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1); @@ -673,7 +715,7 @@ TEST(5_Deep) std::string output = this->tmp_file ("tmp.oas"); - const char *argv[] = { "x", "-u", "--no-summary", "-b=1000", "-t=0.0,0.005,0.01,0.02,0.09,0.1", input_a.c_str (), input_b.c_str (), output.c_str () }; + const char *argv[] = { "x", "-u", "--drop-empty-cells=false", "--no-summary", "-b=1000", "-t=0.0,0.005,0.01,0.02,0.09,0.1", input_a.c_str (), input_b.c_str (), output.c_str () }; EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1); diff --git a/testdata/bd/strmxor_au1d2.oas b/testdata/bd/strmxor_au1d2.oas new file mode 100644 index 0000000000000000000000000000000000000000..e4802b448e2755b0f0c5de7b8c542b3f0075eaff GIT binary patch literal 714 zcmY!lcJ=kt^>+;R4CduxWH!_@V0gjKC?n3q!6L)YEF;ds&!EJR>XUoMnybM;fb~F; z;|1o9>4KX(8~>b$4qRRSKbqrK6n{_8gJqf4>PjZKWjDxlFOecr#8*H*VHd*(k7XJ+yA3p0`tXW*z}hJ+-D ZJ&TcvaT7Bm1EVryF_2_v7-4{c0RWxDG#dZ_ literal 0 HcmV?d00001 From b454d2ae42acc924ba1512f5cec124363f2d33ec Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 25 Oct 2025 23:12:22 +0200 Subject: [PATCH 16/16] Fixed issue #2201 (trace path) * you can zoom in now to select the end point. Problem was actually that zooming in was a problem when the start point went out of the viewport In addition: * Messages are sticky now ("Click on second point") * "Esc" will cancel path trace mode * The cursor switches back to normal after tracing --- src/edt/edt/edtService.cc | 7 ++++ src/laybasic/laybasic/layFinder.cc | 22 +++++++---- src/laybasic/laybasic/layFinder.h | 20 ++++++++++ src/laybasic/laybasic/layViewObject.cc | 23 +++++------ src/laybasic/laybasic/layViewObject.h | 9 +++++ .../lay_plugin/layNetTracerDialog.cc | 39 ++++++++++++++++++- .../lay_plugin/layNetTracerDialog.h | 2 + 7 files changed, 101 insertions(+), 21 deletions(-) diff --git a/src/edt/edt/edtService.cc b/src/edt/edt/edtService.cc index d0c5e4933..83c164472 100644 --- a/src/edt/edt/edtService.cc +++ b/src/edt/edt/edtService.cc @@ -1327,6 +1327,13 @@ static std::string path_to_string (const db::Layout &layout, const lay::ObjectIn void Service::display_status (bool transient) { + if (transient && view ()->canvas ()->begin_mouse_receivers () != view ()->canvas ()->end_mouse_receivers () + && (*view ()->canvas ()->begin_mouse_receivers ())->claims_message_bar ()) { + // do not display transient if there is a plugin active that has captured the mouse and claims the message bar - + // this way we can use messages in active plugins and still get visual feedback by the transient selection. + return; + } + EditableSelectionIterator r = transient ? begin_transient_selection () : begin_selection (); EditableSelectionIterator rr = r; diff --git a/src/laybasic/laybasic/layFinder.cc b/src/laybasic/laybasic/layFinder.cc index f3083e2d8..4056d8050 100644 --- a/src/laybasic/laybasic/layFinder.cc +++ b/src/laybasic/laybasic/layFinder.cc @@ -63,7 +63,7 @@ static int inst_point_sel_tests = 10000; Finder::Finder (bool point_mode, bool top_level_sel) : m_min_level (0), m_max_level (0), - mp_layout (0), mp_view (0), m_cv_index (0), m_point_mode (point_mode), m_catch_all (false), m_top_level_sel (top_level_sel) + mp_layout (0), mp_view (0), m_cv_index (0), m_point_mode (point_mode), m_catch_all (false), m_consider_viewport (true), m_top_level_sel (top_level_sel) { m_distance = std::numeric_limits::max (); } @@ -482,7 +482,10 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db: checkpoint (); // Viewport in current cell coordinate space (DBU) - db::Box viewport_box = (vp * db::CplxTrans (layout ().dbu ()) * t).inverted () * db::DBox (0, 0, view ()->viewport ().width (), view ()->viewport ().height ()); + db::Box viewport_box; + if (consider_viewport ()) { + viewport_box = (vp * db::CplxTrans (layout ().dbu ()) * t).inverted () * db::DBox (0, 0, view ()->viewport ().width (), view ()->viewport ().height ()); + } if (! m_context_layers.empty ()) { @@ -583,7 +586,7 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db: bool any_valid_edge = m_capture_all_shapes; for (db::Shape::polygon_edge_iterator e = shape->begin_edge (); ! e.at_end (); ++e) { - if ((*e).clipped (viewport_box).first) { + if (viewport_box.empty () || (*e).clipped (viewport_box).first) { any_valid_edge = true; test_edge (t, *e, d, match); } @@ -606,7 +609,7 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db: ++pt; for (; pt != shape->end_point (); ++pt) { db::Edge e (p, *pt); - if (e.clipped (viewport_box).first) { + if (viewport_box.empty () || e.clipped (viewport_box).first) { any_valid_edge = true; test_edge (t, e, d, match); } @@ -618,7 +621,7 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db: db::Polygon poly; shape->polygon (poly); for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { - if ((*e).clipped (viewport_box).first) { + if (viewport_box.empty () || (*e).clipped (viewport_box).first) { any_valid_edge = true; test_edge (t, *e, d, match); } @@ -651,7 +654,7 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db: // convert to polygon and test those edges db::Polygon poly (box); for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { - if ((*e).clipped (viewport_box).first) { + if (viewport_box.empty () || (*e).clipped (viewport_box).first) { any_valid_edge = true; test_edge (t, *e, d, match); } @@ -819,7 +822,10 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d checkpoint (); // Viewport in current cell coordinate space (DBU) - db::Box viewport_box = (vp * db::CplxTrans (layout ().dbu ()) * t).inverted () * db::DBox (0, 0, view ()->viewport ().width (), view ()->viewport ().height ()); + db::Box viewport_box; + if (consider_viewport ()) { + viewport_box = (vp * db::CplxTrans (layout ().dbu ()) * t).inverted () * db::DBox (0, 0, view ()->viewport ().width (), view ()->viewport ().height ()); + } if (! point_mode ()) { @@ -952,7 +958,7 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d bool any_valid_edge = false; for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) { // only consider edges that cut through the viewport - if ((*e).clipped (viewport_box).first) { + if (viewport_box.empty () || (*e).clipped (viewport_box).first) { any_valid_edge = true; test_edge (t, *e, d, match); } diff --git a/src/laybasic/laybasic/layFinder.h b/src/laybasic/laybasic/layFinder.h index f0ecdcc46..eca725dc7 100644 --- a/src/laybasic/laybasic/layFinder.h +++ b/src/laybasic/laybasic/layFinder.h @@ -96,6 +96,25 @@ public: m_catch_all = f; } + /** + * @brief Gets a flag indicating that the viewport will be considered + */ + bool consider_viewport () const + { + return m_consider_viewport; + } + + /** + * @brief Sets a flag indicating that the viewport will be considered + * If this flag is true (the default), only shapes and instances will be considered + * if edges (or polygons) or boundary edges (for instances) are visible in the + * viewport. If this flag is false, shapes or instances are considered always. + */ + void set_consider_viewport (bool f) + { + m_consider_viewport = f; + } + /** * @brief Destructor (just provided to please the compiler) */ @@ -217,6 +236,7 @@ private: double m_distance; bool m_point_mode; bool m_catch_all; + bool m_consider_viewport; bool m_top_level_sel; db::box_convert m_box_convert; db::box_convert m_cell_box_convert; diff --git a/src/laybasic/laybasic/layViewObject.cc b/src/laybasic/laybasic/layViewObject.cc index 3cfa61c04..6efa9f881 100644 --- a/src/laybasic/laybasic/layViewObject.cc +++ b/src/laybasic/laybasic/layViewObject.cc @@ -621,7 +621,10 @@ END_PROTECTED void ViewObjectUI::set_cursor (lay::Cursor::cursor_shape cursor) { - m_cursor = cursor; + if (m_cursor != cursor) { + m_cursor = cursor; + realize_cursor (); + } } void @@ -629,15 +632,7 @@ ViewObjectUI::set_default_cursor (lay::Cursor::cursor_shape cursor) { if (cursor != m_default_cursor) { m_default_cursor = cursor; -#if defined(HAVE_QT) - if (m_cursor == lay::Cursor::none && mp_widget) { - if (m_default_cursor == lay::Cursor::none) { - mp_widget->unsetCursor (); - } else { - mp_widget->setCursor (lay::Cursor::qcursor (m_default_cursor)); - } - } -#endif + realize_cursor (); } } @@ -652,11 +647,17 @@ ViewObjectUI::ensure_entered () void ViewObjectUI::begin_mouse_event (lay::Cursor::cursor_shape cursor) { - m_cursor = cursor; + set_cursor (cursor); } void ViewObjectUI::end_mouse_event () +{ + realize_cursor (); +} + +void +ViewObjectUI::realize_cursor () { #if defined(HAVE_QT) if (mp_widget) { diff --git a/src/laybasic/laybasic/layViewObject.h b/src/laybasic/laybasic/layViewObject.h index 7664b8f78..a175e5c5d 100644 --- a/src/laybasic/laybasic/layViewObject.h +++ b/src/laybasic/laybasic/layViewObject.h @@ -285,6 +285,14 @@ public: */ virtual void drag_cancel () { } + /** + * @brief Gets a value indicating whether the mouse receiver claims the view message bar + * + * If this method returns true, other services are not supposed to emit transient + * messages. + */ + virtual bool claims_message_bar () const { return false; } + /** * @brief Gets a value indicating whether a cursor position it set */ @@ -1121,6 +1129,7 @@ private: void objects_changed (); int widget_height () const; int widget_width () const; + void realize_cursor (); /** * @brief Register a service diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc index 598b1f475..4106392ce 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.cc @@ -202,13 +202,32 @@ NetTracerDialog::item_double_clicked (QListWidgetItem *item) } } +void +NetTracerDialog::drag_cancel () +{ + if (m_mouse_state > 0) { + + view ()->message (); + ui ()->ungrab_mouse (this); + set_cursor (lay::Cursor::none); + + m_mouse_state = 0; + + } +} + +bool +NetTracerDialog::claims_message_bar () const +{ + return true; +} + bool NetTracerDialog::mouse_move_event (const db::DPoint & /*p*/, unsigned int /*buttons*/, bool prio) { if (prio && m_mouse_state != 0) { set_cursor (lay::Cursor::cross); } - return false; } @@ -397,11 +416,13 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto { unsigned int start_layer = 0; db::Point start_point; + db::Shape start_shape; // locate the seed { lay::ShapeFinder finder (true /*point mode*/, false /*all levels*/, db::ShapeIterator::All); + finder.set_consider_viewport (false); // go through all visible layers of all cellviews and find a seed shape for (lay::LayerPropertiesConstIterator lprop = view ()->begin_layers (); ! lprop.at_end (); ++lprop) { @@ -417,7 +438,7 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto } m_cv_index = r->cv_index (); - + start_shape = r->shape (); start_layer = r->layer (); } @@ -440,6 +461,12 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto start_point = tt.inverted ().trans (start_search_box.center ()); + // stop if the center start point is not inside the start polygon + db::Polygon poly; + if (start_shape.polygon (poly) && db::inside_poly (poly.begin_edge (), start_point) < 0) { + return 0; + } + } // Set up the net tracer environment @@ -455,6 +482,7 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto if (trace_path) { lay::ShapeFinder finder (true /*point mode*/, false /*all levels*/, db::ShapeIterator::All); + finder.set_consider_viewport (false); // go through all visible layers of all cellviews and find a seed shape for (lay::LayerPropertiesConstIterator lprop = view ()->begin_layers (); ! lprop.at_end (); ++lprop) { @@ -483,6 +511,12 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto stop_point = tt.inverted ().trans (stop_search_box.center ()); stop_layer = r->layer (); + // stop if the center stop point is not inside the stop polygon + db::Polygon poly; + if (r->shape ().polygon (poly) && db::inside_poly (poly.begin_edge (), stop_point) < 0) { + return 0; + } + } db::NetTracer net_tracer; @@ -1261,6 +1295,7 @@ NetTracerDialog::release_mouse () m_mouse_state = 0; view ()->message (); ui ()->ungrab_mouse (this); + set_cursor (lay::Cursor::none); } void diff --git a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h index 36f2f6cc6..4fe41b85b 100644 --- a/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h +++ b/src/plugins/tools/net_tracer/lay_plugin/layNetTracerDialog.h @@ -60,6 +60,8 @@ public: NetTracerDialog (lay::Dispatcher *root, lay::LayoutViewBase *view); virtual ~NetTracerDialog (); + virtual void drag_cancel (); + virtual bool claims_message_bar () const; virtual bool mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio); virtual bool mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio); virtual void menu_activated (const std::string &symbol);