From 08b0eb5e7b6a6ee39ac15e543302c53bc4987f93 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Mon, 6 Dec 2021 14:22:27 +0100 Subject: [PATCH 01/13] filter parametrization plugin draft --- src/CMakeLists.txt | 1 + .../filter_parametrization/CMakeLists.txt | 7 + .../filter_parametrization.cpp | 121 ++++++++++++++++++ .../filter_parametrization.h | 62 +++++++++ 4 files changed, 191 insertions(+) create mode 100644 src/meshlabplugins/filter_parametrization/CMakeLists.txt create mode 100644 src/meshlabplugins/filter_parametrization/filter_parametrization.cpp create mode 100644 src/meshlabplugins/filter_parametrization/filter_parametrization.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dc0d9d1e5..1825ee4d4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -172,6 +172,7 @@ if(NOT DEFINED MESHLAB_PLUGINS) # it may be already defined in parent directory meshlabplugins/filter_mls meshlabplugins/filter_mutualglobal meshlabplugins/filter_mutualinfo + meshlabplugins/filter_parametrization meshlabplugins/filter_plymc meshlabplugins/filter_qhull meshlabplugins/filter_quality diff --git a/src/meshlabplugins/filter_parametrization/CMakeLists.txt b/src/meshlabplugins/filter_parametrization/CMakeLists.txt new file mode 100644 index 000000000..fbf159df9 --- /dev/null +++ b/src/meshlabplugins/filter_parametrization/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright 2019, 2020, Visual Computing Lab, ISTI - Italian National Research Council + +set(SOURCES filter_parametrization.cpp) + +set(HEADERS filter_parametrization.h) + +add_meshlab_plugin(filter_parametrization ${SOURCES} ${HEADERS}) diff --git a/src/meshlabplugins/filter_parametrization/filter_parametrization.cpp b/src/meshlabplugins/filter_parametrization/filter_parametrization.cpp new file mode 100644 index 000000000..c9de97161 --- /dev/null +++ b/src/meshlabplugins/filter_parametrization/filter_parametrization.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +* MeshLab o o * +* A versatile mesh processing toolbox o o * +* _ O _ * +* Copyright(C) 2005-2021 \/)\/ * +* Visual Computing Lab /\/| * +* ISTI - Italian National Research Council | * +* \ * +* All rights reserved. * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +****************************************************************************/ + +#include "filter_parametrization.h" + +FilterParametrizationPlugin::FilterParametrizationPlugin() +{ + typeList = { FP_HARMONIC_PARAM}; + + for(const ActionIDType& tt : typeList) + actionList.push_back(new QAction(filterName(tt), this)); +} + +QString FilterParametrizationPlugin::pluginName() const +{ + return "FilterParametrization"; +} + +QString FilterParametrizationPlugin::vendor() const +{ + return "CNR-ISTI-VCLab"; +} + +QString FilterParametrizationPlugin::filterName(ActionIDType filterId) const +{ + switch(filterId) { + case FP_HARMONIC_PARAM : + return "Armonic Parametrization"; + default : + assert(0); + return ""; + } +} + +QString FilterParametrizationPlugin::filterInfo(ActionIDType filterId) const +{ + switch(filterId) { + case FP_HARMONIC_PARAM : + return ""; + default : + assert(0); + return "Unknown Filter"; + } +} + +FilterParametrizationPlugin::FilterClass FilterParametrizationPlugin::getClass(const QAction *a) const +{ + switch(ID(a)) { + case FP_HARMONIC_PARAM : + return FilterPlugin::Texture; + default : + assert(0); + return FilterPlugin::Generic; + } +} + +FilterPlugin::FilterArity FilterParametrizationPlugin::filterArity(const QAction*) const +{ + return SINGLE_MESH; +} + +int FilterParametrizationPlugin::getPreConditions(const QAction*) const +{ + return MeshModel::MM_NONE; +} + +int FilterParametrizationPlugin::postCondition(const QAction*) const +{ + return MeshModel::MM_VERTTEXCOORD; +} + +RichParameterList FilterParametrizationPlugin::initParameterList(const QAction *action, const MeshModel &m) +{ + RichParameterList parlst; + switch(ID(action)) { + case FP_HARMONIC_PARAM : + break; + default : + assert(0); + } + return parlst; +} + +std::map FilterParametrizationPlugin::applyFilter( + const QAction * action, + const RichParameterList & par, + MeshDocument &md, + unsigned int& /*postConditionMask*/, + vcg::CallBackPos *cb) +{ + switch(ID(action)) { + case FP_HARMONIC_PARAM : + + break; + default : + wrongActionCalled(action); + } + return std::map(); +} + +MESHLAB_PLUGIN_NAME_EXPORTER(FilterSamplePlugin) diff --git a/src/meshlabplugins/filter_parametrization/filter_parametrization.h b/src/meshlabplugins/filter_parametrization/filter_parametrization.h new file mode 100644 index 000000000..347d0f927 --- /dev/null +++ b/src/meshlabplugins/filter_parametrization/filter_parametrization.h @@ -0,0 +1,62 @@ +/**************************************************************************** +* MeshLab o o * +* A versatile mesh processing toolbox o o * +* _ O _ * +* Copyright(C) 2005-2021 \/)\/ * +* Visual Computing Lab /\/| * +* ISTI - Italian National Research Council | * +* \ * +* All rights reserved. * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +****************************************************************************/ + +#ifndef MESHLAB_FILTER_PARAMETRIZATION_PLUGIN_H +#define MESHLAB_FILTER_PARAMETRIZATION_PLUGIN_H + +#include + +class FilterParametrizationPlugin : public QObject, public FilterPlugin +{ + //keep these three lines unchanged + Q_OBJECT + MESHLAB_PLUGIN_IID_EXPORTER(FILTER_PLUGIN_IID) + Q_INTERFACES(FilterPlugin) + +public: + //enum used to give an ID to every filter implemented in the plugin + enum FileterIds {FP_HARMONIC_PARAM}; + + FilterParametrizationPlugin(); + + QString pluginName() const; + QString vendor() const; + + QString filterName(ActionIDType filter) const; + QString filterInfo(ActionIDType filter) const; + FilterClass getClass(const QAction* a) const; + FilterArity filterArity(const QAction*) const; + int getPreConditions(const QAction *) const; + int postCondition(const QAction* ) const; + RichParameterList initParameterList(const QAction*, const MeshModel &/*m*/); + std::map applyFilter( + const QAction* action, + const RichParameterList & params, + MeshDocument &md, + unsigned int& postConditionMask, + vcg::CallBackPos * cb); + +private: +}; + +#endif //MESHLAB_FILTER_PARAMETRIZATION_PLUGIN_H From 1263a6362b08d3c2caae114e6e13b4ce8e3b89ad Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Mon, 6 Dec 2021 15:54:38 +0100 Subject: [PATCH 02/13] harmonic parametrization function working --- src/CMakeLists.txt | 1 + .../filter_parametrization/CMakeLists.txt | 14 +++++-- .../filter_parametrization.cpp | 42 +++++++++++++++++-- .../filter_parametrization.h | 1 + 4 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1825ee4d4..1e9a1153d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,6 +32,7 @@ if (Qt5_VERSION VERSION_LESS 5.15.0) message(FATAL_ERROR "Minimum supported Qt5 version is 5.15!") endif() find_package(OpenMP) +find_package(Threads) message(STATUS "Searching for required components with bundled fallback") find_package(GLEW) diff --git a/src/meshlabplugins/filter_parametrization/CMakeLists.txt b/src/meshlabplugins/filter_parametrization/CMakeLists.txt index fbf159df9..0e61e4b58 100644 --- a/src/meshlabplugins/filter_parametrization/CMakeLists.txt +++ b/src/meshlabplugins/filter_parametrization/CMakeLists.txt @@ -1,7 +1,15 @@ # Copyright 2019, 2020, Visual Computing Lab, ISTI - Italian National Research Council -set(SOURCES filter_parametrization.cpp) +if (TARGET external-libigl AND TARGET Threads::Threads) -set(HEADERS filter_parametrization.h) + set(SOURCES filter_parametrization.cpp) -add_meshlab_plugin(filter_parametrization ${SOURCES} ${HEADERS}) + set(HEADERS filter_parametrization.h) + + add_meshlab_plugin(filter_parametrization ${SOURCES} ${HEADERS}) + + target_link_libraries(filter_parametrization PRIVATE external-libigl Threads::Threads) +else() + message( + STATUS "Skipping filter_parametrization - don't know about libigl or Threads::Threads on this system.") +endif() diff --git a/src/meshlabplugins/filter_parametrization/filter_parametrization.cpp b/src/meshlabplugins/filter_parametrization/filter_parametrization.cpp index c9de97161..e45af7f56 100644 --- a/src/meshlabplugins/filter_parametrization/filter_parametrization.cpp +++ b/src/meshlabplugins/filter_parametrization/filter_parametrization.cpp @@ -23,6 +23,12 @@ #include "filter_parametrization.h" +#include + +#include +#include +#include + FilterParametrizationPlugin::FilterParametrizationPlugin() { typeList = { FP_HARMONIC_PARAM}; @@ -45,7 +51,7 @@ QString FilterParametrizationPlugin::filterName(ActionIDType filterId) const { switch(filterId) { case FP_HARMONIC_PARAM : - return "Armonic Parametrization"; + return "Harmonic Parametrization"; default : assert(0); return ""; @@ -81,7 +87,12 @@ FilterPlugin::FilterArity FilterParametrizationPlugin::filterArity(const QAction int FilterParametrizationPlugin::getPreConditions(const QAction*) const { - return MeshModel::MM_NONE; + return MeshModel::MM_VERTCOORD | MeshModel::MM_FACEVERT; +} + +int FilterParametrizationPlugin::getRequirements(const QAction*) +{ + return MeshModel::MM_VERTTEXCOORD; } int FilterParametrizationPlugin::postCondition(const QAction*) const @@ -94,6 +105,7 @@ RichParameterList FilterParametrizationPlugin::initParameterList(const QAction * RichParameterList parlst; switch(ID(action)) { case FP_HARMONIC_PARAM : + parlst.addParam(RichInt("harm_function", 1,"N-Harmonic Function", "1 denotes harmonic function, 2 biharmonic, 3 triharmonic, etc.")); break; default : assert(0); @@ -106,12 +118,34 @@ std::map FilterParametrizationPlugin::applyFilter( const RichParameterList & par, MeshDocument &md, unsigned int& /*postConditionMask*/, - vcg::CallBackPos *cb) + vcg::CallBackPos *) { switch(ID(action)) { - case FP_HARMONIC_PARAM : + case FP_HARMONIC_PARAM : { + int f = par.getInt("harm_function"); + if (f < 1) + throw MLException("Invalid N-Harmonic Function value. Must be >= 1."); + + EigenMatrixX3m v = meshlab::vertexMatrix(md.mm()->cm); + Eigen::MatrixX3d verts = v.cast(); + Eigen::MatrixX3i faces = meshlab::faceMatrix(md.mm()->cm); + + Eigen::MatrixXd V_uv, bnd_uv; + Eigen::VectorXi bnd; + + igl::boundary_loop(faces,bnd); + igl::map_vertices_to_circle(verts,bnd,bnd_uv); + igl::harmonic(verts,faces,bnd,bnd_uv,1,V_uv); + + unsigned int i = 0; + for (auto& v : md.mm()->cm.vert){ + v.T().u() =V_uv(i, 0); + v.T().v() =V_uv(i, 1); + i++; + } break; + } default : wrongActionCalled(action); } diff --git a/src/meshlabplugins/filter_parametrization/filter_parametrization.h b/src/meshlabplugins/filter_parametrization/filter_parametrization.h index 347d0f927..b9fc1476c 100644 --- a/src/meshlabplugins/filter_parametrization/filter_parametrization.h +++ b/src/meshlabplugins/filter_parametrization/filter_parametrization.h @@ -47,6 +47,7 @@ public: FilterClass getClass(const QAction* a) const; FilterArity filterArity(const QAction*) const; int getPreConditions(const QAction *) const; + int getRequirements(const QAction *); int postCondition(const QAction* ) const; RichParameterList initParameterList(const QAction*, const MeshModel &/*m*/); std::map applyFilter( From a53ac8cd4933f2fbc2c03e1937d414d0b0f7af47 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Mon, 13 Dec 2021 13:07:32 +0100 Subject: [PATCH 03/13] lscm parametrization filter --- src/common/utilities/eigen_mesh_conversions.h | 9 ++-- .../filter_parametrization.cpp | 52 +++++++++++++++++-- .../filter_parametrization.h | 4 +- 3 files changed, 57 insertions(+), 8 deletions(-) diff --git a/src/common/utilities/eigen_mesh_conversions.h b/src/common/utilities/eigen_mesh_conversions.h index 7bf55913e..4ad5a0e06 100644 --- a/src/common/utilities/eigen_mesh_conversions.h +++ b/src/common/utilities/eigen_mesh_conversions.h @@ -31,9 +31,12 @@ typedef Eigen::Matrix EigenVectorXm; typedef Eigen::Matrix EigenVectorXb; typedef Eigen::Matrix EigenVectorXui; -typedef Eigen::Matrix EigenMatrixX2m; -typedef Eigen::Matrix EigenMatrixX3m; -typedef Eigen::Matrix EigenMatrixX4m; + +typedef Eigen::Matrix EigenMatrixX2m; +typedef Eigen::Matrix EigenMatrixX3m; +typedef Eigen::Matrix EigenMatrixX4m; + +typedef Eigen::Matrix EigenMatrixXm; namespace meshlab { diff --git a/src/meshlabplugins/filter_parametrization/filter_parametrization.cpp b/src/meshlabplugins/filter_parametrization/filter_parametrization.cpp index e45af7f56..e70540ed2 100644 --- a/src/meshlabplugins/filter_parametrization/filter_parametrization.cpp +++ b/src/meshlabplugins/filter_parametrization/filter_parametrization.cpp @@ -27,11 +27,12 @@ #include #include +#include #include FilterParametrizationPlugin::FilterParametrizationPlugin() -{ - typeList = { FP_HARMONIC_PARAM}; +{ + typeList = { FP_HARMONIC_PARAM, FP_LEAST_SQUARES_PARAM}; for(const ActionIDType& tt : typeList) actionList.push_back(new QAction(filterName(tt), this)); @@ -52,6 +53,8 @@ QString FilterParametrizationPlugin::filterName(ActionIDType filterId) const switch(filterId) { case FP_HARMONIC_PARAM : return "Harmonic Parametrization"; + case FP_LEAST_SQUARES_PARAM: + return "Least Squares Conformal Maps Parametrization"; default : assert(0); return ""; @@ -63,6 +66,8 @@ QString FilterParametrizationPlugin::filterInfo(ActionIDType filterId) const switch(filterId) { case FP_HARMONIC_PARAM : return ""; + case FP_LEAST_SQUARES_PARAM: + return ""; default : assert(0); return "Unknown Filter"; @@ -73,6 +78,7 @@ FilterParametrizationPlugin::FilterClass FilterParametrizationPlugin::getClass(c { switch(ID(a)) { case FP_HARMONIC_PARAM : + case FP_LEAST_SQUARES_PARAM: return FilterPlugin::Texture; default : assert(0); @@ -100,12 +106,15 @@ int FilterParametrizationPlugin::postCondition(const QAction*) const return MeshModel::MM_VERTTEXCOORD; } -RichParameterList FilterParametrizationPlugin::initParameterList(const QAction *action, const MeshModel &m) +RichParameterList FilterParametrizationPlugin::initParameterList(const QAction *action, const MeshModel &) { RichParameterList parlst; switch(ID(action)) { case FP_HARMONIC_PARAM : parlst.addParam(RichInt("harm_function", 1,"N-Harmonic Function", "1 denotes harmonic function, 2 biharmonic, 3 triharmonic, etc.")); + break; + case FP_LEAST_SQUARES_PARAM: + break; default : assert(0); @@ -134,7 +143,10 @@ std::map FilterParametrizationPlugin::applyFilter( Eigen::VectorXi bnd; igl::boundary_loop(faces,bnd); - igl::map_vertices_to_circle(verts,bnd,bnd_uv); + if (bnd.size() == 0) + throw MLException( + "Harmonic Parametrization can be applied only on meshes that have a boundary."); + igl::map_vertices_to_circle(verts, bnd, bnd_uv); igl::harmonic(verts,faces,bnd,bnd_uv,1,V_uv); unsigned int i = 0; @@ -146,6 +158,38 @@ std::map FilterParametrizationPlugin::applyFilter( break; } + case FP_LEAST_SQUARES_PARAM: { + EigenMatrixX3m v = meshlab::vertexMatrix(md.mm()->cm); + Eigen::MatrixX3d verts = v.cast(); + Eigen::MatrixX3i faces = meshlab::faceMatrix(md.mm()->cm); + Eigen::VectorXi bnd, boundaryPoints(2, 1); + + Eigen::MatrixXd V_uv; + + igl::boundary_loop(faces,bnd); + if (bnd.size() == 0) + throw MLException( + "Least Squares Conformal Maps Parametrization can be applied only on meshes that " + "have a boundary."); + + boundaryPoints(0) = bnd(0); + boundaryPoints(1) = bnd(bnd.size()/2); + + Eigen::MatrixXd bc(2,2); + bc<<0,0,1,0; + + // LSCM parametrization + igl::lscm(verts,faces,boundaryPoints,bc,V_uv); + + unsigned int i = 0; + for (auto& v : md.mm()->cm.vert){ + v.T().u() =V_uv(i, 0); + v.T().v() =V_uv(i, 1); + i++; + } + + break; + } default : wrongActionCalled(action); } diff --git a/src/meshlabplugins/filter_parametrization/filter_parametrization.h b/src/meshlabplugins/filter_parametrization/filter_parametrization.h index b9fc1476c..753d77403 100644 --- a/src/meshlabplugins/filter_parametrization/filter_parametrization.h +++ b/src/meshlabplugins/filter_parametrization/filter_parametrization.h @@ -35,7 +35,9 @@ class FilterParametrizationPlugin : public QObject, public FilterPlugin public: //enum used to give an ID to every filter implemented in the plugin - enum FileterIds {FP_HARMONIC_PARAM}; + enum FileterIds { + FP_HARMONIC_PARAM, + FP_LEAST_SQUARES_PARAM}; FilterParametrizationPlugin(); From 6dfd0bbd56aa8b2a55a92ffe955d3a8d89771edc Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Mon, 13 Dec 2021 15:05:07 +0100 Subject: [PATCH 04/13] add eigen unsupported --- src/vcglib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vcglib b/src/vcglib index be8f3e773..d3d496848 160000 --- a/src/vcglib +++ b/src/vcglib @@ -1 +1 @@ -Subproject commit be8f3e773fe46e991945df4674ca24cdce196195 +Subproject commit d3d496848dcdae95420989fc7ae1e56c57172b40 From 3adbc4ee637b1c319c5c877b879084dc93a25b24 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Thu, 13 Jan 2022 16:10:25 +0100 Subject: [PATCH 05/13] documentation filter parametrization --- .../filter_parametrization/filter_parametrization.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/meshlabplugins/filter_parametrization/filter_parametrization.cpp b/src/meshlabplugins/filter_parametrization/filter_parametrization.cpp index e70540ed2..ecf32dd93 100644 --- a/src/meshlabplugins/filter_parametrization/filter_parametrization.cpp +++ b/src/meshlabplugins/filter_parametrization/filter_parametrization.cpp @@ -63,11 +63,18 @@ QString FilterParametrizationPlugin::filterName(ActionIDType filterId) const QString FilterParametrizationPlugin::filterInfo(ActionIDType filterId) const { + QString commonDescription = + "The resulting parametrization is saved in the per vertex texture coordinates.
" + "The filter uses the original code provided in the " + "libigl library.
"; switch(filterId) { case FP_HARMONIC_PARAM : - return ""; + return "Computes a single patch, fixed boundary harmonic parametrization of a mesh. The " + "filter requires that the input mesh has a single fixed boundary." + + commonDescription; case FP_LEAST_SQUARES_PARAM: - return ""; + return "Compuites a least squares conformal maps parametrization of a mesh. " + + commonDescription; default : assert(0); return "Unknown Filter"; From fc018aa683c92999e52a6997c70051b8d558decf Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Thu, 20 Jan 2022 13:06:50 +0100 Subject: [PATCH 06/13] update vcglib --- src/vcglib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vcglib b/src/vcglib index d3d496848..5cdefce22 160000 --- a/src/vcglib +++ b/src/vcglib @@ -1 +1 @@ -Subproject commit d3d496848dcdae95420989fc7ae1e56c57172b40 +Subproject commit 5cdefce22e377369d7c8eede0c78a6fb224b81de From adde96dd94523e3903f9f59cb1f9827691d1023a Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Thu, 20 Jan 2022 13:29:34 +0100 Subject: [PATCH 07/13] python filter names --- .../filter_parametrization.cpp | 13 +++++++++++++ .../filter_parametrization/filter_parametrization.h | 1 + 2 files changed, 14 insertions(+) diff --git a/src/meshlabplugins/filter_parametrization/filter_parametrization.cpp b/src/meshlabplugins/filter_parametrization/filter_parametrization.cpp index ecf32dd93..51eaffcda 100644 --- a/src/meshlabplugins/filter_parametrization/filter_parametrization.cpp +++ b/src/meshlabplugins/filter_parametrization/filter_parametrization.cpp @@ -61,6 +61,19 @@ QString FilterParametrizationPlugin::filterName(ActionIDType filterId) const } } +QString FilterParametrizationPlugin::pythonFilterName(ActionIDType filter) const +{ + switch(filter) { + case FP_HARMONIC_PARAM : + return "compute_texcoord_parametrization_harmonic"; + case FP_LEAST_SQUARES_PARAM: + return "compute_texcoord_parametrization_least_squares_conformal_maps"; + default : + assert(0); + return ""; + } +} + QString FilterParametrizationPlugin::filterInfo(ActionIDType filterId) const { QString commonDescription = diff --git a/src/meshlabplugins/filter_parametrization/filter_parametrization.h b/src/meshlabplugins/filter_parametrization/filter_parametrization.h index 753d77403..6d1c69150 100644 --- a/src/meshlabplugins/filter_parametrization/filter_parametrization.h +++ b/src/meshlabplugins/filter_parametrization/filter_parametrization.h @@ -45,6 +45,7 @@ public: QString vendor() const; QString filterName(ActionIDType filter) const; + QString pythonFilterName(ActionIDType filter) const; QString filterInfo(ActionIDType filter) const; FilterClass getClass(const QAction* a) const; FilterArity filterArity(const QAction*) const; From 17640937f46f591fd68e313bb457a0701b4ca345 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 21 Jan 2022 15:56:09 +0100 Subject: [PATCH 08/13] new deterministic searcher --- src/common/CMakeLists.txt | 8 +- src/common/plugins/action_searcher.cpp | 176 +++++++++++++++++++++++++ src/common/plugins/action_searcher.h | 62 +++++++++ src/common/searcher.cpp | 151 --------------------- src/common/searcher.h | 58 -------- src/meshlab/additionalgui.cpp | 33 ++--- src/meshlab/additionalgui.h | 6 +- src/meshlab/mainwindow.h | 2 +- src/meshlab/mainwindow_Init.cpp | 4 +- 9 files changed, 256 insertions(+), 244 deletions(-) create mode 100644 src/common/plugins/action_searcher.cpp create mode 100644 src/common/plugins/action_searcher.h delete mode 100644 src/common/searcher.cpp delete mode 100644 src/common/searcher.h diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index aaae96398..ad1c62a0b 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -65,6 +65,7 @@ set(HEADERS plugins/interfaces/filter_plugin.h plugins/interfaces/io_plugin.h plugins/interfaces/render_plugin.h + plugins/action_searcher.h plugins/meshlab_plugin_type.h plugins/plugin_manager.h python/function.h @@ -81,8 +82,7 @@ set(HEADERS ml_selection_buffers.h ml_thread_safe_memory_info.h mlapplication.h - mlexception.h - searcher.h) + mlexception.h) set(SOURCES ml_document/helpers/mesh_document_state_data.cpp @@ -107,6 +107,7 @@ set(SOURCES plugins/interfaces/decorate_plugin.cpp plugins/interfaces/filter_plugin.cpp plugins/interfaces/io_plugin.cpp + plugins/action_searcher.cpp plugins/meshlab_plugin_type.cpp plugins/plugin_manager.cpp python/function.cpp @@ -121,8 +122,7 @@ set(SOURCES filterscript.cpp ml_selection_buffers.cpp ml_thread_safe_memory_info.cpp - mlapplication.cpp - searcher.cpp) + mlapplication.cpp) set(RESOURCES meshlab-common.qrc) diff --git a/src/common/plugins/action_searcher.cpp b/src/common/plugins/action_searcher.cpp new file mode 100644 index 000000000..5450e26f3 --- /dev/null +++ b/src/common/plugins/action_searcher.cpp @@ -0,0 +1,176 @@ +/***************************************************************************** + * MeshLab o o * + * A versatile mesh processing toolbox o o * + * _ O _ * + * Copyright(C) 2005-2022 \/)\/ * + * Visual Computing Lab /\/| * + * ISTI - Italian National Research Council | * + * \ * + * All rights reserved. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * + * for more details. * + * * + ****************************************************************************/ + +#include "action_searcher.h" + +#include +#include + +#include "interfaces/filter_plugin.h" + +ActionSearcher::ActionSearcher() +{ +} + +void ActionSearcher::clear() +{ + titleActionsMap.clear(); +} + +/** + * @brief Adds the given action to the ActionSearcher, allowing queries to it. + * @param action + */ +void ActionSearcher::addAction(QAction *action, bool usePythonFilterNames) +{ + if (action != nullptr) { + if (!usePythonFilterNames) { + // add title to the action map + QString title = action->text(); + title = title.toLower(); + title.remove(ignexp); + QStringList res = title.split(sepexp, Qt::SkipEmptyParts); + res.removeDuplicates(); + addSubStrings(res); + for (const QString& str : qAsConst(res)) { + titleActionsMap[str].push_back(action); + } + } + else { + // if the action is a filter, we should add also the python name to the search + QObject* parent = action->parent(); + FilterPlugin* fp = qobject_cast(parent); + if (fp) { + QString title = fp->pythonFilterName(action); + title.replace("_", " "); + title.remove(ignexp); + QStringList res = title.split(sepexp, Qt::SkipEmptyParts); + res.removeDuplicates(); + addSubStrings(res); + for (const QString& str : qAsConst(res)) { + titleActionsMap[str].push_back(action); + } + } + } + + // add info to the action map + QString info = action->toolTip(); + info = info.toLower(); + info.remove(ignexp); + QStringList res = info.split(sepexp, Qt::SkipEmptyParts); + res.removeDuplicates(); + addSubStrings(res); + for (const QString& str : qAsConst(res)) { + infoActionsMap[str].push_back(action); + } + } +} + +/** + * @brief Performs a query using the inputString and returns an array containing the best matching + * actions matching to the input string. + * + * The array will have maximum size equal to maxNumberactions. + * @param inputString: query string + * @param maxNumberActions: maximum size of the output array + * @return array containing the best matching actions + */ +std::vector ActionSearcher::bestMatchingActions(QString inputString, int maxNumberActions) const +{ + std::vector res; + + // clean the input string + inputString = inputString.toLower(); + inputString.replace("_", " "); // to allow python search + inputString.remove(ignexp); + + // split the input string + QStringList inputList = inputString.split(sepexp, Qt::SkipEmptyParts); + inputList.removeDuplicates(); + + const float bonuspertitleword = 1.0f / std::pow(10,inputList.size()); + + // store the ranking for each action + std::map actionRanking; + + // for each input string + for (const QString& str : qAsConst(inputList)) { + auto it = titleActionsMap.find(str); + // if matches in a title of an action + if (it != titleActionsMap.end()) { + for (QAction* act : it->second) { // for each matching action, increment its ranking + actionRanking[act] += bonuspertitleword; + } + } + it = infoActionsMap.find(str); + // if matches in a info of an action + if (it != infoActionsMap.end()) { + for (QAction* act : it->second) { // for each matching action, increment its ranking + actionRanking[act]++; + } + } + } + + std::map> rankingMap; // flipped map of actionRanking + // populate the flipped map + for (const auto& p : actionRanking) { + // for each ranking, push another action havint that ranking + rankingMap[p.second].push_back(p.first); + } + // at the end, the map will store the actions stored by ranking + + int count = 0; // used to stop the number of inserted actions in the resulting vector + + // reverse iteration: start from the biggest ranking + for (auto it = rankingMap.rbegin(); it != rankingMap.rend() && count < maxNumberActions; ++it) { + auto v1 = it->second; + // need to sort properly actions having the same ranking, since they would be sorted using + // memory addresses, that are not deterministic: we use action titles + std::sort(v1.begin(), v1.end(), ActionComparator()); + // insert the sorted actions to the result vector + res.insert(res.end(), v1.begin(), v1.end()); + count += it->second.size(); + } + // if at the end the number exceeded the maximum number, we truncate the array + if (count >= maxNumberActions) { + res.resize(maxNumberActions); + } + return res; +} + +void ActionSearcher::addSubStrings(QStringList &res) +{ + QStringList resWithPrefix; + foreach(QString str, res) + { + QString strPref = str; + resWithPrefix.push_back(strPref); + for(int i=0;i +#include +#include +#include + +class ActionSearcher +{ +public: + ActionSearcher(); + + void clear(); + void addAction(QAction* action, bool usePythonFilterNames = false); + + std::vector bestMatchingActions(QString inputString, int maxNumberActions) const; + +private: + const QRegExp sepexp = QRegExp("\\W+"); + const QRegExp ignexp = QRegExp( + "\\b(an|the|of|it|as|in|by|and|or|for)\\b|\\b[a-z]\\b|'s\\b|\\.|<[^>]*>"); + + // map that stores, for each string, all the actions that store that string in their titles + std::map> titleActionsMap; + + // map that stores, for each stirng, all the actions that store that stirng in their info + std::map> infoActionsMap; + + struct ActionComparator { + bool operator()(QAction* a1, QAction* a2) { + return a1->text() < a2->text(); + } + }; + + static void addSubStrings(QStringList& res); +}; + +#endif // ACTION_SEARCHER_H diff --git a/src/common/searcher.cpp b/src/common/searcher.cpp deleted file mode 100644 index 37754dd4c..000000000 --- a/src/common/searcher.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include "searcher.h" -#include "mlexception.h" - -#include - -WordActionsMap::WordActionsMap() -:wordacts() -{ - -} - -void WordActionsMap::addWordsPerAction(QAction& act,const QStringList& words) -{ - foreach(QString word,words) - wordacts[word].push_back(&act); -} - -void WordActionsMap::removeActionReferences(QAction& act ) -{ - for(QMap >::iterator it = wordacts.begin();it != wordacts.end();++it) - it.value().removeAll(&act); -} - -bool WordActionsMap::getActionsPerWord( const QString& word,QList& res ) const -{ - QMap< QString,QList >::const_iterator it = wordacts.find(word); - if (it != wordacts.end()) - { - res = it.value(); - return true; - } - return false; -} - -void WordActionsMap::clear() -{ - wordacts.clear(); -} - -WordActionsMapAccessor::WordActionsMapAccessor() -:map(),sepexp(),ignexp() -{ - sepexp.setPattern("\\W+"); - ignexp.setPattern("\\b(an|the|of|it|as|in|by|and|or|for)\\b|\\b[a-z]\\b|'s\\b|\\.|<[^>]*>"); -} - -void WordActionsMapAccessor::addWordsPerAction(QAction& act,const QString& st ) -{ - QStringList wlist; - purifiedSplit(st,wlist); - addSubStrings(wlist); - map.addWordsPerAction(act,wlist); -} - -int WordActionsMapAccessor::rankedMatchesPerInputString( const QString& input,RankedMatches& rm ) const -{ - QStringList inputlist; - purifiedSplit(input,inputlist); - return rm.computeRankedMatches(inputlist,map); -} - -void WordActionsMapAccessor::purifiedSplit( const QString& input,QStringList& res ) const -{ - res.clear(); - QString tmp = input; - tmp = tmp.toLower(); - tmp.remove(ignexp); - res = tmp.split(sepexp,QString::SkipEmptyParts); - res.removeDuplicates(); -} - -void WordActionsMapAccessor::addSubStrings( QStringList& res ) const -{ - QStringList resWithPrefix; - foreach(QString str, res) - { - QString strPref = str; - resWithPrefix.push_back(strPref); - for(int i=0;i wordmatchesperaction; - ranking.clear(); - int inputstsize = inputst.size(); - ranking.resize(inputstsize); - float bonuspertitleword = 0.0f; - if (matchesontitlearemoreimportant) - bonuspertitleword = 1.0f / std::pow(10,inputstsize); - foreach(const QString& st,inputst) - { - QList res; - bool found = map.getActionsPerWord(st,res); - if (found) - { - foreach(QAction* act, res) - { - ++wordmatchesperaction[act]; - QString title = act->text().toLower().trimmed(); - if (title.contains(st)) - wordmatchesperaction[act] = wordmatchesperaction[act] + bonuspertitleword; - } - } - } - - QMap > rankedmatches; - for (QMap::iterator it = wordmatchesperaction.begin(); it != wordmatchesperaction.end(); ++it) - rankedmatches[it.value()].push_back(it.key()); - - int maxindex = -1; - for(QMap >::iterator it = rankedmatches.end() - 1;it != rankedmatches.begin() - 1;--it) - { - int index = std::floor(it.key()) - 1; - if (index >= ranking.size()) - { - throw InvalidInvariantException("WARNING! Index contained in wordmatchesperaction it's out-of-bound."); - return 0; - } - if (index > maxindex) - maxindex = index; - ranking[index].append(it.value()); - } - return maxindex + 1; -} - -void RankedMatches::getActionsWithNMatches( const int n,QList& res ) -{ - res.clear(); - int index = n -1; - if ((index >= ranking.size()) || (n < 1)) - { - throw InvalidInvariantException(QString("WARNING! Parameter n MUST be in the range [1..") + QString::number(ranking.size()) + "]."); - return; - } - res = ranking[index]; -} - - diff --git a/src/common/searcher.h b/src/common/searcher.h deleted file mode 100644 index ce01d4d2a..000000000 --- a/src/common/searcher.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef SEARCHER_H -#define SEARCHER_H - -#include -#include -#include -#include -#include -#include -#include - -class WordActionsMap -{ -public: - WordActionsMap(); - void addWordsPerAction(QAction& act,const QStringList& words); - void removeActionReferences(QAction& act); - bool getActionsPerWord( const QString& word,QList& res ) const; - void clear(); -private: - QMap > wordacts; -}; - -class RankedMatches; - -class WordActionsMapAccessor -{ -public: - WordActionsMapAccessor(); - void addWordsPerAction(QAction& act,const QString& st); - inline void removeActionReferences(QAction& act) {map.removeActionReferences(act);} - inline void setSeparator(const QRegExp& sep) {sepexp = sep;} - inline void setIgnoredWords(const QRegExp& ign) {ignexp = ign;} - int rankedMatchesPerInputString(const QString& input,RankedMatches& rm) const; - inline QRegExp separtor() const {return sepexp;} - inline QRegExp ignored() const {return ignexp;} - void clear() {map.clear(); sepexp = QRegExp(); ignexp = QRegExp();} - -private: - void purifiedSplit(const QString& input,QStringList& res) const; - void addSubStrings(QStringList& res) const; - WordActionsMap map; - QRegExp sepexp; - QRegExp ignexp; -}; - -class RankedMatches -{ -public: - RankedMatches(); - void getActionsWithNMatches(const int n,QList& res); -private: - friend int WordActionsMapAccessor::rankedMatchesPerInputString(const QString& input,RankedMatches& rm) const; - int computeRankedMatches(const QStringList& inputst,const WordActionsMap& map,bool matchesontitlearemoreimportant = true); - QVector > ranking; -}; - -#endif diff --git a/src/meshlab/additionalgui.cpp b/src/meshlab/additionalgui.cpp index 64550695f..ed52e6557 100644 --- a/src/meshlab/additionalgui.cpp +++ b/src/meshlab/additionalgui.cpp @@ -8,7 +8,7 @@ #include #include -SearchMenu::SearchMenu(const WordActionsMapAccessor& wm,const int max,QWidget* parent,const int fixedwidth) +SearchMenu::SearchMenu(const ActionSearcher& wm,const int max,QWidget* parent,const int fixedwidth) :MenuWithToolTip(QString(),parent),searchline(NULL),wama(wm),maxres(max),fixedwidthsize(fixedwidth) { searchline = new MenuLineEdit(this); @@ -24,29 +24,14 @@ SearchMenu::SearchMenu(const WordActionsMapAccessor& wm,const int max,QWidget* p void SearchMenu::getResults(const QString& text,QList& result) { - try - { - RankedMatches rm; - int ii = wama.rankedMatchesPerInputString(text,rm); - int inserted = 0; - while(ii > 0) - { - QList myacts; - rm.getActionsWithNMatches(ii,myacts); - if (inserted + myacts.size() > maxres) - myacts = myacts.mid(0,myacts.size() - (inserted + myacts.size() - maxres)); - result.append(myacts); - QAction* sep = new QAction(this); - sep->setSeparator(true); - result.append(sep); - inserted += myacts.size(); - --ii; - } - } - catch(InvalidInvariantException& e) - { - qDebug() << "WARNING!!!!!!!!!!!!!!!!!!!" << e.what() << "\n"; - } + std::vector rm = wama.bestMatchingActions(text, 15); + + for (QAction* act : rm) { + result.append(act); + } + QAction* sep = new QAction(this); + sep->setSeparator(true); + result.append(sep); } void SearchMenu::updateGUI( const QList& results ) diff --git a/src/meshlab/additionalgui.h b/src/meshlab/additionalgui.h index bf5667dc5..c076803de 100644 --- a/src/meshlab/additionalgui.h +++ b/src/meshlab/additionalgui.h @@ -20,7 +20,7 @@ #include #include #include -#include "../common/searcher.h" +#include "../common/plugins/action_searcher.h" #include #include #include @@ -75,7 +75,7 @@ class SearchMenu : public MenuWithToolTip { Q_OBJECT public: - SearchMenu(const WordActionsMapAccessor& wm,const int max,QWidget* parent,const int fixedwidth = -1); + SearchMenu(const ActionSearcher& wm,const int max,QWidget* parent,const int fixedwidth = -1); int& searchLineWidth(); void clearResults(); QSize sizeHint () const; @@ -84,7 +84,7 @@ protected: void resizeEvent ( QResizeEvent * event); private: MenuLineEdit* searchline; - const WordActionsMapAccessor& wama; + const ActionSearcher& wama; int maxres; int fixedwidthsize; diff --git a/src/meshlab/mainwindow.h b/src/meshlab/mainwindow.h index 3f06f612e..0df44c450 100644 --- a/src/meshlab/mainwindow.h +++ b/src/meshlab/mainwindow.h @@ -361,7 +361,7 @@ public: QMenu* rasterLayerMenu() { return filterMenuRasterLayer; } private: - WordActionsMapAccessor wama; + ActionSearcher wama; //////// ToolBars /////////////// QToolBar* mainToolBar; QToolBar* decoratorToolBar; diff --git a/src/meshlab/mainwindow_Init.cpp b/src/meshlab/mainwindow_Init.cpp index 56bd70b14..b1c79cdd5 100644 --- a/src/meshlab/mainwindow_Init.cpp +++ b/src/meshlab/mainwindow_Init.cpp @@ -36,7 +36,6 @@ #include #include #include "mainwindow.h" -#include #include #include #include @@ -692,8 +691,7 @@ void MainWindow::initMenuForSearching(QMenu* menu) void MainWindow::initItemForSearching(QAction* act) { - QString tx = act->text() + " " + act->toolTip(); - wama.addWordsPerAction(*act, tx); + wama.addAction(act); } QString MainWindow::getDecoratedFileName(const QString& name) From 8d0638f23c978a1ce55761d42d35eb6eb87d9e56 Mon Sep 17 00:00:00 2001 From: Paolo Cignoni Date: Sun, 23 Jan 2022 23:22:22 +0100 Subject: [PATCH 09/13] added randomSeed param to random displacement --- .../filter_sample/filter_sample.cpp | 25 +++++++++++-------- .../filter_sample/filter_sample.h | 1 + 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/meshlabplugins/filter_sample/filter_sample.cpp b/src/meshlabplugins/filter_sample/filter_sample.cpp index f875b5f56..c658256f1 100644 --- a/src/meshlabplugins/filter_sample/filter_sample.cpp +++ b/src/meshlabplugins/filter_sample/filter_sample.cpp @@ -162,6 +162,7 @@ RichParameterList FilterSamplePlugin::initParameterList(const QAction *action,co case FP_MOVE_VERTEX : parlst.addParam(RichBool ("UpdateNormals", true, "Recompute normals", "Toggle the recomputation of the normals after the random displacement.\n\nIf disabled the face normals will remains unchanged resulting in a visually pleasant effect.")); parlst.addParam(RichAbsPerc("Displacement", m.cm.bbox.Diag()/100.0f,0.0f,m.cm.bbox.Diag(), "Max displacement", "The vertex are displaced of a vector whose norm is bounded by this value")); + parlst.addParam(RichInt("RandomSeed", 0, "Random Seed", "The seed used to generate random values. If seed is zero no random seed is used")); break; default : assert(0); @@ -181,7 +182,7 @@ std::map FilterSamplePlugin::applyFilter(const QAction * { switch(ID(action)) { case FP_MOVE_VERTEX : - vertexDisplacement(md, cb, parameters.getBool("UpdateNormals"), parameters.getAbsPerc("Displacement")); + vertexDisplacement(md, cb, parameters.getInt("RandomSeed"), parameters.getBool("UpdateNormals"), parameters.getAbsPerc("Displacement")); break; default : wrongActionCalled(action); @@ -190,23 +191,25 @@ std::map FilterSamplePlugin::applyFilter(const QAction * } bool FilterSamplePlugin::vertexDisplacement( - MeshDocument &md, - vcg::CallBackPos *cb, - bool updateNormals, - Scalarm max_displacement) + MeshDocument &md, + vcg::CallBackPos *cb, + int randomSeed, + bool updateNormals, + Scalarm max_displacement) { CMeshO &m = md.mm()->cm; - srand(time(NULL)); + if(randomSeed==0) srand(time(NULL)); + else srand(randomSeed); for(unsigned int i = 0; i< m.vert.size(); i++){ // Typical usage of the callback for showing a nice progress bar in the bottom. // First parameter is a 0..100 number indicating percentage of completion, the second is an info string. cb(100*i/m.vert.size(), "Randomly Displacing..."); - - Scalarm rndax = (Scalarm(2.0*rand())/RAND_MAX - 1.0 ) *max_displacement; - Scalarm rnday = (Scalarm(2.0*rand())/RAND_MAX - 1.0 ) *max_displacement; - Scalarm rndaz = (Scalarm(2.0*rand())/RAND_MAX - 1.0 ) *max_displacement; - m.vert[i].P() += Point3m(rndax,rnday,rndaz); + + Scalarm rndax = (Scalarm(2.0*rand())/float(RAND_MAX) - 1.0 ) *max_displacement; + Scalarm rnday = (Scalarm(2.0*rand())/float(RAND_MAX) - 1.0 ) *max_displacement; + Scalarm rndaz = (Scalarm(2.0*rand())/float(RAND_MAX) - 1.0 ) *max_displacement; + m.vert[i].P() += Point3m(rndax,rnday,rndaz); } // Log function dump textual info in the lower part of the MeshLab screen. diff --git a/src/meshlabplugins/filter_sample/filter_sample.h b/src/meshlabplugins/filter_sample/filter_sample.h index 408c1ed30..4e8c145b5 100644 --- a/src/meshlabplugins/filter_sample/filter_sample.h +++ b/src/meshlabplugins/filter_sample/filter_sample.h @@ -74,6 +74,7 @@ private: bool vertexDisplacement( MeshDocument &md, vcg::CallBackPos *cb, + int randomSeed, bool updateNormals, Scalarm max_displacement); }; From 98a9fc8bf35e076e22e94cbd2adcede43145f46e Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Mon, 24 Jan 2022 11:59:48 +0100 Subject: [PATCH 10/13] consistent search engine organization --- src/meshlab/dialogs/plugin_info_dialog.cpp | 2 +- src/meshlab/mainwindow.h | 2 - src/meshlab/mainwindow_Init.cpp | 49 ++++------------------ src/meshlab/mainwindow_RunTime.cpp | 1 + 4 files changed, 11 insertions(+), 43 deletions(-) diff --git a/src/meshlab/dialogs/plugin_info_dialog.cpp b/src/meshlab/dialogs/plugin_info_dialog.cpp index 6859183f0..e17c565a8 100644 --- a/src/meshlab/dialogs/plugin_info_dialog.cpp +++ b/src/meshlab/dialogs/plugin_info_dialog.cpp @@ -119,7 +119,7 @@ void PluginInfoDialog::on_loadPluginsPushButton_clicked() QStringList fileList = QFileDialog::getOpenFileNames(this, "Load Plugins", "", pluginFileFormat); PluginManager& pm = meshlab::pluginManagerInstance(); bool loadOk = false; - for (const QString& fileName : fileList){ + for (const QString& fileName : qAsConst(fileList)){ QFileInfo finfo(fileName); try { diff --git a/src/meshlab/mainwindow.h b/src/meshlab/mainwindow.h index 0df44c450..407391ee0 100644 --- a/src/meshlab/mainwindow.h +++ b/src/meshlab/mainwindow.h @@ -256,8 +256,6 @@ private: void createActions(); void createMenus(); void initSearchEngine(); - void initItemForSearching(QAction* act); - void initMenuForSearching(QMenu* menu); void fillFilterMenu(); void fillRenderMenu(); void fillShadersMenu(); diff --git a/src/meshlab/mainwindow_Init.cpp b/src/meshlab/mainwindow_Init.cpp index b1c79cdd5..3d8bf9ba0 100644 --- a/src/meshlab/mainwindow_Init.cpp +++ b/src/meshlab/mainwindow_Init.cpp @@ -658,40 +658,23 @@ void MainWindow::createMenus() void MainWindow::initSearchEngine() { + wama.clear(); for (const auto& p : PM.filterPluginIterator()){ for (QAction* act : p->actions()) - initItemForSearching(act); + wama.addAction(act); } - /*for (const auto& p : PM.editPluginFactoryIterator()){ + for (const auto& p : PM.editPluginFactoryIterator()){ for (QAction* act : p->actions()) - initItemForSearching(act); + wama.addAction(act); } for (const auto& p : PM.renderPluginIterator()){ for (QAction* act : p->actions()) - initItemForSearching(act); - }*/ - - initMenuForSearching(editMenu); - initMenuForSearching(renderMenu); -} - -void MainWindow::initMenuForSearching(QMenu* menu) -{ - if (menu == NULL) - return; - const QList& acts = menu->actions(); - for(QAction* act: acts) { - QMenu* submenu = act->menu(); - if (!act->isSeparator() && (submenu == NULL)) - initItemForSearching(act); - else if (!act->isSeparator()) - initMenuForSearching(submenu); + wama.addAction(act); + } + for (const auto& p : PM.decoratePluginIterator()){ + for (QAction* act : p->actions()) + wama.addAction(act); } -} - -void MainWindow::initItemForSearching(QAction* act) -{ - wama.addAction(act); } QString MainWindow::getDecoratedFileName(const QString& name) @@ -928,20 +911,6 @@ void MainWindow::updateAllPluginsActions() filterToolBar->clear(); updateFilterToolBar(); - - //TODO update the searcher: this seems to be an impossible task due to unreadable code. - //for now, just close and reopen meshlab.... - /* - disconnect(searchShortCut, SIGNAL(activated()), searchButton, SLOT(openMenu())); - wama.clear(); - delete searchMenu; - - initSearchEngine(); - int longest = longestActionWidthInAllMenus(); - searchMenu = new SearchMenu(wama, 15, searchButton, longest); - searchButton->setMenu(searchMenu); - connect(searchShortCut, SIGNAL(activated()), searchButton, SLOT(openMenu())); - */ } void MainWindow::loadDefaultSettingsFromPlugins() diff --git a/src/meshlab/mainwindow_RunTime.cpp b/src/meshlab/mainwindow_RunTime.cpp index 69e7e87c9..658ed4e7d 100644 --- a/src/meshlab/mainwindow_RunTime.cpp +++ b/src/meshlab/mainwindow_RunTime.cpp @@ -2576,6 +2576,7 @@ void MainWindow::aboutPlugins() PluginInfoDialog dialog(this); dialog.exec(); updateAllPluginsActions(); + initSearchEngine(); QSettings settings; QStringList disabledPlugins; for (MeshLabPlugin* pf : PM.pluginIterator(true)){ From 1ad302e21a1920c6e71e2b293a1d1cbbf5baa0af Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Mon, 24 Jan 2022 12:02:19 +0100 Subject: [PATCH 11/13] include filter name always also when searching for python filter names --- src/common/plugins/action_searcher.cpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/common/plugins/action_searcher.cpp b/src/common/plugins/action_searcher.cpp index 5450e26f3..1799de16c 100644 --- a/src/common/plugins/action_searcher.cpp +++ b/src/common/plugins/action_searcher.cpp @@ -44,19 +44,18 @@ void ActionSearcher::clear() void ActionSearcher::addAction(QAction *action, bool usePythonFilterNames) { if (action != nullptr) { - if (!usePythonFilterNames) { - // add title to the action map - QString title = action->text(); - title = title.toLower(); - title.remove(ignexp); - QStringList res = title.split(sepexp, Qt::SkipEmptyParts); - res.removeDuplicates(); - addSubStrings(res); - for (const QString& str : qAsConst(res)) { - titleActionsMap[str].push_back(action); - } + + // add title to the action map + QString title = action->text(); + title = title.toLower(); + title.remove(ignexp); + QStringList res = title.split(sepexp, Qt::SkipEmptyParts); + res.removeDuplicates(); + addSubStrings(res); + for (const QString& str : qAsConst(res)) { + titleActionsMap[str].push_back(action); } - else { + if (usePythonFilterNames) { // if the action is a filter, we should add also the python name to the search QObject* parent = action->parent(); FilterPlugin* fp = qobject_cast(parent); @@ -77,7 +76,7 @@ void ActionSearcher::addAction(QAction *action, bool usePythonFilterNames) QString info = action->toolTip(); info = info.toLower(); info.remove(ignexp); - QStringList res = info.split(sepexp, Qt::SkipEmptyParts); + res = info.split(sepexp, Qt::SkipEmptyParts); res.removeDuplicates(); addSubStrings(res); for (const QString& str : qAsConst(res)) { From 72b05e717efaa7cba3da21c8c18b8d2ca0a1115c Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Mon, 24 Jan 2022 15:06:46 +0100 Subject: [PATCH 12/13] singleton instance of action searcher --- src/common/globals.cpp | 7 +++++++ src/common/globals.h | 2 ++ .../plugins/containers/filter_plugin_container.cpp | 11 +++++++++++ .../plugins/containers/filter_plugin_container.h | 1 + src/common/plugins/plugin_manager.cpp | 5 +++++ src/common/plugins/plugin_manager.h | 1 + 6 files changed, 27 insertions(+) diff --git a/src/common/globals.cpp b/src/common/globals.cpp index 1c31602fd..64f6f398a 100644 --- a/src/common/globals.cpp +++ b/src/common/globals.cpp @@ -29,6 +29,7 @@ #include "parameters/rich_parameter_list.h" #include "plugins/plugin_manager.h" +#include "plugins/action_searcher.h" #include "python/function_set.h" QString basePath() @@ -141,6 +142,12 @@ PluginManager& meshlab::pluginManagerInstance() return pm; } +ActionSearcher& meshlab::actionSearcherInstance() +{ + static ActionSearcher as; + return as; +} + pymeshlab::FunctionSet& pymeshlab::functionSetInstance() { static FunctionSet fs(meshlab::pluginManagerInstance()); diff --git a/src/common/globals.h b/src/common/globals.h index a529ca326..1d073b931 100644 --- a/src/common/globals.h +++ b/src/common/globals.h @@ -37,6 +37,7 @@ class RichParameterList; class PluginManager; +class ActionSearcher; namespace meshlab { @@ -46,6 +47,7 @@ QString logDebugFileName(); RichParameterList& defaultGlobalParameterList(); PluginManager& pluginManagerInstance(); +ActionSearcher& actionSearcherInstance(); // keep these functions inlined please // each plugin that uses them need to have their own definition diff --git a/src/common/plugins/containers/filter_plugin_container.cpp b/src/common/plugins/containers/filter_plugin_container.cpp index d79524c6b..a0244fc53 100644 --- a/src/common/plugins/containers/filter_plugin_container.cpp +++ b/src/common/plugins/containers/filter_plugin_container.cpp @@ -61,6 +61,17 @@ QAction* FilterPluginContainer::filterAction(const QString& name) return nullptr; } +FilterPlugin *FilterPluginContainer::pluginOfFilter(const QAction *action) const +{ + for (FilterPlugin* fp : filterPlugins) { + std::list al = fp->actions(); + auto it = std::find(al.begin(), al.end(), action); + if (it != al.end()) + return fp; + } + return nullptr; +} + FilterPluginContainer::FilterPluginRangeIterator FilterPluginContainer::filterPluginIterator(bool iterateAlsoDisabledPlugins) const { return FilterPluginRangeIterator(this, iterateAlsoDisabledPlugins); diff --git a/src/common/plugins/containers/filter_plugin_container.h b/src/common/plugins/containers/filter_plugin_container.h index 02d6e7de7..b6c684f66 100644 --- a/src/common/plugins/containers/filter_plugin_container.h +++ b/src/common/plugins/containers/filter_plugin_container.h @@ -45,6 +45,7 @@ public: void eraseFilterPlugin(FilterPlugin* iFilter); QAction* filterAction(const QString& name); + FilterPlugin* pluginOfFilter(const QAction* action) const; FilterPluginRangeIterator filterPluginIterator(bool iterateAlsoDisabledPlugins = false) const; diff --git a/src/common/plugins/plugin_manager.cpp b/src/common/plugins/plugin_manager.cpp index 3d2aa08c5..0d0c447b8 100644 --- a/src/common/plugins/plugin_manager.cpp +++ b/src/common/plugins/plugin_manager.cpp @@ -291,6 +291,11 @@ QAction* PluginManager::filterAction(const QString& name) return filterPlugins.filterAction(name); } +FilterPlugin* PluginManager::getFilterPluginFromAction(const QAction *action) const +{ + return filterPlugins.pluginOfFilter(action); +} + IOPlugin* PluginManager::inputMeshPlugin(const QString& inputFormat) const { return ioPlugins.inputMeshPlugin(inputFormat); diff --git a/src/common/plugins/plugin_manager.h b/src/common/plugins/plugin_manager.h index ed44098b3..1bb1769c5 100644 --- a/src/common/plugins/plugin_manager.h +++ b/src/common/plugins/plugin_manager.h @@ -62,6 +62,7 @@ public: DecoratePlugin* getDecoratePlugin(const QString& name); QAction* filterAction(const QString& name); + FilterPlugin* getFilterPluginFromAction(const QAction* action) const; IOPlugin* inputMeshPlugin(const QString& inputFormat) const; IOPlugin* outputMeshPlugin(const QString& outputFormat) const; From a28309b6bcdd4ee5ed31777473fd20eb805cac1a Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Mon, 24 Jan 2022 16:37:38 +0100 Subject: [PATCH 13/13] using same single instance of action searcher --- src/meshlab/mainwindow.h | 2 +- src/meshlab/mainwindow_Init.cpp | 27 ++++++++++++++------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/meshlab/mainwindow.h b/src/meshlab/mainwindow.h index 407391ee0..582d9ad38 100644 --- a/src/meshlab/mainwindow.h +++ b/src/meshlab/mainwindow.h @@ -359,7 +359,7 @@ public: QMenu* rasterLayerMenu() { return filterMenuRasterLayer; } private: - ActionSearcher wama; + ActionSearcher& searcher; //////// ToolBars /////////////// QToolBar* mainToolBar; QToolBar* decoratorToolBar; diff --git a/src/meshlab/mainwindow_Init.cpp b/src/meshlab/mainwindow_Init.cpp index 3d8bf9ba0..eb4a63a24 100644 --- a/src/meshlab/mainwindow_Init.cpp +++ b/src/meshlab/mainwindow_Init.cpp @@ -46,13 +46,14 @@ QProgressBar *MainWindow::qb; -MainWindow::MainWindow(): - httpReq(this), - gpumeminfo(NULL), - defaultGlobalParams(meshlab::defaultGlobalParameterList()), - lastUsedDirectory(QDir::home()), - PM(meshlab::pluginManagerInstance()), - _currviewcontainer(NULL) +MainWindow::MainWindow() : + searcher(meshlab::actionSearcherInstance()), + httpReq(this), + gpumeminfo(NULL), + defaultGlobalParams(meshlab::defaultGlobalParameterList()), + lastUsedDirectory(QDir::home()), + PM(meshlab::pluginManagerInstance()), + _currviewcontainer(NULL) { QSettings settings; //toDelete plugins, flagged in the last session @@ -650,7 +651,7 @@ void MainWindow::createMenus() { initSearchEngine(); int longest = longestActionWidthInAllMenus(); - searchMenu = new SearchMenu(wama, 15, searchButton, longest); + searchMenu = new SearchMenu(searcher, 15, searchButton, longest); searchButton->setMenu(searchMenu); connect(searchShortCut, SIGNAL(activated()), searchButton, SLOT(openMenu())); } @@ -658,22 +659,22 @@ void MainWindow::createMenus() void MainWindow::initSearchEngine() { - wama.clear(); + searcher.clear(); for (const auto& p : PM.filterPluginIterator()){ for (QAction* act : p->actions()) - wama.addAction(act); + searcher.addAction(act); } for (const auto& p : PM.editPluginFactoryIterator()){ for (QAction* act : p->actions()) - wama.addAction(act); + searcher.addAction(act); } for (const auto& p : PM.renderPluginIterator()){ for (QAction* act : p->actions()) - wama.addAction(act); + searcher.addAction(act); } for (const auto& p : PM.decoratePluginIterator()){ for (QAction* act : p->actions()) - wama.addAction(act); + searcher.addAction(act); } }