Merge branch 'master' of github.com:PeC-KAYO/meshlab

This commit is contained in:
Dʳ Pierre-Édouard Cailliau 2022-01-24 23:23:27 +01:00
commit d139116d2a
No known key found for this signature in database
GPG Key ID: 0B3A4F36A195A1E8
24 changed files with 624 additions and 308 deletions

View File

@ -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)
@ -172,6 +173,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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,175 @@
/*****************************************************************************
* 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 <cmath>
#include <QRegExp>
#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) {
// 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);
}
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<FilterPlugin*>(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);
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<QAction *> ActionSearcher::bestMatchingActions(QString inputString, int maxNumberActions) const
{
std::vector<QAction *> 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<QAction*, float> 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<float, std::vector<QAction*>> 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<str.length()-3;++i)
{
strPref.chop(1);
resWithPrefix.push_back(strPref);
}
}
resWithPrefix.removeDuplicates();
res = resWithPrefix;
}

View File

@ -0,0 +1,62 @@
/*****************************************************************************
* 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. *
* *
****************************************************************************/
#ifndef ACTION_SEARCHER_H
#define ACTION_SEARCHER_H
#include <map>
#include <vector>
#include <QString>
#include <QAction>
class ActionSearcher
{
public:
ActionSearcher();
void clear();
void addAction(QAction* action, bool usePythonFilterNames = false);
std::vector<QAction*> 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<QString, std::vector<QAction*>> titleActionsMap;
// map that stores, for each stirng, all the actions that store that stirng in their info
std::map<QString, std::vector<QAction*>> infoActionsMap;
struct ActionComparator {
bool operator()(QAction* a1, QAction* a2) {
return a1->text() < a2->text();
}
};
static void addSubStrings(QStringList& res);
};
#endif // ACTION_SEARCHER_H

View File

@ -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<QAction*> 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);

View File

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

View File

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

View File

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

View File

@ -1,151 +0,0 @@
#include "searcher.h"
#include "mlexception.h"
#include <cmath>
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<QString,QList<QAction*> >::iterator it = wordacts.begin();it != wordacts.end();++it)
it.value().removeAll(&act);
}
bool WordActionsMap::getActionsPerWord( const QString& word,QList<QAction*>& res ) const
{
QMap< QString,QList<QAction*> >::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<str.length()-3;++i)
{
strPref.chop(1);
resWithPrefix.push_back(strPref);
}
}
resWithPrefix.removeDuplicates();
res = resWithPrefix;
}
RankedMatches::RankedMatches()
:ranking()
{
}
int RankedMatches::computeRankedMatches( const QStringList& inputst,const WordActionsMap& map, bool matchesontitlearemoreimportant)
{
QMap<QAction*, float> 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<QAction*> 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<float, QList<QAction*> > rankedmatches;
for (QMap<QAction*, float>::iterator it = wordmatchesperaction.begin(); it != wordmatchesperaction.end(); ++it)
rankedmatches[it.value()].push_back(it.key());
int maxindex = -1;
for(QMap<float,QList<QAction*> >::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<QAction*>& 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];
}

View File

@ -1,58 +0,0 @@
#ifndef SEARCHER_H
#define SEARCHER_H
#include<QString>
#include<QMap>
#include<QList>
#include<QAction>
#include<QRegExp>
#include<QVector>
#include<QSet>
class WordActionsMap
{
public:
WordActionsMap();
void addWordsPerAction(QAction& act,const QStringList& words);
void removeActionReferences(QAction& act);
bool getActionsPerWord( const QString& word,QList<QAction*>& res ) const;
void clear();
private:
QMap<QString,QList<QAction*> > 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<QAction*>& res);
private:
friend int WordActionsMapAccessor::rankedMatchesPerInputString(const QString& input,RankedMatches& rm) const;
int computeRankedMatches(const QStringList& inputst,const WordActionsMap& map,bool matchesontitlearemoreimportant = true);
QVector<QList<QAction*> > ranking;
};
#endif

View File

@ -31,9 +31,12 @@
typedef Eigen::Matrix<Scalarm, Eigen::Dynamic, 1> EigenVectorXm;
typedef Eigen::Matrix<bool, Eigen::Dynamic, 1> EigenVectorXb;
typedef Eigen::Matrix<unsigned int, Eigen::Dynamic, 1> EigenVectorXui;
typedef Eigen::Matrix<Scalarm, Eigen::Dynamic, 2> EigenMatrixX2m;
typedef Eigen::Matrix<Scalarm, Eigen::Dynamic, 3> EigenMatrixX3m;
typedef Eigen::Matrix<Scalarm, Eigen::Dynamic, 4> EigenMatrixX4m;
typedef Eigen::Matrix<Scalarm, Eigen::Dynamic, 2> EigenMatrixX2m;
typedef Eigen::Matrix<Scalarm, Eigen::Dynamic, 3> EigenMatrixX3m;
typedef Eigen::Matrix<Scalarm, Eigen::Dynamic, 4> EigenMatrixX4m;
typedef Eigen::Matrix<Scalarm, Eigen::Dynamic, Eigen::Dynamic> EigenMatrixXm;
namespace meshlab {

View File

@ -8,7 +8,7 @@
#include <QStyle>
#include <QDebug>
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<QAction*>& result)
{
try
{
RankedMatches rm;
int ii = wama.rankedMatchesPerInputString(text,rm);
int inserted = 0;
while(ii > 0)
{
QList<QAction*> 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<QAction*> 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<QAction*>& results )

View File

@ -20,7 +20,7 @@
#include <QPushButton>
#include <QLabel>
#include <QSlider>
#include "../common/searcher.h"
#include "../common/plugins/action_searcher.h"
#include <QToolTip>
#include <QSyntaxHighlighter>
#include <QProxyStyle>
@ -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;

View File

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

View File

@ -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();
@ -361,7 +359,7 @@ public:
QMenu* rasterLayerMenu() { return filterMenuRasterLayer; }
private:
WordActionsMapAccessor wama;
ActionSearcher& searcher;
//////// ToolBars ///////////////
QToolBar* mainToolBar;
QToolBar* decoratorToolBar;

View File

@ -36,7 +36,6 @@
#include <QWidgetAction>
#include <QMessageBox>
#include "mainwindow.h"
#include <common/searcher.h>
#include <common/mlapplication.h>
#include <common/mlexception.h>
#include <common/globals.h>
@ -47,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
@ -651,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()));
}
@ -659,41 +659,23 @@ void MainWindow::createMenus()
void MainWindow::initSearchEngine()
{
searcher.clear();
for (const auto& p : PM.filterPluginIterator()){
for (QAction* act : p->actions())
initItemForSearching(act);
searcher.addAction(act);
}
/*for (const auto& p : PM.editPluginFactoryIterator()){
for (const auto& p : PM.editPluginFactoryIterator()){
for (QAction* act : p->actions())
initItemForSearching(act);
searcher.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<QAction*>& acts = menu->actions();
for(QAction* act: acts) {
QMenu* submenu = act->menu();
if (!act->isSeparator() && (submenu == NULL))
initItemForSearching(act);
else if (!act->isSeparator())
initMenuForSearching(submenu);
searcher.addAction(act);
}
for (const auto& p : PM.decoratePluginIterator()){
for (QAction* act : p->actions())
searcher.addAction(act);
}
}
void MainWindow::initItemForSearching(QAction* act)
{
QString tx = act->text() + " " + act->toolTip();
wama.addWordsPerAction(*act, tx);
}
QString MainWindow::getDecoratedFileName(const QString& name)
@ -930,20 +912,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()

View File

@ -2576,6 +2576,7 @@ void MainWindow::aboutPlugins()
PluginInfoDialog dialog(this);
dialog.exec();
updateAllPluginsActions();
initSearchEngine();
QSettings settings;
QStringList disabledPlugins;
for (MeshLabPlugin* pf : PM.pluginIterator(true)){

View File

@ -0,0 +1,15 @@
# Copyright 2019, 2020, Visual Computing Lab, ISTI - Italian National Research Council
if (TARGET external-libigl AND TARGET Threads::Threads)
set(SOURCES filter_parametrization.cpp)
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()

View File

@ -0,0 +1,219 @@
/****************************************************************************
* 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"
#include <common/utilities/eigen_mesh_conversions.h>
#include <igl/boundary_loop.h>
#include <igl/harmonic.h>
#include <igl/lscm.h>
#include <igl/map_vertices_to_circle.h>
FilterParametrizationPlugin::FilterParametrizationPlugin()
{
typeList = { FP_HARMONIC_PARAM, FP_LEAST_SQUARES_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 "Harmonic Parametrization";
case FP_LEAST_SQUARES_PARAM:
return "Least Squares Conformal Maps Parametrization";
default :
assert(0);
return "";
}
}
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 =
"The resulting parametrization is saved in the per vertex texture coordinates.<br>"
"The filter uses the original code provided in the "
"<a href=\"https://libigl.github.io/\">libigl library</a>.<br>";
switch(filterId) {
case FP_HARMONIC_PARAM :
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 "Compuites a least squares conformal maps parametrization of a mesh. " +
commonDescription;
default :
assert(0);
return "Unknown Filter";
}
}
FilterParametrizationPlugin::FilterClass FilterParametrizationPlugin::getClass(const QAction *a) const
{
switch(ID(a)) {
case FP_HARMONIC_PARAM :
case FP_LEAST_SQUARES_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_VERTCOORD | MeshModel::MM_FACEVERT;
}
int FilterParametrizationPlugin::getRequirements(const QAction*)
{
return MeshModel::MM_VERTTEXCOORD;
}
int FilterParametrizationPlugin::postCondition(const QAction*) const
{
return MeshModel::MM_VERTTEXCOORD;
}
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);
}
return parlst;
}
std::map<std::string, QVariant> FilterParametrizationPlugin::applyFilter(
const QAction * action,
const RichParameterList & par,
MeshDocument &md,
unsigned int& /*postConditionMask*/,
vcg::CallBackPos *)
{
switch(ID(action)) {
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<double>();
Eigen::MatrixX3i faces = meshlab::faceMatrix(md.mm()->cm);
Eigen::MatrixXd V_uv, bnd_uv;
Eigen::VectorXi bnd;
igl::boundary_loop(faces,bnd);
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;
for (auto& v : md.mm()->cm.vert){
v.T().u() =V_uv(i, 0);
v.T().v() =V_uv(i, 1);
i++;
}
break;
}
case FP_LEAST_SQUARES_PARAM: {
EigenMatrixX3m v = meshlab::vertexMatrix(md.mm()->cm);
Eigen::MatrixX3d verts = v.cast<double>();
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);
}
return std::map<std::string, QVariant>();
}
MESHLAB_PLUGIN_NAME_EXPORTER(FilterSamplePlugin)

View File

@ -0,0 +1,66 @@
/****************************************************************************
* 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 <common/plugins/interfaces/filter_plugin.h>
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,
FP_LEAST_SQUARES_PARAM};
FilterParametrizationPlugin();
QString pluginName() const;
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;
int getPreConditions(const QAction *) const;
int getRequirements(const QAction *);
int postCondition(const QAction* ) const;
RichParameterList initParameterList(const QAction*, const MeshModel &/*m*/);
std::map<std::string, QVariant> applyFilter(
const QAction* action,
const RichParameterList & params,
MeshDocument &md,
unsigned int& postConditionMask,
vcg::CallBackPos * cb);
private:
};
#endif //MESHLAB_FILTER_PARAMETRIZATION_PLUGIN_H

View File

@ -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<std::string, QVariant> 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<std::string, QVariant> 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.

View File

@ -74,6 +74,7 @@ private:
bool vertexDisplacement(
MeshDocument &md,
vcg::CallBackPos *cb,
int randomSeed,
bool updateNormals,
Scalarm max_displacement);
};