From a092d7c6ca3ef8867c1c94f031cfa6ed1d97cb53 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 4 Apr 2026 14:27:01 +0200 Subject: [PATCH 1/5] Enhancement to Library#lib_by_name * Better description * Allowing "*" for the technology name to capture all libraries with that name --- src/db/db/dbLibrary.cc | 2 +- src/db/db/gsiDeclDbLibrary.cc | 13 +++++++++---- src/db/unit_tests/dbLibrariesTests.cc | 27 +++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/db/db/dbLibrary.cc b/src/db/db/dbLibrary.cc index 0af31331c..e0f93dd7b 100644 --- a/src/db/db/dbLibrary.cc +++ b/src/db/db/dbLibrary.cc @@ -55,7 +55,7 @@ Library::~Library () bool Library::is_for_technology (const std::string &name) const { - return m_technologies.find (name) != m_technologies.end (); + return (! m_technologies.empty () && name == "*") || m_technologies.find (name) != m_technologies.end (); } bool diff --git a/src/db/db/gsiDeclDbLibrary.cc b/src/db/db/gsiDeclDbLibrary.cc index 4da1335f2..4f2425eb9 100644 --- a/src/db/db/gsiDeclDbLibrary.cc +++ b/src/db/db/gsiDeclDbLibrary.cc @@ -165,10 +165,13 @@ LibraryClass decl_Library ("db", "LibraryBase", "@brief Gets a library by name\n" "Returns the library object for the given name. If the name is not a valid library name, nil is returned.\n" "\n" - "Different libraries can be registered under the same names for different technologies. When a technology name is given in 'for_technologies', " - "the first library matching this technology is returned. If no technology is given, the first library is returned.\n" + "Different libraries can be registered under the same names for different technologies. By specifying a technology, this method\n" + "will return the first library matching both name and the given technology. It will also return libraries not bound to a specific\n" + "technology in that case. Without a technology name given ('unspecific'), only libraries not bound to a technology are returned.\n" + "You can also specify '*' for the technology - in that case, the first library with the given name is returned, regardless whether\n" + "it is bound to a technology or not.\n" "\n" - "The technology selector has been introduced in version 0.27." + "The technology selector has been introduced in version 0.27. The '*' option for the technology has been added in version 0.30.8." ) + gsi::method ("library_by_id", &library_by_id, gsi::arg ("id"), "@brief Gets the library object for the given ID\n" @@ -274,8 +277,10 @@ LibraryClass decl_Library ("db", "LibraryBase", gsi::method ("is_for_technology", &db::Library::is_for_technology, gsi::arg ("tech"), "@brief Returns a value indicating whether the library is associated with the given technology.\n" "The method is equivalent to checking whether the \\technologies list is empty.\n" + "As a special case, you can pass '*' for the 'tech' argument. In that case, this method\n" + "will return true, if the library is bound to any technology.\n" "\n" - "This method has been introduced in version 0.27" + "This method has been introduced in version 0.27. The '*' option for the technology has been added in version 0.30.8." ) + gsi::method ("for_technologies", &db::Library::for_technologies, "@brief Returns a value indicating whether the library is associated with any technology.\n" diff --git a/src/db/unit_tests/dbLibrariesTests.cc b/src/db/unit_tests/dbLibrariesTests.cc index 1d48e852e..ed10661e4 100644 --- a/src/db/unit_tests/dbLibrariesTests.cc +++ b/src/db/unit_tests/dbLibrariesTests.cc @@ -506,6 +506,16 @@ TEST(3) std::unique_ptr lib_b (new LIBT_B ()); db::LibraryManager::instance ().register_lib (lib_b.get ()); + EXPECT_EQ (lib_a->is_for_technology ("X"), false); + EXPECT_EQ (lib_a->is_for_technology ("*"), false); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A").first, true); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "*").first, true); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "X").first, true); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "").first, true); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A").second, lib_a->get_id ()); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "X").second, lib_a->get_id ()); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "*").second, lib_a->get_id ()); + // This test tests the ability to reference libraries out of other libraries ("B" references "A"), // the ability to persist that and whether this survives a write/read cycle. @@ -545,6 +555,15 @@ TEST(4) std::unique_ptr lib_a1_inst (new LIBT_A ()); tl::weak_ptr lib_a1 (lib_a1_inst.get ()); lib_a1->add_technology ("X"); + EXPECT_EQ (lib_a1->is_for_technology ("Z"), false); + EXPECT_EQ (lib_a1->is_for_technology ("X"), true); + EXPECT_EQ (lib_a1->is_for_technology ("XX"), false); + EXPECT_EQ (lib_a1->is_for_technology ("*"), true); + lib_a1->add_technology ("XX"); + EXPECT_EQ (lib_a1->is_for_technology ("Z"), false); + EXPECT_EQ (lib_a1->is_for_technology ("X"), true); + EXPECT_EQ (lib_a1->is_for_technology ("XX"), true); + EXPECT_EQ (lib_a1->is_for_technology ("*"), true); std::unique_ptr lib_a2_inst (new LIBT_A ()); tl::weak_ptr lib_a2 (lib_a2_inst.get ()); @@ -552,12 +571,15 @@ TEST(4) std::unique_ptr lib_a3_inst (new LIBT_A ()); tl::weak_ptr lib_a3 (lib_a3_inst.get ()); + // same technologies as a1, so it can entirely replace it: lib_a3->add_technology ("X"); + lib_a3->add_technology ("XX"); std::unique_ptr lib_a4_inst (new LIBT_A ()); tl::weak_ptr lib_a4 (lib_a4_inst.get ()); EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A").first, false); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "*").first, false); EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "Z").first, false); EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "").first, false); EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "X").first, false); @@ -565,20 +587,25 @@ TEST(4) db::LibraryManager::instance ().register_lib (lib_a1.get ()); EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A").first, false); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "*").first, true); EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "Z").first, false); EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "").first, false); EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "X").first, true); EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "X").second, lib_a1->get_id ()); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "XX").second, lib_a1->get_id ()); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "*").second, lib_a1->get_id ()); db::LibraryManager::instance ().register_lib (lib_a2.get ()); EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A").first, false); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "*").first, true); EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "Z").first, false); EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "").first, false); EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "X").first, true); EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "X").second, lib_a1->get_id ()); EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "Y").first, true); EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "Y").second, lib_a2->get_id ()); + EXPECT_EQ (db::LibraryManager::instance ().lib_by_name ("A", "*").second, lib_a2->get_id ()); db::LibraryManager::instance ().register_lib (lib_a3.get ()); // lib_a3 replaces lib_a1 From 57eb90df69bc2fcc5f4591eb90b3b3a3847f2f65 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 4 Apr 2026 14:37:15 +0200 Subject: [PATCH 2/5] Fixed a small glitch (Warning about open transaction at end of partial move) --- src/edt/edt/edtPartialService.cc | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/edt/edt/edtPartialService.cc b/src/edt/edt/edtPartialService.cc index 917122572..73a3c9cdb 100644 --- a/src/edt/edt/edtPartialService.cc +++ b/src/edt/edt/edtPartialService.cc @@ -2434,18 +2434,12 @@ PartialService::end_move (const db::DPoint & /*p*/, lay::angle_constraint_type / if (m_current != m_start) { - if (manager ()) { - manager ()->transaction (tl::to_string (tr ("Partial move"))); - } + db::Transaction transaction ((manager () && ! manager ()->transacting ()) ? manager () : 0, tl::to_string (tr ("Partial move"))); db::DTrans move_trans = db::DTrans (m_current - m_start); transform_selection (move_trans); - if (manager ()) { - manager ()->commit (); - } - } if (! m_keep_selection) { From 65fec369024d8bb14bd40095c3afb03c509cccb7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 4 Apr 2026 14:40:30 +0200 Subject: [PATCH 3/5] Fixed a bug in PCell parameter computation "coerce_parameters" and "callback" was called with the client layout instead of definition layout, hence the DBU was incorrect if both layouts have different DBU. This became visible on the computed character dimensions of the Basic.TEXT PCell when the client layout had a DBU != 1nm. --- src/edt/edt/edtPCellParametersPage.cc | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/edt/edt/edtPCellParametersPage.cc b/src/edt/edt/edtPCellParametersPage.cc index 38969f3f6..500d76b58 100644 --- a/src/edt/edt/edtPCellParametersPage.cc +++ b/src/edt/edt/edtPCellParametersPage.cc @@ -699,7 +699,9 @@ PCellParametersPage::setup (lay::LayoutViewBase *view, int cv_index, const db::P // initial callback try { - mp_pcell_decl->callback (mp_view->cellview (m_cv_index)->layout (), std::string (), m_states); + if (mp_pcell_decl->layout ()) { + mp_pcell_decl->callback (*mp_pcell_decl->layout (), std::string (), m_states); + } } catch (tl::Exception &ex) { // potentially caused by script errors in callback implementation tl::error << ex.msg (); @@ -788,7 +790,9 @@ PCellParametersPage::parameter_changed () // Note: checking for is_busy prevents callbacks during debugger execution if (! edit_error) { - mp_pcell_decl->callback (mp_view->cellview (m_cv_index)->layout (), pd ? pd->get_name () : std::string (), states); + if (mp_pcell_decl->layout ()) { + mp_pcell_decl->callback (*mp_pcell_decl->layout (), pd ? pd->get_name () : std::string (), states); + } m_states = states; } @@ -1014,7 +1018,9 @@ PCellParametersPage::get_parameters (db::ParameterStates &states, bool *ok) auto parameters = parameter_from_states (states); auto before_coerce = parameters; - mp_pcell_decl->coerce_parameters (mp_view->cellview (m_cv_index)->layout (), parameters); + if (mp_pcell_decl->layout ()) { + mp_pcell_decl->coerce_parameters (*mp_pcell_decl->layout (), parameters); + } if (parameters != before_coerce) { states_from_parameters (states, parameters); @@ -1070,8 +1076,8 @@ PCellParametersPage::set_parameters (const std::vector ¶meters) states_from_parameters (m_states, parameters); try { - if (mp_view->cellview (m_cv_index).is_valid ()) { - mp_pcell_decl->callback (mp_view->cellview (m_cv_index)->layout (), std::string (), m_states); + if (mp_view->cellview (m_cv_index).is_valid () && mp_pcell_decl->layout ()) { + mp_pcell_decl->callback (*mp_pcell_decl->layout (), std::string (), m_states); } } catch (tl::Exception &ex) { // potentially caused by script errors in callback implementation From 60a210c264095b014a898e34f81a7e5f7faf33ec Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 4 Apr 2026 15:28:05 +0200 Subject: [PATCH 4/5] Config option: disable generation of new layers The name of the config option is "auto-create-new-layers". It is a per-view option and can be edited on the "Application/Layer Properties" page. With this option set to true (default), new layers are automatically added to the layer list on 1. Paste of cells and shapes 2. Placing of instances Manually adding new layers to the layer list is always possible. --- src/edt/edt/edtInstPropertiesPage.cc | 4 +- src/edt/edt/edtInstService.cc | 4 +- src/edt/edt/edtMainService.cc | 4 +- src/laybasic/laybasic/layLayoutViewBase.cc | 16 ++- src/laybasic/laybasic/layLayoutViewBase.h | 17 +++ src/laybasic/laybasic/layLayoutViewConfig.cc | 1 + src/laybasic/laybasic/laybasicConfig.h | 1 + src/layui/layui/LayoutViewConfigPage5.ui | 129 +++++++++++-------- src/layui/layui/layHierarchyControlPanel.cc | 4 +- src/layui/layui/layLayoutViewConfigPages.cc | 5 + 10 files changed, 129 insertions(+), 56 deletions(-) diff --git a/src/edt/edt/edtInstPropertiesPage.cc b/src/edt/edt/edtInstPropertiesPage.cc index 8c28e4f1f..e1eeb1d71 100644 --- a/src/edt/edt/edtInstPropertiesPage.cc +++ b/src/edt/edt/edtInstPropertiesPage.cc @@ -934,7 +934,9 @@ InstPropertiesPage::do_apply (bool current_only, bool relative) throw; } - mp_service->view ()->add_new_layers (layer_state); + if (mp_service->view ()->auto_create_new_layers ()) { + mp_service->view ()->add_new_layers (layer_state); + } // remove superfluous proxies for (unsigned int i = 0; i < mp_service->view ()->cellviews (); ++i) { diff --git a/src/edt/edt/edtInstService.cc b/src/edt/edt/edtInstService.cc index 6c8794276..e78721a6a 100644 --- a/src/edt/edt/edtInstService.cc +++ b/src/edt/edt/edtInstService.cc @@ -332,7 +332,9 @@ InstService::make_cell (const lay::CellView &cv) } - view ()->add_new_layers (layer_state); + if (view ()->auto_create_new_layers ()) { + view ()->add_new_layers (layer_state); + } m_has_valid_cell = true; m_current_cell = inst_cell_index; diff --git a/src/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index 2187b574d..92f5f777c 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -2685,7 +2685,9 @@ MainService::paste () } // Add new layers to the view if required. - view ()->add_new_layers (new_layers, cv_index); + if (mp_view->auto_create_new_layers ()) { + view ()->add_new_layers (new_layers, cv_index); + } view ()->update_content (); } diff --git a/src/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc index b0c537a40..b1c80ce58 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.cc +++ b/src/laybasic/laybasic/layLayoutViewBase.cc @@ -362,6 +362,7 @@ LayoutViewBase::init (db::Manager *mgr) m_clear_ruler_new_cell = false; m_dbu_coordinates = false; m_absolute_coordinates = false; + m_auto_create_new_layers = true; m_drop_small_cells = false; m_drop_small_cells_value = 10; m_drop_small_cells_cond = DSC_Max; @@ -1099,6 +1100,13 @@ LayoutViewBase::configure (const std::string &name, const std::string &value) absolute_coordinates (flag); return true; + } else if (name == cfg_auto_create_new_layers) { + + bool flag; + tl::from_string (value, flag); + auto_create_new_layers (flag); + return true; + } else if (name == cfg_guiding_shape_visible) { bool v = false; @@ -4965,7 +4973,13 @@ LayoutViewBase::absolute_coordinates (bool f) m_absolute_coordinates = f; } -void +void +LayoutViewBase::auto_create_new_layers (bool f) +{ + m_auto_create_new_layers = f; +} + +void LayoutViewBase::select_cellviews_fit (const std::list &cvs) { if (m_cellviews != cvs) { diff --git a/src/laybasic/laybasic/layLayoutViewBase.h b/src/laybasic/laybasic/layLayoutViewBase.h index eaa83b818..c8cf43a4c 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.h +++ b/src/laybasic/laybasic/layLayoutViewBase.h @@ -1884,6 +1884,22 @@ public: */ void absolute_coordinates (bool f); + /** + * @brief Gets a value indicating whether new layer entries shall be created in the view + * + * Certain operations such as paste or creation of instances establish new layers. + * This flag controls whether such new layers are automatically added to the layer list. + */ + bool auto_create_new_layers () const + { + return m_auto_create_new_layers; + } + + /** + * @brief Sets a value indicating whether new layer entries shall be created in the view + */ + void auto_create_new_layers (bool f); + /** * @brief Gets the canvas object (where the layout is drawn and view objects are placed) */ @@ -3092,6 +3108,7 @@ private: bool m_clear_ruler_new_cell; bool m_dbu_coordinates; bool m_absolute_coordinates; + bool m_auto_create_new_layers; bool m_dirty; bool m_prop_changed; diff --git a/src/laybasic/laybasic/layLayoutViewConfig.cc b/src/laybasic/laybasic/layLayoutViewConfig.cc index dd65bad15..590429231 100644 --- a/src/laybasic/laybasic/layLayoutViewConfig.cc +++ b/src/laybasic/laybasic/layLayoutViewConfig.cc @@ -114,6 +114,7 @@ public: options.push_back (std::pair (cfg_guiding_shape_color, cc.to_string (tl::Color ()))); options.push_back (std::pair (cfg_guiding_shape_vertex_size, "5")); options.push_back (std::pair (cfg_abs_units, "false")); + options.push_back (std::pair (cfg_auto_create_new_layers, "true")); options.push_back (std::pair (cfg_dbu_units, "false")); options.push_back (std::pair (cfg_drawing_workers, "1")); options.push_back (std::pair (cfg_drop_small_cells, "false")); diff --git a/src/laybasic/laybasic/laybasicConfig.h b/src/laybasic/laybasic/laybasicConfig.h index f3b6b2c03..8506f6776 100644 --- a/src/laybasic/laybasic/laybasicConfig.h +++ b/src/laybasic/laybasic/laybasicConfig.h @@ -119,6 +119,7 @@ static const std::string cfg_stipple_palette ("stipple-palette"); static const std::string cfg_line_style_palette ("line-style-palette"); static const std::string cfg_dbu_units ("dbu-units"); static const std::string cfg_abs_units ("absolute-units"); +static const std::string cfg_auto_create_new_layers ("auto-create-new-layers"); static const std::string cfg_drawing_workers ("drawing-workers"); static const std::string cfg_drop_small_cells ("drop-small-cells"); static const std::string cfg_drop_small_cells_cond ("drop-small-cells-condition"); diff --git a/src/layui/layui/LayoutViewConfigPage5.ui b/src/layui/layui/LayoutViewConfigPage5.ui index f8a02467c..0fbfba3f4 100644 --- a/src/layui/layui/LayoutViewConfigPage5.ui +++ b/src/layui/layui/LayoutViewConfigPage5.ui @@ -1,85 +1,86 @@ - + + LayoutViewConfigPage5 - - + + 0 0 694 - 301 + 453 - + Application Settings - - - 9 - - + + 6 + + 9 + - - + + Use default layer properties file - + true - - + + 9 - + 6 - - - + + + ... - - - + + + Automatically add other layers - - + + - - - - <html><body><b>Hint</b>: a technology or reader specific layer properties file (i.e. for PCB import) will override this setting.</p></body></html> + + + + <html><body><b>Hint</b>: a technology or reader specific layer properties file (i.e. for PCB import) will override this setting.</p></body></html> - + true - - - + + + The following layer properties file is loaded into the layer view list every time a layout is opened or created: - + true - + - + Qt::Vertical - + QSizePolicy::Fixed - + 20 5 @@ -91,34 +92,34 @@ - - + + Layer properties display - - - 9 - - + + 6 + + 9 + - - + + Always show layer source in layer list - - + + Always show layer and datatype together with database name in layer source - - + + Always show layout index in layer source @@ -126,9 +127,35 @@ + + + + On new layers + + + + + + New layers are created implicitly when new content is pasted or instances are placed. + + + true + + + + + + + New layers are automatically added to the layer list + + + + + + - + lyp_file_gbx lyp_file_le diff --git a/src/layui/layui/layHierarchyControlPanel.cc b/src/layui/layui/layHierarchyControlPanel.cc index 131323839..2b948058c 100644 --- a/src/layui/layui/layHierarchyControlPanel.cc +++ b/src/layui/layui/layHierarchyControlPanel.cc @@ -1214,7 +1214,9 @@ HierarchyControlPanel::paste () // Add new layers to the view if required. if (! new_layers.empty ()) { - mp_view->add_new_layers (new_layers, m_active_index); + if (mp_view->auto_create_new_layers ()) { + mp_view->add_new_layers (new_layers, m_active_index); + } mp_view->update_content (); } diff --git a/src/layui/layui/layLayoutViewConfigPages.cc b/src/layui/layui/layLayoutViewConfigPages.cc index ac6ecfe7f..3b70dc170 100644 --- a/src/layui/layui/layLayoutViewConfigPages.cc +++ b/src/layui/layui/layLayoutViewConfigPages.cc @@ -1013,6 +1013,10 @@ LayoutViewConfigPage5::setup (lay::Dispatcher *root) bool always_show_li = false; root->config_get (cfg_layers_always_show_layout_index, always_show_li); mp_ui->ly_index_cb->setChecked (always_show_li); + + bool auto_create_new_layers = true; + root->config_get (cfg_auto_create_new_layers, auto_create_new_layers); + mp_ui->auto_create_new_layers_cb->setChecked (auto_create_new_layers); } void @@ -1027,6 +1031,7 @@ LayoutViewConfigPage5::commit (lay::Dispatcher *root) root->config_set (cfg_layers_always_show_source, mp_ui->source_display_cb->isChecked ()); root->config_set (cfg_layers_always_show_ld, mp_ui->ld_display_cb->isChecked ()); root->config_set (cfg_layers_always_show_layout_index, mp_ui->ly_index_cb->isChecked ()); + root->config_set (cfg_auto_create_new_layers, mp_ui->auto_create_new_layers_cb->isChecked ()); } void From d09734fe0d22b71ae93b26d18222aa08234257d5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 5 Apr 2026 22:18:39 +0200 Subject: [PATCH 5/5] Small feature: hovering over an image will not just display the image parameters in the status bar, but also the value of the pixel the mouse is over --- src/img/img/imgObject.cc | 1 - src/img/img/imgObject.h | 4 ++-- src/img/img/imgService.cc | 39 +++++++++++++++++++++++++++++++++++++-- src/img/img/imgService.h | 2 +- 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/img/img/imgObject.cc b/src/img/img/imgObject.cc index 96acfe403..6642ccdcb 100644 --- a/src/img/img/imgObject.cc +++ b/src/img/img/imgObject.cc @@ -1295,7 +1295,6 @@ Object::box () const // include landmarks for (std::vector ::const_iterator l = m_landmarks.begin (); l != m_landmarks.end (); ++l) { - b += m_trans * *l; } diff --git a/src/img/img/imgObject.h b/src/img/img/imgObject.h index f45d94031..a48e72551 100644 --- a/src/img/img/imgObject.h +++ b/src/img/img/imgObject.h @@ -794,7 +794,7 @@ public: /** * @brief Set the transformation matrix * - * This transformation matrix converts pixel coordinates (0,0 being the lower left corner and each pixel having the dimension of pixel_width and pixel_height) + * This transformation matrix converts pixel coordinates (0,0 being the center and each pixel having the dimension of pixel_width and pixel_height) * to micron coordinates. The coordinate of the pixel is the lower left corner of the pixel. */ void set_matrix (const db::Matrix3d &trans); @@ -802,7 +802,7 @@ public: /** * @brief Return the pixel-to-micron transformation * - * This transformation converts pixel coordinates (0,0 being the lower left corner and each pixel having the dimension of pixel_width and pixel_height) + * This transformation converts pixel coordinates (0,0 being the center and each pixel having the dimension of pixel_width and pixel_height) * to micron coordinates. The coordinate of the pixel is the lower left corner of the pixel. */ const db::Matrix3d &matrix () const diff --git a/src/img/img/imgService.cc b/src/img/img/imgService.cc index 03bdf85ed..dd8ca8b04 100644 --- a/src/img/img/imgService.cc +++ b/src/img/img/imgService.cc @@ -1309,6 +1309,7 @@ Service::transient_select (const db::DPoint &pos) clear_transient_selection (); bool any_selected = false; + std::string data_string; // compute search box double l = catch_distance (); @@ -1336,12 +1337,41 @@ Service::transient_select (const db::DPoint &pos) mp_transient_view = new img::View (this, imin, img::View::mode_transient); } + if (mp_transient_view->image_object ()) { + + const img::Object *image = mp_transient_view->image_object (); + + db::DPoint pixel = image->matrix ().inverted ().trans (pos); + if (pixel.x () > image->width () * -0.5 - 0.5 + db::epsilon && pixel.x () < image->width () * 0.5 + 0.5 - db::epsilon && + pixel.y () > image->height () * -0.5 - 0.5 + db::epsilon && pixel.y () < image->height () * 0.5 + 0.5 - db::epsilon) { + + db::Point pixel_index = db::Point (pixel + db::DVector (image->width () * 0.5 - 0.5, image->height () * 0.5 - 0.5)); + + // check once again to account to rounding issues + if (pixel_index.x () >= 0 && pixel_index.x () < image->width () && + pixel_index.y () >= 0 && pixel_index.y () < image->height ()) { + + if (image->is_color ()) { + data_string = tl::sprintf ("RGB: %.5g,%.5g,%.5g", + image->pixel (pixel_index.x (), pixel_index.y (), 0), + image->pixel (pixel_index.x (), pixel_index.y (), 1), + image->pixel (pixel_index.x (), pixel_index.y (), 2)); + } else { + data_string = tl::sprintf ("%.5g", image->pixel (pixel_index.x (), pixel_index.y ())); + } + + } + + } + + } + any_selected = true; } if (any_selected && ! editables ()->has_selection ()) { - display_status (true); + display_status (true, data_string); } return any_selected; @@ -1464,11 +1494,13 @@ Service::select (const db::DBox &box, lay::Editable::SelectionMode mode) } void -Service::display_status (bool transient) +Service::display_status (bool transient, const std::string &data_string) { View *selected_view = transient ? mp_transient_view : (m_selected_image_views.size () == 1 ? m_selected_image_views [0] : 0); if (! selected_view) { + view ()->message (std::string ()); + } else { const img::Object *image = selected_view->image_object (); @@ -1478,6 +1510,9 @@ Service::display_status (bool transient) msg = tl::to_string (tr ("selected: ")); } msg += tl::sprintf (tl::to_string (tr ("image(%dx%d)")), image->width (), image->height ()); + if (! data_string.empty ()) { + msg += " [" + data_string + "]"; + } view ()->message (msg); } diff --git a/src/img/img/imgService.h b/src/img/img/imgService.h index ff7f9a2d8..a709ab2ca 100644 --- a/src/img/img/imgService.h +++ b/src/img/img/imgService.h @@ -586,7 +586,7 @@ private: /** * @brief Display a message about the current selection */ - void display_status (bool transient); + void display_status (bool transient, const std::string &data_string = std::string ()); /** * @brief Gets a value indicating the (new) top z position