diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0398e8566b..82494a57e0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -203,6 +203,7 @@ if(NOT DEFINED MESHLAB_PLUGINS) # it may be already defined in parent directory meshlabplugins/edit_referencing meshlabplugins/edit_quality meshlabplugins/edit_select + meshlabplugins/edit_cut ) endif() diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 0e640fdd4d..7f002ac5b4 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -161,6 +161,7 @@ set(SOURCES plugins/containers/render_plugin_container.cpp plugins/interfaces/meshlab_plugin_logger.cpp plugins/interfaces/decorate_plugin.cpp + plugins/interfaces/edit_plugin.cpp plugins/interfaces/filter_plugin.cpp plugins/interfaces/io_plugin.cpp plugins/action_searcher.cpp diff --git a/src/common/plugins/interfaces/edit_plugin.cpp b/src/common/plugins/interfaces/edit_plugin.cpp new file mode 100644 index 0000000000..59019a975c --- /dev/null +++ b/src/common/plugins/interfaces/edit_plugin.cpp @@ -0,0 +1,5 @@ +#include "edit_plugin.h" + +void EditPlugin::initGlobalParameterList(RichParameterList& globalparam) +{ +} \ No newline at end of file diff --git a/src/common/plugins/interfaces/edit_plugin.h b/src/common/plugins/interfaces/edit_plugin.h index af8d29f18e..f58cbf49f5 100644 --- a/src/common/plugins/interfaces/edit_plugin.h +++ b/src/common/plugins/interfaces/edit_plugin.h @@ -108,6 +108,8 @@ class EditPlugin : public MeshLabPlugin EditPlugin() {} virtual ~EditPlugin() {} + virtual void initGlobalParameterList(RichParameterList& defaultGlobalParamSet); + //gets a list of actions available from this plugin virtual std::list actions() const {return actionList;}; @@ -117,8 +119,14 @@ class EditPlugin : public MeshLabPlugin //get the description for the given action virtual QString getEditToolDescription(const QAction *) = 0; + void setCurrentGlobalParamSet(RichParameterList* cgp) + { + currentGlobalParamSet = cgp; + } + protected: std::list actionList; + RichParameterList* currentGlobalParamSet; }; #define EDIT_PLUGIN_IID "vcg.meshlab.EditPlugin/1.0" diff --git a/src/meshlab/mainwindow.h b/src/meshlab/mainwindow.h index 5656b2d29e..56d2f64540 100644 --- a/src/meshlab/mainwindow.h +++ b/src/meshlab/mainwindow.h @@ -113,6 +113,7 @@ class MainWindow : public QMainWindow static bool QCallBack(const int pos, const char * str); //const QString appName() const {return tr("MeshLab v")+appVer(); } //const QString appVer() const {return tr("1.3.2"); } + RichParameterList& getCurrentParameterList(); MainWindowSetting mwsettings; public slots: // callback function to execute a filter @@ -373,6 +374,7 @@ private slots: QToolBar* decoratorToolBar; QToolBar* editToolBar; QToolBar* filterToolBar; + QToolBar* dynartToolBar; QToolBar* searchToolBar; MLRenderingGlobalToolbar* globrendtoolbar; ///////// Menus /////////////// @@ -531,6 +533,9 @@ private slots: static QString getDecoratedFileName(const QString& name); MultiViewer_Container* _currviewcontainer; + +Q_SIGNALS: + void customSettingsChanged(const RichParameterList &rpl); }; /// Event filter that is installed to intercept the open events sent directly by the Operative System diff --git a/src/meshlab/mainwindow_Init.cpp b/src/meshlab/mainwindow_Init.cpp index 014a48bfd6..3bcd0f3fac 100644 --- a/src/meshlab/mainwindow_Init.cpp +++ b/src/meshlab/mainwindow_Init.cpp @@ -520,6 +520,7 @@ void MainWindow::createToolBars() editToolBar = addToolBar(tr("Edit")); editToolBar->addAction(suspendEditModeAct); for(EditPlugin *iEditFactory: PM.editPluginFactoryIterator()) { + if (iEditFactory->pluginName() == "EditCut") continue; for(QAction* editAction: iEditFactory->actions()){ if (!editAction->icon().isNull()) { editToolBar->addAction(editAction); @@ -534,6 +535,16 @@ void MainWindow::createToolBars() updateFilterToolBar(); + dynartToolBar = addToolBar(tr("Dynart Tools")); + for(EditPlugin *iEditFactory: PM.editPluginFactoryIterator()) { + if (iEditFactory->pluginName() != "EditCut") continue; + for(QAction* editAction: iEditFactory->actions()){ + if (!editAction->icon().isNull()) { + dynartToolBar->addAction(editAction); + } + } + } + QWidget *spacerWidget = new QWidget(); spacerWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); spacerWidget->setVisible(true); @@ -922,6 +933,12 @@ void MainWindow::loadDefaultSettingsFromPlugins() } } + //edit settings + for (EditPlugin* ep : PM.editPluginFactoryIterator()) { + ep->initGlobalParameterList(defaultGlobalParams); + ep->setCurrentGlobalParamSet(¤tGlobalParams); + } + //io settings for (IOPlugin* iop : PM.ioPluginIterator()){ for (const FileFormat& ff : iop->importFormats()) { diff --git a/src/meshlab/mainwindow_RunTime.cpp b/src/meshlab/mainwindow_RunTime.cpp index edd1bbcc81..2bf46132c4 100644 --- a/src/meshlab/mainwindow_RunTime.cpp +++ b/src/meshlab/mainwindow_RunTime.cpp @@ -104,6 +104,11 @@ void MainWindow::updateCustomSettings() { mwsettings.updateGlobalParameterList(currentGlobalParams); emit dispatchCustomSettings(currentGlobalParams); + +} +RichParameterList& MainWindow::getCurrentParameterList() +{ + return currentGlobalParams; } void MainWindow::updateWindowMenu() @@ -2196,6 +2201,15 @@ void MainWindow::reloadAllMesh() { // Discards changes and reloads current file // save current file name + QMessageBox::StandardButton reply; + reply = QMessageBox::question( + this, + tr("You are reloading all mesh!"), + tr("Are You sure to Reload?"), + QMessageBox::Yes | QMessageBox::No); + if (reply == QMessageBox::No) { + return; + } qb->show(); QElapsedTimer t; t.start(); @@ -2244,6 +2258,14 @@ void MainWindow::reload() return; // Discards changes and reloads current file // save current file name + QMessageBox::StandardButton reply; + reply = QMessageBox::question(this, + tr("You are reloading the current mesh"), + tr("Are you sure to reload?"), + QMessageBox::Yes | QMessageBox::No); + if (reply == QMessageBox::No) { + return; + } qb->show(); QString fileName = meshDoc()->mm()->fullName(); diff --git a/src/meshlabplugins/edit_align/edit_align_factory.h b/src/meshlabplugins/edit_align/edit_align_factory.h index 33ca58fe12..f5e24d35b0 100644 --- a/src/meshlabplugins/edit_align/edit_align_factory.h +++ b/src/meshlabplugins/edit_align/edit_align_factory.h @@ -38,6 +38,11 @@ class EditAlignFactory : public QObject, public EditPlugin EditAlignFactory(); virtual ~EditAlignFactory() { delete editAlign; } + void initGlobalParameterList(RichParameterList& /*paramList*/) + { + // No global parameters needed for this plugin + } + virtual QString pluginName() const; //get the edit tool for the given action diff --git a/src/meshlabplugins/edit_cut/CMakeLists.txt b/src/meshlabplugins/edit_cut/CMakeLists.txt new file mode 100644 index 0000000000..581dfd7713 --- /dev/null +++ b/src/meshlabplugins/edit_cut/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright 2019-2020, Collabora, Ltd. +# SPDX-License-Identifier: BSL-1.0 + +set(SOURCES edit_cut.cpp edit_cut_factory.cpp) + +set(HEADERS edit_cut.h edit_cut_factory.h) + +set(RESOURCES edit_cut.qrc) + +add_meshlab_plugin(edit_cut ${SOURCES} ${HEADERS} ${RESOURCES}) + +target_link_libraries(edit_cut PRIVATE OpenGL::GLU) diff --git a/src/meshlabplugins/edit_cut/edit_cut.cpp b/src/meshlabplugins/edit_cut/edit_cut.cpp new file mode 100644 index 0000000000..947110f58c --- /dev/null +++ b/src/meshlabplugins/edit_cut/edit_cut.cpp @@ -0,0 +1,538 @@ +#include "edit_cut.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace vcg; + +// ---- SplitTab from vcglib refine.h ---- +// Index is bitmask: bit0 = edge01 split, bit1 = edge12 split, bit2 = edge20 split +// Vertices: 0,1,2 = original; 3 = mid01, 4 = mid12, 5 = mid20 +struct SplitEntry { + int TriNum; + int TV[4][3]; +}; + +static const SplitEntry SplitTab[8] = { + /* 0 0 0 */ {1, {{0,1,2},{0,0,0},{0,0,0},{0,0,0}} }, + /* 0 0 1 */ {2, {{0,3,2},{3,1,2},{0,0,0},{0,0,0}} }, + /* 0 1 0 */ {2, {{0,1,4},{0,4,2},{0,0,0},{0,0,0}} }, + /* 0 1 1 */ {3, {{3,1,4},{0,3,2},{4,2,3},{0,0,0}} }, + /* 1 0 0 */ {2, {{0,1,5},{5,1,2},{0,0,0},{0,0,0}} }, + /* 1 0 1 */ {3, {{0,3,5},{3,1,5},{2,5,1},{0,0,0}} }, + /* 1 1 0 */ {3, {{2,5,4},{0,1,5},{4,5,1},{0,0,0}} }, + /* 1 1 1 */ {4, {{3,4,5},{0,3,5},{3,1,4},{5,4,2}} }, +}; + +// ---- 2D segment-segment intersection returning lambda along first segment ---- +static inline bool SegSegIntersect( + const Point2f &p0, const Point2f &p1, + const Point2f &p2, const Point2f &p3, + float &outLambda0) +{ + float a = (p1 - p0)[0]; + float b = (p2 - p3)[0]; + float c = (p1 - p0)[1]; + float d = (p2 - p3)[1]; + float e = (p2 - p0)[0]; + float f = (p2 - p0)[1]; + + float det = a * d - b * c; + if (fabs(det) < 1e-8f) + return false; + + float lambda0 = (d * e - b * f) / det; + float lambda1 = (-c * e + a * f) / det; + + if (lambda0 < 0.0f || lambda0 > 1.0f || lambda1 < 0.0f || lambda1 > 1.0f) + return false; + + outLambda0 = lambda0; + return true; +} + +// ======================================================================== + +EditCutPlugin::EditCutPlugin() +{ + memset(viewpSize, 0, sizeof(viewpSize)); + memset(mvMatrix_f, 0, sizeof(mvMatrix_f)); + memset(prMatrix_f, 0, sizeof(prMatrix_f)); + memset(SelViewport, 0, sizeof(SelViewport)); + SelMatrix.setIdentity(); +} + +const QString EditCutPlugin::info() +{ + return QString("Draw a closed polyline to cut through mesh triangles and delete the inside region."); +} + +void EditCutPlugin::suggestedRenderingData(MeshModel & /*m*/, MLRenderingData &dt) +{ + MLPerViewGLOptions opts; + dt.get(opts); + opts._sel_enabled = true; + opts._face_sel = true; + opts._vertex_sel = true; + dt.set(opts); +} + +bool EditCutPlugin::startEdit(MeshModel & /*m*/, GLArea *gla, MLSceneGLSharedDataContext * /*cont*/) +{ + if (gla == NULL) + return false; + if (!GLExtensionsManager::initializeGLextensions_notThrowing()) + return false; + + gla->setCursor(Qt::CrossCursor); + cutPolyLine.clear(); + return true; +} + +void EditCutPlugin::endEdit(MeshModel & /*m*/, GLArea * /*parent*/, MLSceneGLSharedDataContext * /*cont*/) +{ + cutPolyLine.clear(); +} + +// ---- Mouse Events ---- + +void EditCutPlugin::mousePressEvent(QMouseEvent *event, MeshModel &m, GLArea *gla) +{ + cutPolyLine.push_back(QTLogicalToOpenGL(gla, event->pos())); + gla->update(); +} + +void EditCutPlugin::mouseMoveEvent(QMouseEvent *event, MeshModel & /*m*/, GLArea *gla) +{ + if (!cutPolyLine.empty()) + cutPolyLine.back() = QTLogicalToOpenGL(gla, event->pos()); + gla->update(); +} + +void EditCutPlugin::mouseReleaseEvent(QMouseEvent *event, MeshModel & /*m*/, GLArea *gla) +{ + if (!cutPolyLine.empty()) + cutPolyLine.back() = QTLogicalToOpenGL(gla, event->pos()); +} + +void EditCutPlugin::keyPressEvent(QKeyEvent *, MeshModel &, GLArea *) +{ +} + +void EditCutPlugin::keyReleaseEvent(QKeyEvent *e, MeshModel &m, GLArea *gla) +{ + switch (e->key()) { + case Qt::Key_C: // Clear polyline + cutPolyLine.clear(); + gla->update(); + e->accept(); + break; + + case Qt::Key_Backspace: // Remove last polyline point + if (!cutPolyLine.empty()) { + cutPolyLine.pop_back(); + gla->update(); + } + e->accept(); + break; + + case Qt::Key_Q: // Cut + add to selection (like edit_select Q) + if (cutPolyLine.size() >= 3) + executeCut(m, gla); + e->accept(); + break; + + case Qt::Key_W: // Subtract from selection (like edit_select W) + if (cutPolyLine.size() >= 3) { + vector projVec; + GLPickTri::FillProjectedVector(m.cm, projVec, this->SelMatrix, this->SelViewport); + + QImage bufQImg(viewpSize[2], viewpSize[3], QImage::Format_RGB32); + bufQImg.fill(Qt::white); + QPainter bufQPainter(&bufQImg); + vector qpoints; + for (size_t i = 0; i < cutPolyLine.size(); ++i) + qpoints.push_back(QPointF(cutPolyLine[i][0], cutPolyLine[i][1])); + bufQPainter.setBrush(QBrush(Qt::black)); + bufQPainter.drawPolygon(&qpoints[0], (int)qpoints.size(), Qt::WindingFill); + bufQPainter.end(); + QRgb blk = QColor(Qt::black).rgb(); + + for (size_t fi = 0; fi < m.cm.face.size(); ++fi) { + if (m.cm.face[fi].IsD() || !m.cm.face[fi].IsS()) continue; + int vi0 = tri::Index(m.cm, m.cm.face[fi].V(0)); + int vi1 = tri::Index(m.cm, m.cm.face[fi].V(1)); + int vi2 = tri::Index(m.cm, m.cm.face[fi].V(2)); + if (vi0 < 0 || vi0 >= (int)projVec.size() || + vi1 < 0 || vi1 >= (int)projVec.size() || + vi2 < 0 || vi2 >= (int)projVec.size()) continue; + if (projVec[vi0][2] <= -1.0 || projVec[vi0][2] >= 1.0) continue; + if (projVec[vi1][2] <= -1.0 || projVec[vi1][2] >= 1.0) continue; + if (projVec[vi2][2] <= -1.0 || projVec[vi2][2] >= 1.0) continue; + float cx = (float)(projVec[vi0][0] + projVec[vi1][0] + projVec[vi2][0]) / 3.0f; + float cy = (float)(projVec[vi0][1] + projVec[vi1][1] + projVec[vi2][1]) / 3.0f; + int px = (int)cx; + int py = (int)cy; + if (px <= 0 || px >= viewpSize[2] || py <= 0 || py >= viewpSize[3]) continue; + if (bufQImg.pixel(px, py) == blk) + m.cm.face[fi].ClearS(); + } + gla->updateSelection(m.id(), false, true); + gla->update(); + } + e->accept(); + break; + + case Qt::Key_D: // Deselect all + tri::UpdateSelection::FaceClear(m.cm); + gla->updateSelection(m.id(), false, true); + gla->update(); + e->accept(); + break; + + case Qt::Key_A: // Select all faces + tri::UpdateSelection::FaceAll(m.cm); + gla->updateSelection(m.id(), false, true); + gla->update(); + e->accept(); + break; + + case Qt::Key_I: // Invert selection + tri::UpdateSelection::FaceInvert(m.cm); + gla->updateSelection(m.id(), false, true); + gla->update(); + e->accept(); + break; + + case Qt::Key_Escape: // Clear polyline only (D=deselect, Delete=delete selected) + cutPolyLine.clear(); + gla->update(); + e->accept(); + break; + + default: + break; + } +} + +// ---- Decorate ---- + +void EditCutPlugin::decorate(MeshModel &m, GLArea *gla) +{ + glPushMatrix(); + glMultMatrix(m.cm.Tr); + GLPickTri::glGetMatrixAndViewport(this->SelMatrix, this->SelViewport); + glGetDoublev(GL_MODELVIEW_MATRIX, mvMatrix_f); + glGetDoublev(GL_PROJECTION_MATRIX, prMatrix_f); + glGetIntegerv(GL_VIEWPORT, viewpSize); + glPopMatrix(); + + DrawXORPolyLine(gla); + + QString line2; + if (cutPolyLine.size() < 3) + line2 = "Need at least 3 points"; + else + line2 = "Q=cut and select, W=subtract"; + + this->realTimeLog("Lasso Cut", m.shortName(), + "Click to add points — C=clear, BACKSPACE=undo, ESC=cancel
" + "%s
" + "D=deselect all, A=select all, I=invert — DELETE=delete selected", + line2.toStdString().c_str()); +} + +// ---- DrawXORPolyLine ---- + +void EditCutPlugin::DrawXORPolyLine(GLArea *gla) +{ + if (cutPolyLine.empty()) + return; + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0, QTDeviceWidth(gla), 0, QTDeviceHeight(gla), -1, 1); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + glDisable(GL_TEXTURE_2D); + glEnable(GL_COLOR_LOGIC_OP); + glLogicOp(GL_XOR); + glColor3f(1, 1, 1); + glLineStipple(1, 0xAAAA); + glEnable(GL_LINE_STIPPLE); + glLineWidth(QTLogicalToDevice(gla, 1)); + + if (cutPolyLine.size() == 1) { + glBegin(GL_POINTS); + glVertex(cutPolyLine[0]); + } else if (cutPolyLine.size() == 2) { + glBegin(GL_LINES); + glVertex(cutPolyLine[0]); + glVertex(cutPolyLine[1]); + } else { + glBegin(GL_LINE_LOOP); + for (size_t ii = 0; ii < cutPolyLine.size(); ii++) + glVertex(cutPolyLine[ii]); + } + glEnd(); + + glDisable(GL_LOGIC_OP); + glPopAttrib(); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); +} + +// ======================================================================== +// Cut edges along polyline boundary, then select faces inside +// ======================================================================== + +void EditCutPlugin::executeCut(MeshModel &m, GLArea *gla) +{ + if (viewpSize[2] <= 0 || viewpSize[3] <= 0) + return; + if (cutPolyLine.size() < 3) + return; + + try { + + int numPolySeg = (int)cutPolyLine.size(); + + // ---- Step 1: Project all vertices to screen space ---- + vector projVec; + GLPickTri::FillProjectedVector(m.cm, projVec, this->SelMatrix, this->SelViewport); + + // ---- Step 2: Find which edges are crossed by the polyline ---- + typedef pair EdgeKey; + map> edgeSplitMap; + + int faceCount = (int)m.cm.face.size(); + int vertCount = (int)m.cm.vert.size(); + + for (size_t fi = 0; fi < (size_t)faceCount; ++fi) + { + if (m.cm.face[fi].IsD()) continue; + + for (int j = 0; j < 3; ++j) + { + int vi0 = tri::Index(m.cm, m.cm.face[fi].V(j)); + int vi1 = tri::Index(m.cm, m.cm.face[fi].V((j + 1) % 3)); + + if (vi0 < 0 || vi0 >= vertCount || vi1 < 0 || vi1 >= vertCount) + continue; + + EdgeKey ek(min(vi0, vi1), max(vi0, vi1)); + if (edgeSplitMap.count(ek)) continue; + + if (projVec[vi0][2] <= -1.0 || projVec[vi0][2] >= 1.0) continue; + if (projVec[vi1][2] <= -1.0 || projVec[vi1][2] >= 1.0) continue; + + Point2f a((float)projVec[vi0][0], (float)projVec[vi0][1]); + Point2f b((float)projVec[vi1][0], (float)projVec[vi1][1]); + + for (int k = 0; k < numPolySeg; ++k) + { + Point2f c = cutPolyLine[k]; + Point2f d = cutPolyLine[(k + 1) % numPolySeg]; + + float lambda0; + if (SegSegIntersect(a, b, c, d, lambda0)) + { + if (lambda0 < 0.005f || lambda0 > 0.995f) continue; + + float t = (vi0 < vi1) ? lambda0 : (1.0f - lambda0); + + Point3m p0 = m.cm.vert[ek.first].P(); + Point3m p1 = m.cm.vert[ek.second].P(); + Point3m pos = p0 * (Scalarm)(1.0 - t) + p1 * (Scalarm)t; + edgeSplitMap[ek] = make_pair(t, pos); + break; + } + } + } + } + + // ---- Step 3: Create new split vertices ---- + if (!edgeSplitMap.empty()) + { + map edgeToNewVert; + + int numNew = (int)edgeSplitMap.size(); + int firstNewIdx = (int)m.cm.vert.size(); + tri::Allocator::AddVertices(m.cm, numNew); + + int idx = firstNewIdx; + for (auto &entry : edgeSplitMap) + { + m.cm.vert[idx].P() = entry.second.second; + + float t = entry.second.first; + Point3m n0 = m.cm.vert[entry.first.first].N(); + Point3m n1 = m.cm.vert[entry.first.second].N(); + Point3m nn = n0 * (Scalarm)(1.0f - t) + n1 * (Scalarm)t; + Scalarm len = nn.Norm(); + if (len > 0) nn /= len; + m.cm.vert[idx].N() = nn; + + edgeToNewVert[entry.first] = idx; + idx++; + } + + // ---- Step 4: Split faces ---- + size_t origFaceCount = m.cm.face.size(); + + int totalNewFaces = 0; + for (size_t fi = 0; fi < origFaceCount; ++fi) + { + if (m.cm.face[fi].IsD()) continue; + + int mask = 0; + for (int j = 0; j < 3; ++j) + { + int a = tri::Index(m.cm, m.cm.face[fi].V(j)); + int b = tri::Index(m.cm, m.cm.face[fi].V((j + 1) % 3)); + EdgeKey ek(min(a, b), max(a, b)); + if (edgeToNewVert.count(ek)) mask |= (1 << j); + } + if (mask > 0) + totalNewFaces += SplitTab[mask].TriNum - 1; + } + + if (totalNewFaces > 0) + { + int firstNewFace = (int)m.cm.face.size(); + tri::Allocator::AddFaces(m.cm, totalNewFaces); + int nextNewFace = firstNewFace; + + for (size_t fi = 0; fi < origFaceCount; ++fi) + { + if (m.cm.face[fi].IsD()) continue; + + int vIdx[3]; + vIdx[0] = tri::Index(m.cm, m.cm.face[fi].V(0)); + vIdx[1] = tri::Index(m.cm, m.cm.face[fi].V(1)); + vIdx[2] = tri::Index(m.cm, m.cm.face[fi].V(2)); + + int vv[6]; + vv[0] = vIdx[0]; + vv[1] = vIdx[1]; + vv[2] = vIdx[2]; + vv[3] = vv[4] = vv[5] = -1; + + int mask = 0; + for (int j = 0; j < 3; ++j) + { + int a = vIdx[j]; + int b = vIdx[(j + 1) % 3]; + EdgeKey ek(min(a, b), max(a, b)); + auto it = edgeToNewVert.find(ek); + if (it != edgeToNewVert.end()) + { + vv[3 + j] = it->second; + mask |= (1 << j); + } + } + + if (mask == 0) continue; + + const SplitEntry &se = SplitTab[mask]; + + bool valid = true; + for (int i = 0; i < se.TriNum && valid; ++i) + for (int c = 0; c < 3; ++c) + if (vv[se.TV[i][c]] < 0 || vv[se.TV[i][c]] >= (int)m.cm.vert.size()) + valid = false; + if (!valid) continue; + + for (int i = 0; i < se.TriNum; ++i) + { + CMeshO::FacePointer fp; + if (i == 0) + fp = &m.cm.face[fi]; + else { + fp = &m.cm.face[nextNewFace]; + nextNewFace++; + } + + fp->V(0) = &m.cm.vert[vv[se.TV[i][0]]]; + fp->V(1) = &m.cm.vert[vv[se.TV[i][1]]]; + fp->V(2) = &m.cm.vert[vv[se.TV[i][2]]]; + } + } + } + } + + // ---- Step 5: Select faces inside the polyline ---- + selectInsideFaces(m, gla); + + } catch (...) { + cutPolyLine.clear(); + } +} + +// ======================================================================== +// Select faces whose centroid is inside the polyline +// ======================================================================== + +void EditCutPlugin::selectInsideFaces(MeshModel &m, GLArea *gla) +{ + // Re-project all vertices (including newly created split vertices) + vector projVec; + GLPickTri::FillProjectedVector(m.cm, projVec, this->SelMatrix, this->SelViewport); + + // Rasterize polyline as filled polygon (same as edit_select) + QImage bufQImg(viewpSize[2], viewpSize[3], QImage::Format_RGB32); + bufQImg.fill(Qt::white); + QPainter bufQPainter(&bufQImg); + vector qpoints; + for (size_t i = 0; i < cutPolyLine.size(); ++i) + qpoints.push_back(QPointF(cutPolyLine[i][0], cutPolyLine[i][1])); + bufQPainter.setBrush(QBrush(Qt::black)); + bufQPainter.drawPolygon(&qpoints[0], (int)qpoints.size(), Qt::WindingFill); + bufQPainter.end(); + QRgb blk = QColor(Qt::black).rgb(); + + // Use face centroid for inside test — gives clean cut along the polyline + for (size_t fi = 0; fi < m.cm.face.size(); ++fi) + { + if (m.cm.face[fi].IsD()) continue; + + int v0 = tri::Index(m.cm, m.cm.face[fi].V(0)); + int v1 = tri::Index(m.cm, m.cm.face[fi].V(1)); + int v2 = tri::Index(m.cm, m.cm.face[fi].V(2)); + if (v0 < 0 || v0 >= (int)projVec.size() || + v1 < 0 || v1 >= (int)projVec.size() || + v2 < 0 || v2 >= (int)projVec.size()) continue; + + if (projVec[v0][2] <= -1.0 || projVec[v0][2] >= 1.0) continue; + if (projVec[v1][2] <= -1.0 || projVec[v1][2] >= 1.0) continue; + if (projVec[v2][2] <= -1.0 || projVec[v2][2] >= 1.0) continue; + + float cx = (float)(projVec[v0][0] + projVec[v1][0] + projVec[v2][0]) / 3.0f; + float cy = (float)(projVec[v0][1] + projVec[v1][1] + projVec[v2][1]) / 3.0f; + + int px = (int)cx; + int py = (int)cy; + if (px <= 0 || px >= viewpSize[2] || py <= 0 || py >= viewpSize[3]) continue; + + if (bufQImg.pixel(px, py) == blk) + m.cm.face[fi].SetS(); + } + + gla->updateSelection(m.id(), false, true); + gla->update(); +} diff --git a/src/meshlabplugins/edit_cut/edit_cut.h b/src/meshlabplugins/edit_cut/edit_cut.h new file mode 100644 index 0000000000..2a9e1d2955 --- /dev/null +++ b/src/meshlabplugins/edit_cut/edit_cut.h @@ -0,0 +1,47 @@ +#ifndef EDITCUTPLUGIN_H +#define EDITCUTPLUGIN_H + +#include +#include +#include + +class EditCutPlugin : public QObject, public EditTool +{ + Q_OBJECT + +public: + EditCutPlugin(); + virtual ~EditCutPlugin() {} + + static const QString info(); + + void suggestedRenderingData(MeshModel &m, MLRenderingData &dt); + bool startEdit(MeshModel &m, GLArea *parent, MLSceneGLSharedDataContext *cont); + void endEdit(MeshModel &m, GLArea *parent, MLSceneGLSharedDataContext *cont); + void decorate(MeshModel &m, GLArea *parent); + void mousePressEvent(QMouseEvent *event, MeshModel &m, GLArea *gla); + void mouseMoveEvent(QMouseEvent *event, MeshModel &m, GLArea *gla); + void mouseReleaseEvent(QMouseEvent *event, MeshModel &m, GLArea *gla); + void keyReleaseEvent(QKeyEvent *e, MeshModel &m, GLArea *gla); + void keyPressEvent(QKeyEvent *e, MeshModel &m, GLArea *gla); + +private: + // Polyline state + std::vector cutPolyLine; + + // Projection data + GLdouble mvMatrix_f[16]; + GLdouble prMatrix_f[16]; + GLint viewpSize[4]; + Eigen::Matrix SelMatrix; + Scalarm SelViewport[4]; + + // Drawing helpers + void DrawXORPolyLine(GLArea *gla); + + // Core algorithm + void executeCut(MeshModel &m, GLArea *gla); + void selectInsideFaces(MeshModel &m, GLArea *gla); +}; + +#endif diff --git a/src/meshlabplugins/edit_cut/edit_cut.qrc b/src/meshlabplugins/edit_cut/edit_cut.qrc new file mode 100644 index 0000000000..7808bf43ba --- /dev/null +++ b/src/meshlabplugins/edit_cut/edit_cut.qrc @@ -0,0 +1,5 @@ + + + images/icon_cut.png + + diff --git a/src/meshlabplugins/edit_cut/edit_cut_factory.cpp b/src/meshlabplugins/edit_cut/edit_cut_factory.cpp new file mode 100644 index 0000000000..6f84147f58 --- /dev/null +++ b/src/meshlabplugins/edit_cut/edit_cut_factory.cpp @@ -0,0 +1,31 @@ +#include "edit_cut_factory.h" +#include "edit_cut.h" + +EditCutFactory::EditCutFactory() +{ + editCut = new QAction(QIcon(":/images/icon_cut.png"), "Lasso Cut Tool", this); + actionList.push_back(editCut); + + foreach(QAction *editAction, actionList) + editAction->setCheckable(true); +} + +QString EditCutFactory::pluginName() const +{ + return "EditCut"; +} + +EditTool* EditCutFactory::getEditTool(const QAction *action) +{ + if (action == editCut) + return new EditCutPlugin(); + assert(0); + return nullptr; +} + +QString EditCutFactory::getEditToolDescription(const QAction *) +{ + return EditCutPlugin::info(); +} + +MESHLAB_PLUGIN_NAME_EXPORTER(EditCutFactory) diff --git a/src/meshlabplugins/edit_cut/edit_cut_factory.h b/src/meshlabplugins/edit_cut/edit_cut_factory.h new file mode 100644 index 0000000000..187334d2fe --- /dev/null +++ b/src/meshlabplugins/edit_cut/edit_cut_factory.h @@ -0,0 +1,26 @@ +#ifndef EDITCUTFACTORYPLUGIN_H +#define EDITCUTFACTORYPLUGIN_H + +#include + +class EditCutFactory : public QObject, public EditPlugin +{ + Q_OBJECT + MESHLAB_PLUGIN_IID_EXPORTER(EDIT_PLUGIN_IID) + Q_INTERFACES(EditPlugin) + +public: + EditCutFactory(); + virtual ~EditCutFactory() { delete editCut; } + + virtual void initGlobalParameterList(RichParameterList& /*defaultGlobalParamSet*/) {} + + virtual QString pluginName() const; + virtual EditTool* getEditTool(const QAction*); + virtual QString getEditToolDescription(const QAction*); + +private: + QAction *editCut; +}; + +#endif diff --git a/src/meshlabplugins/edit_cut/images/icon_cut.png b/src/meshlabplugins/edit_cut/images/icon_cut.png new file mode 100644 index 0000000000..be598b0ce6 Binary files /dev/null and b/src/meshlabplugins/edit_cut/images/icon_cut.png differ diff --git a/src/meshlabplugins/edit_manipulators/edit_manipulators_factory.h b/src/meshlabplugins/edit_manipulators/edit_manipulators_factory.h index b19ef5ed9d..0aebb74cee 100644 --- a/src/meshlabplugins/edit_manipulators/edit_manipulators_factory.h +++ b/src/meshlabplugins/edit_manipulators/edit_manipulators_factory.h @@ -38,6 +38,11 @@ class EditManipulatorsFactory : public QObject, public EditPlugin EditManipulatorsFactory(); virtual ~EditManipulatorsFactory() { delete editManipulators; } + void initGlobalParameterList(RichParameterList& /*paramList*/) + { + // No global parameters needed for this plugin + } + virtual QString pluginName() const; //get the edit tool for the given action diff --git a/src/meshlabplugins/edit_measure/edit_measure_factory.h b/src/meshlabplugins/edit_measure/edit_measure_factory.h index f4d63b4d7c..6f1c4aba11 100644 --- a/src/meshlabplugins/edit_measure/edit_measure_factory.h +++ b/src/meshlabplugins/edit_measure/edit_measure_factory.h @@ -38,6 +38,11 @@ class EditMeasureFactory : public QObject, public EditPlugin EditMeasureFactory(); virtual ~EditMeasureFactory() { delete editMeasure; } + void initGlobalParameterList(RichParameterList& /*paramList*/) + { + // No global parameters needed for this plugin + } + virtual QString pluginName() const; //get the edit tool for the given action diff --git a/src/meshlabplugins/edit_mutualcorrs/edit_mutualcorrs_factory.h b/src/meshlabplugins/edit_mutualcorrs/edit_mutualcorrs_factory.h index 010d34f178..bad04a63d8 100644 --- a/src/meshlabplugins/edit_mutualcorrs/edit_mutualcorrs_factory.h +++ b/src/meshlabplugins/edit_mutualcorrs/edit_mutualcorrs_factory.h @@ -37,6 +37,11 @@ class EditMutualCorrsFactory : public QObject, public EditPlugin EditMutualCorrsFactory(); virtual ~EditMutualCorrsFactory() { delete editMutualCorrs; } + void initGlobalParameterList(RichParameterList& /*paramList*/) + { + // No global parameters needed for this plugin + } + virtual QString pluginName() const; //get the edit tool for the given action diff --git a/src/meshlabplugins/edit_paint/edit_paint_factory.h b/src/meshlabplugins/edit_paint/edit_paint_factory.h index fe02975b42..97a3010fcc 100644 --- a/src/meshlabplugins/edit_paint/edit_paint_factory.h +++ b/src/meshlabplugins/edit_paint/edit_paint_factory.h @@ -38,6 +38,11 @@ class EditPaintFactory : public QObject, public EditPlugin EditPaintFactory(); virtual ~EditPaintFactory() { delete editPaint; } + void initGlobalParameterList(RichParameterList& /*paramList*/) + { + // No global parameters needed for this plugin + } + virtual QString pluginName() const; //get the edit tool for the given action diff --git a/src/meshlabplugins/edit_pickpoints/edit_pickpoints_factory.h b/src/meshlabplugins/edit_pickpoints/edit_pickpoints_factory.h index 44b62838f4..4d71948bf3 100644 --- a/src/meshlabplugins/edit_pickpoints/edit_pickpoints_factory.h +++ b/src/meshlabplugins/edit_pickpoints/edit_pickpoints_factory.h @@ -38,6 +38,11 @@ class EditPickPointsFactory : public QObject, public EditPlugin EditPickPointsFactory(); virtual ~EditPickPointsFactory() { delete editPickPoints; } + void initGlobalParameterList(RichParameterList& /*paramList*/) + { + // No global parameters needed for this plugin + } + virtual QString pluginName() const; //get the edit tool for the given action diff --git a/src/meshlabplugins/edit_point/edit_point_factory.h b/src/meshlabplugins/edit_point/edit_point_factory.h index 632901be01..f88cd41fd3 100644 --- a/src/meshlabplugins/edit_point/edit_point_factory.h +++ b/src/meshlabplugins/edit_point/edit_point_factory.h @@ -38,6 +38,11 @@ class PointEditFactory : public QObject, public EditPlugin PointEditFactory(); virtual ~PointEditFactory() { delete editPoint; } + void initGlobalParameterList(RichParameterList& /*paramList*/) + { + // No global parameters needed for this plugin + } + virtual QString pluginName() const; //get the edit tool for the given action diff --git a/src/meshlabplugins/edit_quality/edit_quality_factory.h b/src/meshlabplugins/edit_quality/edit_quality_factory.h index 2d8ae56a7d..86518cd3ba 100644 --- a/src/meshlabplugins/edit_quality/edit_quality_factory.h +++ b/src/meshlabplugins/edit_quality/edit_quality_factory.h @@ -38,6 +38,11 @@ class QualityMapperFactory : public QObject, public EditPlugin QualityMapperFactory(); virtual ~QualityMapperFactory() { delete editQuality; } + void initGlobalParameterList(RichParameterList& /*paramList*/) + { + // No global parameters needed for this plugin + } + virtual QString pluginName() const; //get the edit tool for the given action diff --git a/src/meshlabplugins/edit_referencing/edit_referencing_factory.h b/src/meshlabplugins/edit_referencing/edit_referencing_factory.h index f2187724eb..bea6799e33 100644 --- a/src/meshlabplugins/edit_referencing/edit_referencing_factory.h +++ b/src/meshlabplugins/edit_referencing/edit_referencing_factory.h @@ -37,6 +37,11 @@ class EditReferencingFactory : public QObject, public EditPlugin EditReferencingFactory(); virtual ~EditReferencingFactory() { delete editReferencing; } + void initGlobalParameterList(RichParameterList& /*paramList*/) + { + // No global parameters needed for this plugin + } + virtual QString pluginName() const; //get the edit tool for the given action diff --git a/src/meshlabplugins/edit_sample/edit_sample_factory.h b/src/meshlabplugins/edit_sample/edit_sample_factory.h index ed14fc598e..8d6a9bbfce 100644 --- a/src/meshlabplugins/edit_sample/edit_sample_factory.h +++ b/src/meshlabplugins/edit_sample/edit_sample_factory.h @@ -38,6 +38,11 @@ class SampleEditFactory : public QObject, public EditPlugin SampleEditFactory(); virtual ~SampleEditFactory() { delete editSample; } + void initGlobalParameterList(RichParameterList& /*paramList*/) + { + // No global parameters needed for this plugin + } + //returns the name of the plugin virtual QString pluginName() const; diff --git a/src/meshlabplugins/edit_select/edit_select.cpp b/src/meshlabplugins/edit_select/edit_select.cpp index 2c4ad3d1aa..07734b270c 100644 --- a/src/meshlabplugins/edit_select/edit_select.cpp +++ b/src/meshlabplugins/edit_select/edit_select.cpp @@ -22,7 +22,12 @@ ****************************************************************************/ #include "edit_select.h" +#include + +#include +#include #include +#include #include #include #include @@ -33,8 +38,10 @@ using namespace std; using namespace vcg; -EditSelectPlugin::EditSelectPlugin(int ConnectedMode) :selectionMode(ConnectedMode) { +EditSelectPlugin::EditSelectPlugin(RichParameterList* cgp, int ConnectedMode) :selectionMode(ConnectedMode) { isDragging = false; + currentGlobalParamSet = cgp; + qApp->installEventFilter(this); } QString EditSelectPlugin::info() @@ -62,131 +69,192 @@ void EditSelectPlugin::suggestedRenderingData(MeshModel & /*m*/, MLRenderingData dt.set(opts); } +bool EditSelectPlugin::keyReleaseEventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::KeyRelease && QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + if (m_ref && gla_ref) { + // Check if the released key is the Alt key + if (keyEvent->key() == Qt::Key_Alt) { + keyReleaseEvent(keyEvent, *m_ref, gla_ref); + } + } + } + // Pass the event to the base class event filter + return QObject::eventFilter(obj, event); +} void EditSelectPlugin::keyReleaseEvent(QKeyEvent *e, MeshModel &m, GLArea *gla) { + bool ctrlState = currentGlobalParamSet->getBool("MeshLab::Editors::InvertCTRLBehavior"); + // global "all" commands - if (e->key() == Qt::Key_A) // select all - { - if (areaMode == 0){ // vertices - tri::UpdateSelection::VertexAll(m.cm); - gla->updateSelection(m.id(), true, false); - } - else if (areaMode == 1){ //faces - tri::UpdateSelection::FaceAll(m.cm); - gla->updateSelection(m.id(), false, true); - } - gla->update(); - e->accept(); - } + switch (e->key()) { + case Qt::Key_A: // select all + if (areaMode == 0) { // vertices + tri::UpdateSelection::VertexAll(m.cm); + gla->updateSelection(m.id(), true, false); + } else if (areaMode == 1) { // faces + tri::UpdateSelection::FaceAll(m.cm); + gla->updateSelection(m.id(), false, true); + } + gla->update(); + e->accept(); + break; + + case Qt::Key_D: // deselect all + if (areaMode == 0) { // vertices + tri::UpdateSelection::VertexClear(m.cm); + gla->updateSelection(m.id(), true, false); + } else if (areaMode == 1) { // faces + tri::UpdateSelection::FaceClear(m.cm); + gla->updateSelection(m.id(), false, true); + } + gla->update(); + e->accept(); + break; + + case Qt::Key_I: // invert all + if (areaMode == 0) { // vertices + tri::UpdateSelection::VertexInvert(m.cm); + gla->updateSelection(m.id(), true, false); + } else if (areaMode == 1) { // faces + tri::UpdateSelection::FaceInvert(m.cm); + gla->updateSelection(m.id(), false, true); + } + gla->update(); + e->accept(); + break; - if (e->key() == Qt::Key_D) // deselect all - { - if (areaMode == 0){ // vertices - tri::UpdateSelection::VertexClear(m.cm); - gla->updateSelection(m.id(), true, false); - } - else if (areaMode == 1){ //faces - tri::UpdateSelection::FaceClear(m.cm); - gla->updateSelection(m.id(), false, true); - } - gla->update(); - e->accept(); + default: + break; } - if (e->key() == Qt::Key_I) // invert all - { - if (areaMode == 0){ // vertices - tri::UpdateSelection::VertexInvert(m.cm); - gla->updateSelection(m.id(), true, false); - } - else if (areaMode == 1){ //faces - tri::UpdateSelection::FaceInvert(m.cm); - gla->updateSelection(m.id(), false, true); - } - gla->update(); - e->accept(); - } + if (selectionMode == SELECT_AREA_MODE) { + switch (e->key()) { + case Qt::Key_T: // toggle pick mode + areaMode = (areaMode + 1) % 2; + gla->update(); + e->accept(); + break; + case Qt::Key_C: // clear Polyline + selPolyLine.clear(); + gla->update(); + e->accept(); + break; - if (selectionMode == SELECT_AREA_MODE) - { - if (e->key() == Qt::Key_T) // toggle pick mode - { - areaMode = (areaMode + 1) % 2; - gla->update(); - e->accept(); - } + case Qt::Key_Backspace: // remove last point Polyline + if (selPolyLine.size() > 0) + selPolyLine.pop_back(); + gla->update(); + e->accept(); + break; - if (e->key() == Qt::Key_C) // clear Polyline - { - selPolyLine.clear(); - gla->update(); - e->accept(); - } + case Qt::Key_Q: // add to selection + doSelection(m, gla, 0); + gla->update(); + e->accept(); + break; - if (e->key() == Qt::Key_Backspace) // remove last point Polyline - { - if (selPolyLine.size() > 0) - selPolyLine.pop_back(); - gla->update(); - e->accept(); - } + case Qt::Key_W: // sub from selection + doSelection(m, gla, 1); + gla->update(); + e->accept(); + break; - if (e->key() == Qt::Key_Q) // add to selection - { - doSelection(m, gla, 0); - gla->update(); - e->accept(); - } + case Qt::Key_E: // invert selection + doSelection(m, gla, 2); + gla->update(); + e->accept(); + break; - if (e->key() == Qt::Key_W) // sub from selection + default: + break; + } + gla->setCursor(QCursor(QPixmap(":/images/sel_area.png"), 1, 1)); + } else { + if (ctrlState){ + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_plus.png"), 1, 1)); + } else { + gla->setCursor(QCursor(QPixmap(":/images/sel_rect.png"), 1, 1)); + } + Qt::KeyboardModifiers mod = e->modifiers(); + if (e->key() == Qt::Key_Alt) { - doSelection(m, gla, 1); - gla->update(); - e->accept(); + if (ctrlState){ + if (mod & Qt::ControlModifier){ + gla->setCursor(QCursor(QPixmap(":/images/sel_rect.png"), 1, 1)); + } else if (mod & Qt::ShiftModifier){ + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_minus.png"), 1, 1)); + } else{ + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_plus.png"), 1, 1)); + } + } else{ + if (mod & Qt::ControlModifier){ + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_plus.png"), 1, 1)); + } else if (mod & Qt::ShiftModifier){ + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_minus.png"), 1, 1)); + } else{ + gla->setCursor(QCursor(QPixmap(":/images/sel_rect.png"), 1, 1)); + } + } + e->accept(); } - if (e->key() == Qt::Key_E) // invert selection - { - doSelection(m, gla, 2); - gla->update(); - e->accept(); + switch (selectionMode) { + case SELECT_VERT_MODE: + if (ctrlState){ + if (mod & Qt::ControlModifier) + gla->setCursor(QCursor(QPixmap(":/images/sel_rect.png"), 1, 1)); + else if (mod & Qt::ShiftModifier) + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_minus.png"), 1, 1)); + } else { + if (mod & Qt::ControlModifier) + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_plus.png"), 1, 1)); + else if (mod & Qt::ShiftModifier) + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_minus.png"), 1, 1)); + } + break; + + default: + if (mod & Qt::AltModifier) { + if (ctrlState){ + if (mod & Qt::ControlModifier) + gla->setCursor(QCursor(QPixmap(":/images/sel_rect.png"), 1, 1)); + else if (mod & Qt::ShiftModifier) + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_minus.png"), 1, 1)); + else + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_plus.png"), 1, 1)); + } else { + if (mod & Qt::ControlModifier) + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_plus.png"), 1, 1)); + else if (mod & Qt::ShiftModifier) + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_minus.png"), 1, 1)); + else + gla->setCursor(QCursor(QPixmap(":/images/sel_rect.png"), 1, 1)); + } + } else { + if (ctrlState){ + if (mod & Qt::ControlModifier) + gla->setCursor(QCursor(QPixmap(":/images/sel_rect.png"), 1, 1)); + else if (mod & Qt::ShiftModifier) + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_minus.png"), 1, 1)); + } else { + if (mod & Qt::ControlModifier) + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_plus.png"), 1, 1)); + else if (mod & Qt::ShiftModifier) + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_minus.png"), 1, 1)); + } + } + break; } - gla->setCursor(QCursor(QPixmap(":/images/sel_area.png"), 1, 1)); } - else - { - gla->setCursor(QCursor(QPixmap(":/images/sel_rect.png"), 1, 1)); - Qt::KeyboardModifiers mod = QApplication::queryKeyboardModifiers(); - if(selectionMode == SELECT_VERT_MODE) - { - if (mod & Qt::ControlModifier) - gla->setCursor(QCursor(QPixmap(":/images/sel_rect_plus.png"), 1, 1)); - else if (mod & Qt::ShiftModifier) - gla->setCursor(QCursor(QPixmap(":/images/sel_rect_minus.png"), 1, 1)); - } - else - { - if (mod & Qt::AltModifier) - { - if (mod & Qt::ControlModifier) - gla->setCursor(QCursor(QPixmap(":/images/sel_rect_plus_eye.png"), 1, 1)); - else if (mod & Qt::ShiftModifier) - gla->setCursor(QCursor(QPixmap(":/images/sel_rect_minus_eye.png"), 1, 1)); - else - gla->setCursor(QCursor(QPixmap(":/images/sel_rect_eye.png"), 1, 1)); - } - else - { - if (mod & Qt::ControlModifier) - gla->setCursor(QCursor(QPixmap(":/images/sel_rect_plus.png"), 1, 1)); - else if (mod & Qt::ShiftModifier) - gla->setCursor(QCursor(QPixmap(":/images/sel_rect_minus.png"), 1, 1)); - } - } - } - + if(ctrlState){ + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_plus.png"), 1, 1)); + } else { + gla->setCursor(QCursor(QPixmap(":/images/sel_rect.png"), 1, 1)); + } } void EditSelectPlugin::doSelection(MeshModel &m, GLArea *gla, int mode) @@ -264,43 +332,61 @@ void EditSelectPlugin::doSelection(MeshModel &m, GLArea *gla, int mode) } -void EditSelectPlugin::keyPressEvent(QKeyEvent * /*event*/, MeshModel & /*m*/, GLArea *gla) +void EditSelectPlugin::keyPressEvent(QKeyEvent *event, MeshModel &m, GLArea *gla) { - if (selectionMode == SELECT_AREA_MODE) - return; + bool ctrlState = currentGlobalParamSet->getBool("MeshLab::Editors::InvertCTRLBehavior"); - gla->setCursor(QCursor(QPixmap(":/images/sel_rect.png"), 1, 1)); - Qt::KeyboardModifiers mod = QApplication::queryKeyboardModifiers(); - if(selectionMode == SELECT_VERT_MODE) - { - if (mod & Qt::ControlModifier) - gla->setCursor(QCursor(QPixmap(":/images/sel_rect_plus.png"), 1, 1)); - else if (mod & Qt::ShiftModifier) - gla->setCursor(QCursor(QPixmap(":/images/sel_rect_minus.png"), 1, 1)); - } - else - { - if (mod & Qt::AltModifier) - { - if (mod & Qt::ControlModifier) - gla->setCursor(QCursor(QPixmap(":/images/sel_rect_plus_eye.png"), 1, 1)); - else if (mod & Qt::ShiftModifier) - gla->setCursor(QCursor(QPixmap(":/images/sel_rect_minus_eye.png"), 1, 1)); - else - gla->setCursor(QCursor(QPixmap(":/images/sel_rect_eye.png"), 1, 1)); - } - else - { - if (mod & Qt::ControlModifier) - gla->setCursor(QCursor(QPixmap(":/images/sel_rect_plus.png"), 1, 1)); - else if (mod & Qt::ShiftModifier) - gla->setCursor(QCursor(QPixmap(":/images/sel_rect_minus.png"), 1, 1)); - } - } + switch (event->key()) + { + case Qt::Key_Control: + { + if (ctrlState) { + gla->setCursor(QCursor(QPixmap(":/images/sel_rect.png"), 1, 1)); + } else { + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_plus.png"), 1, 1)); + } + break; + } + case Qt::Key_Shift: + { + if (ctrlState) { + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_minus.png"), 1, 1)); + } else { + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_plus.png"), 1, 1)); + } + break; + } + case Qt::Key_Alt: + { + if (ctrlState) { + if (event->modifiers() & Qt::ControlModifier) { + gla->setCursor(QCursor(QPixmap(":/images/sel_rect.png"), 1, 1)); + } else if (event->modifiers() & Qt::ShiftModifier) { + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_minus.png"), 1, 1)); + } else { + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_plus_eye.png"), 1, 1)); + } + } else { + if (event->modifiers() & Qt::ControlModifier) { + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_plus.png"), 1, 1)); + } else if (event->modifiers() & Qt::ShiftModifier) { + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_minus.png"), 1, 1)); + } else { + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_eye.png"), 1, 1)); + } + } + break; + } + default: + { + break; + } + } } void EditSelectPlugin::mousePressEvent(QMouseEvent * event, MeshModel &m, GLArea *gla) { + bool ctrlState = currentGlobalParamSet->getBool("MeshLab::Editors::InvertCTRLBehavior"); if (selectionMode == SELECT_AREA_MODE) { selPolyLine.push_back(QTLogicalToOpenGL(gla, event->pos())); @@ -310,37 +396,64 @@ void EditSelectPlugin::mousePressEvent(QMouseEvent * event, MeshModel &m, GLArea LastSelVert.clear(); LastSelFace.clear(); - if ((event->modifiers() & Qt::ControlModifier) || - (event->modifiers() & Qt::ShiftModifier)) - { - CMeshO::FaceIterator fi; - for (fi = m.cm.face.begin(); fi != m.cm.face.end(); ++fi) - if (!(*fi).IsD() && (*fi).IsS()) - LastSelFace.push_back(&*fi); - - CMeshO::VertexIterator vi; - for (vi = m.cm.vert.begin(); vi != m.cm.vert.end(); ++vi) - if (!(*vi).IsD() && (*vi).IsS()) - LastSelVert.push_back(&*vi); + int ctrl = (event->modifiers() & Qt::ControlModifier) ? 1 : 0; + int shift = (event->modifiers() & Qt::ShiftModifier) ? 1 : 0; + int alt = (event->modifiers() & Qt::AltModifier) ? 1 : 0; + + switch (ctrlState * 4 + ctrl * 2 + shift) { + case 0: // !ctrlState && !ctrl && !shift + composingSelMode = SMClear; + selectFrontFlag = false; + break; + case 1: // !ctrlState && !ctrl && shift + composingSelMode = SMSub; + selectFrontFlag = false; + break; + case 2: // !ctrlState && ctrl && !shift + composingSelMode = SMAdd; + selectFrontFlag = false; + break; + case 3: // !ctrlState && ctrl && shift + composingSelMode = SMSub; + selectFrontFlag = false; + break; + case 4: // ctrlState && !ctrl && !shift + composingSelMode = SMAdd; + selectFrontFlag = alt; + break; + case 5: // ctrlState && !ctrl && shift + composingSelMode = SMSub; + selectFrontFlag = alt; + break; + case 6: // ctrlState && ctrl && !shift + composingSelMode = SMClear; + selectFrontFlag = alt; + break; + case 7: // ctrlState && ctrl && shift + composingSelMode = SMSub; + selectFrontFlag = alt; + break; } - composingSelMode = SMClear; - if (event->modifiers() & Qt::ControlModifier) - composingSelMode = SMAdd; - else if (event->modifiers() & Qt::ShiftModifier) - composingSelMode = SMSub; - - if (event->modifiers() & Qt::AltModifier) - selectFrontFlag = true; - else - selectFrontFlag = false; - start = QTLogicalToOpenGL(gla, event->pos()); cur = start; - return; + + if (ctrlState && (!(event->modifiers() & Qt::ControlModifier) || (event->modifiers() & Qt::ShiftModifier))) { + for (CMeshO::FaceIterator fi = m.cm.face.begin(); fi != m.cm.face.end(); ++fi) { + if (!(*fi).IsD() && (*fi).IsS()) { + LastSelFace.push_back(&*fi); + } + } + + for (CMeshO::VertexIterator vi = m.cm.vert.begin(); vi != m.cm.vert.end(); ++vi) { + if (!(*vi).IsD() && (*vi).IsS()) { + LastSelVert.push_back(&*vi); + } + } + } } -void EditSelectPlugin::mouseMoveEvent(QMouseEvent * event, MeshModel & /*m*/, GLArea * gla) +void EditSelectPlugin::mouseMoveEvent(QMouseEvent * event, MeshModel &m, GLArea * gla) { if (selectionMode == SELECT_AREA_MODE) { @@ -367,7 +480,7 @@ void EditSelectPlugin::mouseMoveEvent(QMouseEvent * event, MeshModel & /*m*/, GL // } } -void EditSelectPlugin::mouseReleaseEvent(QMouseEvent * event, MeshModel &/*m*/, GLArea * gla) +void EditSelectPlugin::mouseReleaseEvent(QMouseEvent * event, MeshModel &m, GLArea * gla) { //gla->update(); if (gla == NULL) @@ -481,12 +594,13 @@ void EditSelectPlugin::DrawXORRect(GLArea * gla, bool doubleDraw) void EditSelectPlugin::decorate(MeshModel &m, GLArea * gla) { + bool ctrlState = currentGlobalParamSet->getBool("MeshLab::Editors::InvertCTRLBehavior"); if (selectionMode == SELECT_AREA_MODE) { // get proj data of last rendering glPushMatrix(); glMultMatrix(m.cm.Tr); - GLPickTri::glGetMatrixAndViewport(this->SelMatrix, this->SelViewport); + GLPickTri::glGetMatrixAndViewport(this->SelMatrix, this->SelViewport); glGetDoublev(GL_MODELVIEW_MATRIX, mvMatrix_f); glGetDoublev(GL_PROJECTION_MATRIX, prMatrix_f); glGetIntegerv(GL_VIEWPORT, viewpSize); @@ -522,16 +636,30 @@ void EditSelectPlugin::decorate(MeshModel &m, GLArea * gla) } else { - QString line1, line2, line3; + if (ctrlState){ + QString line1, line2, line3; - line1 = "Drag to select"; - if ((selectionMode == SELECT_FACE_MODE) || (selectionMode == SELECT_CONN_MODE)) - line2 = "you may hold:
- CTRL to add
- SHIFT to subtract
- ALT to select only visible"; - else + line1 = "Drag to select"; + if ((selectionMode == SELECT_FACE_MODE) || (selectionMode == SELECT_CONN_MODE)) + line2 = "you may hold:
- CTRL to NEW selection
- SHIFT to subtract
- ALT to select only visible"; + else + line2 = "you may hold:
- CTRL to NEW selection
- SHIFT to subtract"; + line3 = "
A select all, D de-select all, I invert all"; + + this->realTimeLog("Interactive Selection", m.shortName(), "%s
%s
%s", line1.toStdString().c_str(), line2.toStdString().c_str(), line3.toStdString().c_str()); + } + else{ + QString line1, line2, line3; + + line1 = "Drag to select"; + if ((selectionMode == SELECT_FACE_MODE) || (selectionMode == SELECT_CONN_MODE)) + line2 = "you may hold:
- CTRL to add
- SHIFT to subtract
- ALT to select only visible"; + else line2 = "you may hold:
- CTRL to add
- SHIFT to subtract"; - line3 = "
A select all, D de-select all, I invert all"; + line3 = "
A select all, D de-select all, I invert all"; - this->realTimeLog("Interactive Selection", m.shortName(), "%s
%s
%s", line1.toStdString().c_str(), line2.toStdString().c_str(), line3.toStdString().c_str()); + this->realTimeLog("Interactive Selection", m.shortName(), "%s
%s
%s", line1.toStdString().c_str(), line2.toStdString().c_str(), line3.toStdString().c_str()); + } } if (isDragging) @@ -629,11 +757,17 @@ void EditSelectPlugin::decorate(MeshModel &m, GLArea * gla) bool EditSelectPlugin::startEdit(MeshModel & m, GLArea * gla, MLSceneGLSharedDataContext* /*cont*/) { + bool ctrlState = currentGlobalParamSet->getBool("MeshLab::Editors::InvertCTRLBehavior"); if (gla == NULL) return false; if (!GLExtensionsManager::initializeGLextensions_notThrowing()) return false; + if (ctrlState){ + gla->setCursor(QCursor(QPixmap(":/images/sel_rect_plus.png"), 1, 1)); + } + else{ gla->setCursor(QCursor(QPixmap(":/images/sel_rect.png"), 1, 1)); + } if (selectionMode == SELECT_AREA_MODE) { diff --git a/src/meshlabplugins/edit_select/edit_select.h b/src/meshlabplugins/edit_select/edit_select.h index a5993dbcae..1a4497509a 100644 --- a/src/meshlabplugins/edit_select/edit_select.h +++ b/src/meshlabplugins/edit_select/edit_select.h @@ -24,29 +24,30 @@ #define EDITPLUGIN_H #include +#include class EditSelectPlugin : public QObject, public EditTool { Q_OBJECT - public: - enum { SELECT_FACE_MODE, SELECT_VERT_MODE, SELECT_CONN_MODE, SELECT_AREA_MODE }; - EditSelectPlugin(int _ConnectedMode); + enum { SELECT_FACE_MODE, SELECT_VERT_MODE, SELECT_CONN_MODE, SELECT_AREA_MODE }; + EditSelectPlugin(RichParameterList* cgp, int _ConnectedMode); virtual ~EditSelectPlugin() {} - static QString info(); void suggestedRenderingData(MeshModel & m, MLRenderingData& dt); bool startEdit(MeshModel &/*m*/, GLArea * /*parent*/, MLSceneGLSharedDataContext* /*cont*/); void endEdit(MeshModel &/*m*/, GLArea * /*parent*/, MLSceneGLSharedDataContext* /*cont*/) {} void decorate(MeshModel &/*m*/, GLArea * /*parent*/); - void mousePressEvent(QMouseEvent *event, MeshModel &/*m*/, GLArea *); - void mouseMoveEvent(QMouseEvent *event, MeshModel &/*m*/, GLArea *); - void mouseReleaseEvent(QMouseEvent *event, MeshModel &/*m*/, GLArea *); - void keyReleaseEvent(QKeyEvent *, MeshModel &/*m*/, GLArea *); - void keyPressEvent(QKeyEvent *, MeshModel &/*m*/, GLArea *); + void mousePressEvent(QMouseEvent *event, MeshModel &/*m*/, GLArea *gla); + void mouseMoveEvent(QMouseEvent *event, MeshModel &/*m*/, GLArea *gla); + void mouseReleaseEvent(QMouseEvent *event, MeshModel &/*m*/, GLArea *gla); + virtual void keyReleaseEvent(QKeyEvent *, MeshModel &m, GLArea *gla); + void keyPressEvent(QKeyEvent *, MeshModel &m, GLArea *gla); + EditTool* getEditTool(const QAction *action); + bool keyReleaseEventFilter(QObject *obj, QEvent *event); vcg::Point2f start; vcg::Point2f cur; @@ -71,12 +72,17 @@ class EditSelectPlugin : public QObject, public EditTool void setDecorator(QString, bool); private: + MeshModel *m_ref; + GLArea *gla_ref; + RichParameterList* currentGlobalParamSet; + bool ctrlState; typedef enum { SMAdd, SMClear, SMSub } ComposingSelMode; // How the selection are composed ComposingSelMode composingSelMode; bool selectFrontFlag; void DrawXORRect(GLArea * gla, bool doubleDraw); void DrawXORPolyLine(GLArea * gla); void doSelection(MeshModel &m, GLArea *gla, int mode); + }; #endif diff --git a/src/meshlabplugins/edit_select/edit_select_factory.cpp b/src/meshlabplugins/edit_select/edit_select_factory.cpp index be743ddce7..c3b16299f6 100644 --- a/src/meshlabplugins/edit_select/edit_select_factory.cpp +++ b/src/meshlabplugins/edit_select/edit_select_factory.cpp @@ -23,6 +23,7 @@ #include "edit_select_factory.h" #include "edit_select.h" +#include "common/parameters/rich_parameter_list.h" EditSelectFactory::EditSelectFactory() { @@ -37,7 +38,10 @@ EditSelectFactory::EditSelectFactory() actionList.push_back(editSelectArea); foreach(QAction *editAction, actionList) - editAction->setCheckable(true); + editAction->setCheckable(true); +} +void EditSelectFactory::initGlobalParameterList(RichParameterList& defaultGlobalParamSet) { + defaultGlobalParamSet.addParam(RichBool(InvertCtrlBehavior(), false,"Invert the behavior of the CTRL modifier on edit selection rectangle tools","")); } QString EditSelectFactory::pluginName() const @@ -48,17 +52,21 @@ QString EditSelectFactory::pluginName() const //get the edit tool for the given action EditTool* EditSelectFactory::getEditTool(const QAction *action) { + EditSelectPlugin* result = nullptr; if(action == editSelect) - return new EditSelectPlugin(EditSelectPlugin::SELECT_FACE_MODE); + result = new EditSelectPlugin(currentGlobalParamSet,EditSelectPlugin::SELECT_FACE_MODE); else if(action == editSelectConnected) - return new EditSelectPlugin(EditSelectPlugin::SELECT_CONN_MODE); + result = new EditSelectPlugin(currentGlobalParamSet,EditSelectPlugin::SELECT_CONN_MODE); else if(action == editSelectVert) - return new EditSelectPlugin(EditSelectPlugin::SELECT_VERT_MODE); + result = new EditSelectPlugin(currentGlobalParamSet,EditSelectPlugin::SELECT_VERT_MODE); else if (action == editSelectArea) - return new EditSelectPlugin(EditSelectPlugin::SELECT_AREA_MODE); + result = new EditSelectPlugin(currentGlobalParamSet,EditSelectPlugin::SELECT_AREA_MODE); + + if (result == nullptr) { + assert(0); + } - assert(0); //should never be asked for an action that isn't here - return nullptr; + return (EditTool*)result; } QString EditSelectFactory::getEditToolDescription(const QAction * /*a*/) diff --git a/src/meshlabplugins/edit_select/edit_select_factory.h b/src/meshlabplugins/edit_select/edit_select_factory.h index 46497cab37..71442dbe68 100644 --- a/src/meshlabplugins/edit_select/edit_select_factory.h +++ b/src/meshlabplugins/edit_select/edit_select_factory.h @@ -26,6 +26,7 @@ #define EditSelectFactoryPLUGIN_H #include +#include "common/parameters/rich_parameter_list.h" class EditSelectFactory : public QObject, public EditPlugin { @@ -37,6 +38,8 @@ class EditSelectFactory : public QObject, public EditPlugin EditSelectFactory(); virtual ~EditSelectFactory() { delete editSelect; } + virtual void initGlobalParameterList(RichParameterList& defaultGlobalParamSet); + virtual QString pluginName() const; //get the edit tool for the given action @@ -45,7 +48,13 @@ class EditSelectFactory : public QObject, public EditPlugin //get the description for the given action virtual QString getEditToolDescription(const QAction*); + inline QString InvertCtrlBehavior() const { return "MeshLab::Editors::InvertCTRLBehavior" ; } + +signals: + void setDecorator(QString, bool); + private: + bool ctrlState; QAction *editSelect; QAction *editSelectVert; QAction *editSelectConnected;