mirror of
https://github.com/lucaspalomodevelop/meshlab.git
synced 2026-03-18 18:44:39 +00:00
440 lines
12 KiB
C++
440 lines
12 KiB
C++
/****************************************************************************
|
|
* VCGLib o o *
|
|
* Visual and Computer Graphics Library o o *
|
|
* _ O _ *
|
|
* Copyright(C) 2004 \/)\/ *
|
|
* 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 "holeListModel.h"
|
|
|
|
using namespace vcg;
|
|
|
|
|
|
HoleListModel::HoleListModel(MeshModel *m, QObject *parent)
|
|
: QAbstractItemModel(parent)
|
|
{
|
|
state = HoleListModel::Selection;
|
|
mesh = m;
|
|
pickedAbutment.SetNull();
|
|
|
|
mesh->clearDataMask(MeshModel::MM_FACEFLAGBORDER | MeshModel::MM_FACEFACETOPO);
|
|
mesh->updateDataMask(MeshModel::MM_FACEFACETOPO | MeshModel::MM_FACEFLAGBORDER);
|
|
holesManager.Init(&m->cm);
|
|
emit dataChanged( index(0, 0), index(holesManager.HolesCount(), 2) );
|
|
emit SGN_needUpdateGLA();
|
|
}
|
|
|
|
|
|
void HoleListModel::drawHoles() const
|
|
{
|
|
glLineWidth(2.0f);
|
|
glDepthMask(GL_TRUE);
|
|
glDepthFunc(GL_GREATER);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDisable(GL_LIGHTING);
|
|
|
|
HoleVector::const_iterator it = holesManager.holes.begin();
|
|
for( ; it != holesManager.holes.end(); ++it)
|
|
{
|
|
if( it->IsSelected())
|
|
{
|
|
if(it->IsAccepted())
|
|
glColor(Color4b::DarkGreen);
|
|
else
|
|
glColor(Color4b::DarkRed);
|
|
}
|
|
else
|
|
glColor(Color4b::DarkBlue);
|
|
it->Draw();
|
|
}
|
|
|
|
// draw the edge selected as a bridge abutment in manual-bridging
|
|
if(!pickedAbutment.IsNull())
|
|
{
|
|
glDepthFunc(GL_ALWAYS);
|
|
glLineWidth(2.0f);
|
|
glColor(Color4b::Yellow);
|
|
glBegin(GL_LINES);
|
|
glVertex( pickedAbutment.f->V0(pickedAbutment.z)->P() );
|
|
glVertex( pickedAbutment.f->V1(pickedAbutment.z)->P() );
|
|
glEnd();
|
|
}
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthFunc(GL_LESS);
|
|
glLineWidth(2.0f);
|
|
for(it = holesManager.holes.begin(); it != holesManager.holes.end(); ++it)
|
|
{
|
|
if(it->IsSelected())
|
|
{
|
|
if(it->IsAccepted())
|
|
glColor(Color4b::Green);
|
|
else
|
|
glColor(Color4b::Red);
|
|
}
|
|
else
|
|
glColor(Color4b::Blue);
|
|
|
|
it->Draw();
|
|
}
|
|
}
|
|
|
|
void HoleListModel::drawCompenetratingFaces() const
|
|
{
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDepthFunc(GL_ALWAYS);
|
|
glDisable(GL_LIGHTING);
|
|
glColor3f(0.8f, 0.8f, 0.f);
|
|
|
|
// draw face border also behind other face
|
|
HoleVector::const_iterator it;
|
|
for(it = holesManager.holes.begin(); it != holesManager.holes.end(); ++it)
|
|
if(it->IsCompenetrating())
|
|
it->DrawCompenetratingFace(GL_LINE_LOOP);
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthFunc(GL_LESS);
|
|
|
|
// draw compenetrating face, only the visible part
|
|
for(it = holesManager.holes.begin(); it != holesManager.holes.end(); ++it)
|
|
if(it->IsCompenetrating())
|
|
it->DrawCompenetratingFace(GL_TRIANGLES);
|
|
|
|
// draw face border only visible part
|
|
glLineWidth(4.0f);
|
|
glColor3f(1.0f, 1.0f, 0.f);
|
|
for(it = holesManager.holes.begin(); it != holesManager.holes.end(); ++it)
|
|
if(it->IsCompenetrating())
|
|
it->DrawCompenetratingFace(GL_LINE_LOOP);
|
|
}
|
|
|
|
void HoleListModel::toggleSelectionHoleFromFace(CFaceO *bface)
|
|
{
|
|
assert(holesManager.IsHoleBorderFace(bface));
|
|
HoleVector::iterator h;
|
|
|
|
int ind = holesManager.FindHoleFromFace(bface, h);
|
|
if( ind == -1)
|
|
return;
|
|
h->SetSelect( !h->IsSelected() );
|
|
|
|
emit dataChanged( index(ind, 4), index(ind, 4) );
|
|
emit SGN_needUpdateGLA();
|
|
}
|
|
|
|
void HoleListModel::toggleAcceptanceHole(CFaceO *bface)
|
|
{
|
|
assert(state == HoleListModel::Filled);
|
|
HoleVector::iterator h;
|
|
int ind = holesManager.FindHoleFromFace(bface, h);
|
|
|
|
if(ind == -1)
|
|
return;
|
|
h->SetAccepted( !h->IsAccepted() );
|
|
|
|
emit dataChanged( index(ind, 6), index(ind, 6) );
|
|
emit SGN_needUpdateGLA();
|
|
}
|
|
|
|
void HoleListModel::addBridgeFace(CFaceO *pickedFace, int pickedX, int pickedY)
|
|
{
|
|
BridgeAbutment<CMeshO> picked;
|
|
if (!holesManager.FindBridgeAbutmentFromPick(pickedFace, pickedX, pickedY, picked))
|
|
return;
|
|
|
|
// reselect an already selected edge... deselect
|
|
if(pickedAbutment.f == picked.f && pickedAbutment.z == picked.z )
|
|
{
|
|
pickedAbutment.SetNull();
|
|
return;
|
|
}
|
|
|
|
if(pickedAbutment.IsNull() || pickedAbutment.f == picked.f)
|
|
pickedAbutment = picked;
|
|
else
|
|
{
|
|
// 2 edge selected... bridge building
|
|
std::vector<CMeshO::FacePointer *> local_facePointer;
|
|
local_facePointer.push_back(&pickedAbutment.f);
|
|
local_facePointer.push_back(&picked.f);
|
|
QString errLog;
|
|
if(FgtBridge<CMeshO>::CreateBridge(pickedAbutment, picked, &holesManager, errLog))
|
|
{
|
|
emit SGN_ExistBridge(true);
|
|
emit layoutChanged();
|
|
}
|
|
else
|
|
QMessageBox::warning(0, tr("Bridge error"), errLog);
|
|
|
|
pickedAbutment.SetNull();
|
|
}
|
|
}
|
|
|
|
void HoleListModel::fill(FgtHole<CMeshO>::FillerMode mode)
|
|
{
|
|
mesh->clearDataMask(MeshModel::MM_FACEFLAGBORDER | MeshModel::MM_FACEFACETOPO );
|
|
mesh->updateDataMask(MeshModel::MM_FACEFACETOPO | MeshModel::MM_FACEFLAGBORDER );
|
|
if(holesManager.Fill(mode))
|
|
{
|
|
state = HoleListModel::Filled;
|
|
emit layoutChanged();
|
|
}
|
|
}
|
|
|
|
void HoleListModel::acceptFilling(bool accept)
|
|
{
|
|
/* if(!accept)
|
|
{
|
|
mesh->clearDataMask(MeshModel::MM_FACEFLAGBORDER | MeshModel::MM_FACEFACETOPO );
|
|
mesh->updateDataMask(MeshModel::MM_FACEFACETOPO | MeshModel::MM_FACEFLAGBORDER );
|
|
}
|
|
*/
|
|
holesManager.ConfirmFilling(accept);
|
|
|
|
state = HoleListModel::Selection;
|
|
emit dataChanged( index(0, 0), index(holesManager.HolesCount(), 2) );
|
|
if(holesManager.holes.size()==0)
|
|
{
|
|
QMessageBox::information(0, tr("No holes"), QString("Mesh have no hole to edit."));
|
|
emit SGN_Closing();
|
|
}
|
|
else
|
|
{
|
|
emit SGN_ExistBridge( holesManager.bridges.size() > 0 );
|
|
emit SGN_needUpdateGLA();
|
|
emit layoutChanged();
|
|
}
|
|
}
|
|
|
|
void HoleListModel::autoBridge(bool singleHole, double distCoeff)
|
|
{
|
|
holesManager.DiscardBridges();
|
|
|
|
mesh->clearDataMask(MeshModel::MM_FACEFLAGBORDER | MeshModel::MM_FACEFACETOPO );
|
|
mesh->updateDataMask(MeshModel::MM_FACEFACETOPO | MeshModel::MM_FACEFLAGBORDER );
|
|
|
|
if(singleHole)
|
|
holesManager.AutoSelfBridging(distCoeff, 0);
|
|
else
|
|
holesManager.AutoMultiBridging(0);
|
|
|
|
emit SGN_ExistBridge( holesManager.bridges.size() > 0 );
|
|
emit layoutChanged();
|
|
}
|
|
|
|
void HoleListModel::removeBridges()
|
|
{
|
|
holesManager.DiscardBridges();
|
|
emit SGN_ExistBridge(false);
|
|
emit layoutChanged();
|
|
}
|
|
|
|
void HoleListModel::acceptBridges()
|
|
{
|
|
holesManager.ConfirmBridges();
|
|
emit SGN_ExistBridge(false);
|
|
}
|
|
|
|
void HoleListModel::closeNonManifolds()
|
|
{
|
|
holesManager.CloseNonManifoldHoles();
|
|
emit SGN_ExistBridge( holesManager.bridges.size()>0 );
|
|
|
|
emit layoutChanged();
|
|
}
|
|
|
|
|
|
/************* Implementazione QAbstractItemModel class *****************/
|
|
|
|
|
|
QVariant HoleListModel::data(const QModelIndex &index, int role) const
|
|
{
|
|
if(!index.isValid() )
|
|
return QVariant();
|
|
|
|
if(role == Qt::DisplayRole)
|
|
{
|
|
switch(index.column())
|
|
{
|
|
case 0:
|
|
return holesManager.holes[index.row()].name;
|
|
case 1:
|
|
return holesManager.holes.at(index.row()).Size();
|
|
case 2:
|
|
return QString("%1").arg(holesManager.holes.at(index.row()).Perimeter(), 0, 'f', 5);
|
|
}
|
|
}
|
|
else if (role == Qt::TextAlignmentRole)
|
|
{
|
|
if(index.column() == 0) return Qt::AlignLeft;
|
|
if(index.column() == 1 ||
|
|
index.column() == 2) return Qt::AlignRight;
|
|
return Qt::AlignCenter;
|
|
}
|
|
else if (role == Qt::CheckStateRole)
|
|
{
|
|
bool checked;
|
|
if(index.column() == 3)
|
|
checked = holesManager.holes[index.row()].IsNonManifold();
|
|
else if(index.column() == 4)
|
|
checked = holesManager.holes[index.row()].IsSelected();
|
|
else if(state == HoleListModel::Filled && holesManager.holes[index.row()].IsSelected())
|
|
{
|
|
if(index.column() == 5)
|
|
checked = holesManager.holes[index.row()].IsCompenetrating();
|
|
else if(index.column() == 6)
|
|
checked = holesManager.holes[index.row()].IsAccepted();
|
|
else
|
|
return QVariant();
|
|
}
|
|
else
|
|
return QVariant();
|
|
|
|
if(checked)
|
|
return Qt::Checked;
|
|
else
|
|
return Qt::Unchecked;
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
|
|
QVariant HoleListModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
{
|
|
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
|
|
{
|
|
switch(section)
|
|
{
|
|
case 0:
|
|
return tr("Hole");
|
|
case 1:
|
|
return tr("Edges");
|
|
case 2:
|
|
return tr("Perimeter");
|
|
case 3:
|
|
return tr("Non Manif.");
|
|
case 4:
|
|
if(state == HoleListModel::Filled)
|
|
return tr("Fill");
|
|
else
|
|
return tr("Select");
|
|
case 5:
|
|
if(state == HoleListModel::Filled)
|
|
return tr("Comp.");
|
|
case 6:
|
|
if(state == HoleListModel::Filled)
|
|
return tr("Accept");
|
|
}
|
|
}
|
|
else if (orientation == Qt::Horizontal && role == Qt::SizeHintRole)
|
|
{
|
|
switch(section)
|
|
{
|
|
case 0:
|
|
return QSize(63, 20);
|
|
case 1:
|
|
return QSize(38, 20);
|
|
case 2:
|
|
return QSize(55, 20);
|
|
case 3:
|
|
return QSize(60, 20);
|
|
case 4:
|
|
if(state == HoleListModel::Filled)
|
|
return QSize(20, 20);
|
|
else
|
|
return QSize(50, 20);
|
|
case 5:
|
|
return QSize(38, 20);
|
|
case 6:
|
|
return QSize(42, 20);
|
|
}
|
|
}
|
|
else if (orientation == Qt::Horizontal && role == Qt::ToolTip && state==HoleListModel::Filled && section == 4)
|
|
return tr("Compenetration");
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
|
|
QModelIndex HoleListModel::index(int row, int column, const QModelIndex &/*parent*/) const
|
|
{
|
|
if(row >= (int)holesManager.holes.size())
|
|
return QModelIndex();
|
|
return createIndex(row,column, 0);
|
|
}
|
|
|
|
|
|
Qt::ItemFlags HoleListModel::flags(const QModelIndex &index) const
|
|
{
|
|
Qt::ItemFlags ret = QAbstractItemModel::flags(index);
|
|
|
|
if (!index.isValid())
|
|
return Qt::ItemIsEnabled;
|
|
|
|
if(index.column() == 0)
|
|
ret = ret | Qt::ItemIsEditable;
|
|
else if( (index.column() == 4 && state == HoleListModel::Selection) ||
|
|
(index.column() == 6 && state == HoleListModel::Filled) )
|
|
return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled;
|
|
//return ret = ret | Qt::ItemIsUserCheckable ;
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool HoleListModel::setData( const QModelIndex & index, const QVariant & value, int role )
|
|
{
|
|
if(!index.isValid())
|
|
return false;
|
|
|
|
bool ret = false;
|
|
if(role == Qt::EditRole && index.column() == 0)
|
|
{
|
|
QString newName = value.toString().trimmed();
|
|
if(newName != "")
|
|
{
|
|
holesManager.holes[index.row()].name = newName;
|
|
ret = true;
|
|
}
|
|
}
|
|
else if(role == Qt::CheckStateRole)
|
|
{
|
|
if(state == HoleListModel::Selection)
|
|
{
|
|
if(index.column() == 4 && state == HoleListModel::Selection)
|
|
{
|
|
holesManager.holes[index.row()].SetSelect( !holesManager.holes[index.row()].IsSelected() );
|
|
ret = true;
|
|
}
|
|
}
|
|
else if(index.column() == 6)
|
|
{ // check accept
|
|
holesManager.holes[index.row()].SetAccepted( !holesManager.holes[index.row()].IsAccepted() );
|
|
ret = true;
|
|
}
|
|
}
|
|
if(ret)
|
|
{
|
|
emit dataChanged(index, index);
|
|
emit SGN_needUpdateGLA();
|
|
}
|
|
|
|
return ret;
|
|
}
|