/**************************************************************************** * 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 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 local_facePointer; local_facePointer.push_back(&pickedAbutment.f); local_facePointer.push_back(&picked.f); QString errLog; if(FgtBridge::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::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; }