/**************************************************************************** * MeshLab o o * * An extendible mesh processor o o * * _ O _ * * Copyright(C) 2005, 2009 \/)\/ * * 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 "renderarea.h" #include "textureeditor.h" #include #include #include #define SELECTIONRECT 100 #define ORIGINRECT 200 #define MAX 100000 #define NOSEL -1 #define AREADIM 400 #define HRECT 6 RenderArea::RenderArea(QWidget *parent, QString textureName, MeshModel *m, unsigned tnum) : QGLWidget(parent) { // Parameters antialiased = true; setBackgroundRole(QPalette::Base); setAutoFillBackground(true); textNum = tnum; model = m; image = QImage(); fileName = textureName; if(textureName != QString()) { if (QFile(textureName).exists()) { image = QImage(textureName); int bestW = pow(2.0,floor(::log(double(image.width()))/::log(2.0))); int bestH = pow(2.0,floor(::log(double(image.height()))/::log(2.0))); QImage imgGL = image.scaled(bestW,bestH,Qt::IgnoreAspectRatio,Qt::SmoothTransformation); QImage tmpGL = QGLWidget::convertToGLFormat(imgGL); glGenTextures(1, &id); image = QImage(tmpGL).mirrored(false, true); } else textureName = QString(); } // Initialization oldX = 0; oldY = 0; viewport = vcg::Point2f(0,0); tmpX = 0; tmpY = 0; tb = new Trackball(); tb->center = Point3f(0, 0, 0); tb->radius = 1; panX = 0; panY = 0; oldPX = 0; oldPY = 0; posVX = 0; posVY = 0; degenerate = false; brush = QBrush(Qt::green); mode = View; oldMode = NoMode; editMode = Scale; origin = QPointF(); start = QPoint(); end = QPoint(); selectMode = Area; selBit = CFaceO::NewBitFlag(); selVertBit = CVertexO::NewBitFlag(); selected = false; selectedV = false; vertRect = QRect(0,0,RADIUS,RADIUS); selRect = vector(); for (int i = 0; i < 4; i++) selRect.push_back(QRect(0,0,RECTDIM,RECTDIM)); selStart = QPoint(0,0); selEnd = QPoint(0,0); degree = 0.0f; highlighted = NOSEL; pressed = NOSEL; posX = 0; posY = 0; oScale = QPointF(0,0); scaleX = 1; scaleY = 1; orX = 0; orY = 0; initVX = 0; initVY = 0; locked = false; drawnPath = vector(); drawnPath1 = vector(); banList = vector(); drawP = false; drawP1 = false; path = vector(); path1 = vector(); zoom = 1; rot = QImage(QString(":/images/rotate.png")); scal = QImage(QString(":/images/scale.png")); this->setMouseTracking(true); this->setCursor(Qt::PointingHandCursor); this->setAttribute(Qt::WA_NoSystemBackground); this->setAttribute(Qt::WA_KeyCompression, true); this->setFocusPolicy(Qt::ClickFocus); } RenderArea::~RenderArea() { //CFaceO::DeleteBitFlag(selBit); //CVertexO::DeleteBitFlag(selVertBit); } // Change the Pen of the RenderArea void RenderArea::setPen(const QPen &pen) { this->pen = pen; update(); } // Chanhe the Brush of the RenderArea void RenderArea::setBrush(const QBrush &brush) { this->brush = brush; update(); } // Set the Antialiased property void RenderArea::setAntialiased(bool antialiased) { this->antialiased = antialiased; update(); } // Change the background texture void RenderArea::setTexture(QString path) { image = QImage(path); fileName = path; this->update(); } // Returns the name of the background image QString RenderArea::GetTextureName() { return fileName; } // Inform the render area that the model has degenerate coordinates void RenderArea::SetDegenerate(bool deg) { degenerate = deg; } void RenderArea::paintEvent(QPaintEvent *) { // Init QPainter painter(this); painter.setPen(QPen(brush,2)); painter.setBrush(brush); painter.setRenderHint(QPainter::Antialiasing, antialiased); painter.setRenderHint(QPainter::SmoothPixmapTransform, true); tb->GetView(); tb->Apply(); maxX = 0; maxY = 0; minX = 0; minY = 0; if (model != NULL && HasPerWedgeTexCoord(model->cm) && image != QImage()) { glEnable(GL_COLOR_LOGIC_OP); glEnable(GL_DEPTH_TEST); glLineWidth(1); for (unsigned i = 0; i < model->cm.face.size(); i++) { glLogicOp(GL_XOR); glColor3f(1,1,1); // First draw the model in u,v space if (model->cm.face[i].WT(0).n() == textNum) { // While drawning, it counts the number of 'planes' if (model->cm.face[i].WT(0).u() > maxX || model->cm.face[i].WT(1).u() > maxX || model->cm.face[i].WT(2).u() > maxX) maxX++; if (model->cm.face[i].WT(0).v() > maxY || model->cm.face[i].WT(1).v() > maxY || model->cm.face[i].WT(2).v() > maxY) maxY++; if (model->cm.face[i].WT(0).u() < minX || model->cm.face[i].WT(1).u() < minX || model->cm.face[i].WT(2).u() < minX) minX--; if (model->cm.face[i].WT(0).v() < minY || model->cm.face[i].WT(1).v() < minY || model->cm.face[i].WT(2).v() < minY) minY--; drawEdge(i); // Draw the edge of faces glDisable(GL_COLOR_LOGIC_OP); glColor3f(1.0f, 0.0f, 0.0f); //if (selected && model->cm.face[i].IsUserBit(selBit)) drawSelectedFaces(i); // Draw the selected faces if (selectedV && mode != UnifyVert) drawSelectedVertexes(i); // Draw the selected vertex glEnable(GL_COLOR_LOGIC_OP); } } if (mode == UnifyVert) drawUnifyVertexes(); glDisable(GL_LOGIC_OP); glDisable(GL_COLOR_LOGIC_OP); if (minX != 0 || minY != 0 || maxX != 0 || maxY != 0) drawBackground(); // Draw the background behind the model // 2D glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0,this->width(),this->height(),0,-1,1); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glPushAttrib(GL_ENABLE_BIT); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glDisable(GL_TEXTURE_2D); drawAxis(&painter); // and the axis, always in first plane drawSelectionRectangle(&painter); // Draw the rectangle of selection if (mode != UnifyVert) drawEditRectangle(&painter); else drawUnifyRectangles(&painter); // Draw the rectangles for unify glDisable(GL_LOGIC_OP); glPopAttrib(); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); // Draw blend object glDepthMask(GL_FALSE); glLogicOp(GL_AND); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor4f(1.0f, 0.0f, 0.0f, 0.7f); for (unsigned i = 0; i < model->cm.face.size(); i++) if (selected && model->cm.face[i].IsUserBit(selBit)) drawSelectedFaces(i); // Draw the selected faces glDepthMask(GL_TRUE); glDisable(GL_BLEND); } else painter.drawText(this->visibleRegion().boundingRect().width()/2 - FMETRICX, this->visibleRegion().boundingRect().height()/2 - FMETRICY, tr("NO TEXTURE")); painter.setPen(palette().dark().color()); painter.setBrush(Qt::NoBrush); } // Support methods for drawning void RenderArea::drawSelectedVertexes(int i) { glDisable(GL_COLOR_LOGIC_OP); glColor3f(1,0,0); if (!isInside(&model->cm.face[i])) { for (int j = 0; j < 3; j++) { if(areaUV.contains(QPointF(model->cm.face[i].WT(j).u(), model->cm.face[i].WT(j).v())) && model->cm.face[i].V(j)->IsUserBit(selVertBit)) DrawCircle(QPoint((origin.x() + (cos(degree) * (model->cm.face[i].WT(j).u() - origin.x()) - sin(degree) * (model->cm.face[i].WT(j).v() - origin.y()))) * AREADIM - posVX/zoom, (AREADIM - ((origin.y() + (sin(degree) * (model->cm.face[i].WT(j).u() - origin.x()) + cos(degree) * (model->cm.face[i].WT(j).v() - origin.y())))) * AREADIM) - posVY/zoom)); } } glColor3f(0,0,0); glEnable(GL_COLOR_LOGIC_OP); } void RenderArea::drawSelectedFaces(int i) { glBegin(GL_TRIANGLES); for (int j = 0; j < 3; j++) { if (editMode == Scale) glVertex3f((oScale.x() + (model->cm.face[i].WT(j).u() - oScale.x()) * scaleX) * AREADIM - panX/zoom, AREADIM - ((oScale.y() + (model->cm.face[i].WT(j).v() - oScale.y()) * scaleY) * AREADIM) - panY/zoom, 1); else glVertex3f((origin.x() + (cos(degree) * (model->cm.face[i].WT(j).u() - origin.x()) - sin(degree) * (model->cm.face[i].WT(j).v() - origin.y()))) * AREADIM - panX/zoom, AREADIM - ((origin.y() + (sin(degree) * (model->cm.face[i].WT(j).u() - origin.x()) + cos(degree) * (model->cm.face[i].WT(j).v() - origin.y()))) * AREADIM) - panY/zoom, 1); } glEnd(); } void RenderArea::drawEdge(int i) { glBegin(GL_LINE_LOOP); for (int j = 0; j < 3; j++) { if (mode != EditVert) { if (!model->cm.face[i].IsUserBit(selBit)) glVertex3f(model->cm.face[i].WT(j).u() * AREADIM , AREADIM - (model->cm.face[i].WT(j).v() * AREADIM), 1); else if (editMode == Scale) glVertex3f((oScale.x() + (model->cm.face[i].WT(j).u() - oScale.x()) * scaleX) * AREADIM - panX/zoom, AREADIM - ((oScale.y() + (model->cm.face[i].WT(j).v() - oScale.y()) * scaleY) * AREADIM) - panY/zoom, 1); else glVertex3f((origin.x() + (cos(degree) * (model->cm.face[i].WT(j).u() - origin.x()) - sin(degree) * (model->cm.face[i].WT(j).v() - origin.y()))) * AREADIM - panX/zoom, AREADIM - ((origin.y() + (sin(degree) * (model->cm.face[i].WT(j).u() - origin.x()) + cos(degree) * (model->cm.face[i].WT(j).v() - origin.y()))) * AREADIM) - panY/zoom, 1); } else { if (areaUV.contains(QPointF(model->cm.face[i].WT(j).u(), model->cm.face[i].WT(j).v())) && model->cm.face[i].V(j)->IsUserBit(selVertBit) && !isInside(&model->cm.face[i])) glVertex3f((origin.x() + (cos(degree) * (model->cm.face[i].WT(j).u() - origin.x()) - sin(degree) * (model->cm.face[i].WT(j).v() - origin.y()))) * AREADIM - posVX/zoom, AREADIM - ((origin.y() + (sin(degree) * (model->cm.face[i].WT(j).u() - origin.x()) + cos(degree) * (model->cm.face[i].WT(j).v() - origin.y()))) * AREADIM) - posVY/zoom, 1); else glVertex3f(model->cm.face[i].WT(j).u() * AREADIM , AREADIM - (model->cm.face[i].WT(j).v() * AREADIM), 1); } } glEnd(); } void RenderArea::drawUnifyVertexes() { glDisable(GL_COLOR_LOGIC_OP); glColor3f(1,0,0); if (uvertA != QPoint()) DrawCircle(QPoint((origin.x() + (cos(degree) * (tua - origin.x()) - sin(degree) * (tva - origin.y()))) * AREADIM - posVX/zoom, AREADIM - ((origin.y() + (sin(degree) * (tua - origin.x()) + cos(degree) * (tva - origin.y()))) * AREADIM) - posVY/zoom)); if (uvertB != QPoint()) DrawCircle(QPoint((origin.x() + (cos(degree) * (tub - origin.x()) - sin(degree) * (tvb - origin.y()))) * AREADIM - posVX/zoom, AREADIM - ((origin.y() + (sin(degree) * (tub - origin.x()) + cos(degree) * (tvb - origin.y()))) * AREADIM) - posVY/zoom)); if (uvertA1 != QPoint()) DrawCircle(QPoint((origin.x() + (cos(degree) * (tua1 - origin.x()) - sin(degree) * (tva1 - origin.y()))) * AREADIM - posVX/zoom, AREADIM - ((origin.y() + (sin(degree) * (tua1 - origin.x()) + cos(degree) * (tva1 - origin.y()))) * AREADIM) - posVY/zoom)); if (uvertB1 != QPoint()) DrawCircle(QPoint((origin.x() + (cos(degree) * (tub1 - origin.x()) - sin(degree) * (tvb1 - origin.y()))) * AREADIM - posVX/zoom, AREADIM - ((origin.y() + (sin(degree) * (tub1 - origin.x()) + cos(degree) * (tvb1 - origin.y()))) * AREADIM) - posVY/zoom)); glColor3f(0,0,0); glEnable(GL_COLOR_LOGIC_OP); } void RenderArea::drawBackground() { glColor3f(1,1,1); glBindTexture(GL_TEXTURE_2D, id); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image.bits()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); gluBuild2DMipmaps(GL_TEXTURE_2D, 3, image.width(), image.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.bits()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glEnable(GL_TEXTURE_2D); for (int x = minX; x < maxX; x++) { for (int y = minY; y < maxY; y++) { glBegin(GL_QUADS); glTexCoord2f(0.0,0.0); glVertex3f(0.0 + x*AREADIM,0.0 - y*AREADIM,0.0); glTexCoord2f(1.0,0.0); glVertex3f(1.0*AREADIM + x*AREADIM,0.0 - y*AREADIM,0.0); glTexCoord2f(1.0,1.0); glVertex3f(1.0*AREADIM + x*AREADIM,1.0*AREADIM - y*AREADIM,0.0); glTexCoord2f(0.0,1.0); glVertex3f(0.0 + x*AREADIM,1.0*AREADIM - y*AREADIM,0.0); glEnd(); } } glDisable(GL_TEXTURE_2D); } void RenderArea::drawAxis(QPainter* painter) { // Line and text (native Qt) int w = this->visibleRegion().boundingRect().width(); int h = this->visibleRegion().boundingRect().height(); painter->drawLine(0,h,w,h); painter->drawLine(0,h,0,0); // Calculate the coords and draw it float ox = (float)-viewport.X()*zoom/(AREADIM*zoom), oy = (float)(AREADIM*zoom - h + viewport.Y()*zoom)/(AREADIM*zoom); /*O:*/painter->drawText(TRANSLATE, h - TRANSLATE, QString("(%1,%2)").arg(ox).arg(oy)); /*Y:*/painter->drawText(TRANSLATE, TRANSLATE*3, QString("(%1,%2)").arg(ox).arg((float)(AREADIM*zoom + viewport.Y()*zoom)/(AREADIM*zoom))); /*X:*/painter->drawText(w - TRANSLATE*18, h - TRANSLATE, QString("(%1,%2)").arg((float)(w-viewport.X()*zoom)/(AREADIM*zoom)).arg(oy)); painter->drawText(TRANSLATE, TRANSLATE*6, QString("V")); painter->drawText(w - TRANSLATE*23, h - TRANSLATE, QString("U")); } void RenderArea::drawSelectionRectangle(QPainter *painter) { if (start != QPoint() && end != QPoint()) { painter->setPen(QPen(QBrush(Qt::gray),1)); painter->setBrush(QBrush(Qt::NoBrush)); painter->drawRect(area); } } void RenderArea::drawEditRectangle(QPainter *painter) { if (selection != QRect() && (mode == Edit || mode == EditVert)) { // The rectangle of editing painter->setPen(QPen(QBrush(Qt::yellow),2)); painter->setBrush(QBrush(Qt::NoBrush)); if (mode == Edit) painter->drawRect(QRect(selection.x() - posX, selection.y() - posY, selection.width(), selection.height())); else painter->drawRect(QRect(selection.x() - posVX, selection.y() - posVY, selection.width(), selection.height())); if (mode == Edit || (mode == EditVert && VCount > 1)) { // Rectangle on the corner painter->setPen(QPen(QBrush(Qt::black),1)); for (unsigned l = 0; l < selRect.size(); l++) { if (l == highlighted) painter->setBrush(QBrush(Qt::yellow)); else painter->setBrush(QBrush(Qt::NoBrush)); painter->drawRect(selRect[l]); if (editMode == Scale && mode == Edit) painter->drawImage(selRect[l],scal,QRect(0,0,scal.width(),scal.height())); else painter->drawImage(selRect[l],rot,QRect(0,0,rot.width(),rot.height())); } // and the origin of the rotation if ((editMode == Rotate && mode == Edit) || mode == EditVert) { painter->setPen(QPen(QBrush(Qt::black),1)); if (highlighted == ORIGINRECT) painter->setBrush(QBrush(Qt::blue)); else painter->setBrush(QBrush(Qt::yellow)); if (mode == Edit) painter->drawEllipse(QRect(originR.x() - posX - orX, originR.y() - posY - orY, RADIUS, RADIUS)); else painter->drawEllipse(QRect(originR.x() - posVX - orX, originR.y() - posVY - orY, RADIUS, RADIUS)); } } } } void RenderArea::drawUnifyRectangles(QPainter *painter) { if (unifyRA != QRect()) { painter->setPen(QPen(QBrush(Qt::blue),1)); painter->setBrush(QBrush(Qt::NoBrush)); painter->drawRect(unifyRA); painter->drawText(unifyRA.center().x() - RADIUS*2, unifyRA.center().y(), tr("A")); } if (unifyRB != QRect()) { painter->drawRect(unifyRB); painter->drawText(unifyRB.center().x() - RADIUS*2, unifyRB.center().y(), tr("B")); } if (unifyRA != QRect() && unifyRB != QRect()) painter->drawLine(unifyRA.center(), unifyRB.center()); if (unifyRA1 != QRect()) { painter->drawRect(unifyRA1); painter->drawText(unifyRA1.center().x() - RADIUS*2, unifyRA1.center().y(), tr("A'")); } if (unifyRB1 != QRect()) { painter->drawRect(unifyRB1); painter->drawText(unifyRB1.center().x() - RADIUS*2, unifyRB1.center().y(), tr("B'")); } if (unifyRA1 != QRect() && unifyRB1 != QRect()) painter->drawLine(unifyRA1.center(), unifyRB1.center()); if (drawP) { painter->setPen(QPen(QBrush(Qt::red),2)); for (unsigned k = 0; k < drawnPath.size()-1; k++) painter->drawLine(ToScreenSpace(drawnPath[k].X(), drawnPath[k].Y()), ToScreenSpace(drawnPath[k+1].X(),drawnPath[k+1].Y())); } if (drawP1) { for (unsigned k = 0; k < drawnPath1.size()-1; k++) painter->drawLine(ToScreenSpace(drawnPath1[k].X(), drawnPath1[k].Y()), ToScreenSpace(drawnPath1[k+1].X(),drawnPath1[k+1].Y())); } } // Mouse Event: void RenderArea::mousePressEvent(QMouseEvent *e) { if((e->buttons() & Qt::LeftButton)) { if ((mode == Edit || mode == EditVert) && highlighted == NOSEL) { this->ChangeMode(SPECIALMODE); pressed = NOSEL; selected = false; selectedV = false; selVertBit = CVertexO::NewBitFlag(); for (unsigned i = 0; i < model->cm.face.size(); i++) model->cm.face[i].ClearUserBit(selBit); } switch(mode) { case View: handlePressView(e); break; case Edit: handlePressEdit(e); break; case EditVert: handlePressEdit(e); break; case Select: handlePressSelect(e); break; case UnifyVert: handlePressSelect(e); break; } } else if((e->buttons() & Qt::MidButton)) { // Pan oldMode = mode; this->ChangeMode(VIEWMODE); oldX = e->x(); oldY = e->y(); tmpX = viewport.X(); tmpY = viewport.Y(); tb->MouseDown(e->x(), AREADIM-e->y(), QT2VCG(e->button(), e->modifiers())); this->update(); } } void RenderArea::handlePressView(QMouseEvent *e) { oldX = e->x(); oldY = e->y(); tmpX = viewport.X(); tmpY = viewport.Y(); tb->MouseDown(e->x(), AREADIM-e->y(), QT2VCG(e->button(), e->modifiers())); this->update(); } void RenderArea::handlePressSelect(QMouseEvent *e) { start = e->pos(); end = e->pos(); } void RenderArea::handlePressEdit(QMouseEvent *e) { tpanX = e->x(); tpanY = e->y(); pressed = highlighted; if (highlighted > NOSEL && highlighted < selRect.size()) { rectX = selRect[highlighted].center().x(); rectY = selRect[highlighted].center().y(); if (mode == Edit) { oldSRX = selection.width(); oldSRY = selection.height(); scaleX = 1; scaleY = 1; } int perno; if (highlighted == 0) perno = 3; else if (highlighted == 1) perno = 2; else if (highlighted == 2) perno = 1; else perno = 0; oScale = ToUVSpace(selRect[perno].center().x(), selRect[perno].center().y()); B2 = (float)(rectX - originR.center().x())*(rectX - originR.center().x()) + (rectY - originR.center().y())*(rectY - originR.center().y()); Rm = (float)(rectY - originR.center().y()) / (rectX - originR.center().x()); Rq = (float) rectY - Rm * rectX; } } void RenderArea::mouseReleaseEvent(QMouseEvent *e) { switch(mode) { case View: handleReleaseView(e); break; case Edit: handleReleaseEdit(e); break; case EditVert: handleReleaseEdit(e); break; case Select: handleReleaseSelect(e); break; case UnifyVert: area = QRect(); locked = false; if (unifyRB1 != QRect()) UnifySet(); this->update(); break; } } void RenderArea::handleReleaseView(QMouseEvent *e) { if (selection != QRect()) { UpdateSelectionArea((viewport.X() - initVX)*zoom, (viewport.Y() - initVY)*zoom); originR.moveCenter(QPoint(originR.x() + (viewport.X() - initVX)*zoom, originR.y() + (viewport.Y() - initVY)*zoom)); origin = ToUVSpace(originR.center().x(), originR.center().y()); } initVX = viewport.X(); initVY = viewport.Y(); if (oldMode != NoMode) { this->ChangeMode(oldMode); oldMode = NoMode; } } void RenderArea::handleReleaseEdit(QMouseEvent *e) { oldPX = panX; oldPY = panY; if (pressed == ORIGINRECT) // Drag origin -> Update the position of the rectangle of rotation and the real point in UV { originR = QRect(originR.x() - posX - orX, originR.y() - posY - orY, RADIUS, RADIUS); origin = ToUVSpace(originR.center().x(), originR.center().y()); orX = 0; orY = 0; } else if (pressed == SELECTIONRECT) { if (mode == Edit && posX != 0) // Drag selection -> Update the position of the selection area and the rotatation rect { selection = QRect(selection.x() - posX, selection.y() - posY, selection.width(), selection.height()); originR.moveCenter(QPoint(originR.center().x() - posX, originR.center().y() - posY)); origin = ToUVSpace(originR.center().x(), originR.center().y()); posX = 0; posY = 0; if (selected) UpdateUV(); } else if (mode == EditVert && posVX != 0) { selection = QRect(selection.x() - posVX, selection.y() - posVY, selection.width(), selection.height()); originR.moveCenter(QPoint(originR.center().x() - posVX, originR.center().y() - posVY)); origin = ToUVSpace(originR.center().x(), originR.center().y()); if (selectedV) UpdateVertex(); } } else if (pressed > NOSEL && pressed < selRect.size()) { if (editMode == Rotate && mode == Edit) { RotateComponent(degree); RecalculateSelectionArea(); degree = 0; } else if (scaleX != 1 && scaleY != 1 && mode == Edit) { ScaleComponent(scaleX, scaleY); RecalculateSelectionArea(); scaleX = 1; scaleY = 1; oScale = QPointF(0,0); } else if (mode == EditVert) { selStart = QPoint(MAX,MAX); selEnd = QPoint(-MAX,-MAX); RotateComponent(degree); selection = QRect(QPoint(selStart.x() - VRADIUS, selStart.y() - VRADIUS), QPoint(selEnd.x() + VRADIUS, selEnd.y() + VRADIUS)); QPointF a = ToUVSpace(selection.x(), selection.y()); QPointF b = ToUVSpace(selection.bottomRight().x(),selection.bottomRight().y()); areaUV = QRectF(a, QSizeF(b.x()-a.x(), b.y()-a.y())); UpdateSelectionAreaV(0,0); degree = 0; } } } void RenderArea::handleReleaseSelect(QMouseEvent *e) { start = QPoint(); end = QPoint(); area = QRect(); switch (selectMode) { case Area: if (selected) { selection = QRect(selStart, selEnd); UpdateSelectionArea(0,0); origin = ToUVSpace(selection.center().x(), selection.center().y()); originR = QRect(selection.center().x()-RADIUS/2, selection.center().y()-RADIUS/2, RADIUS, RADIUS); this->ChangeMode(EDITFACEMODE); this->ShowFaces(); } break; case Connected: SelectConnectedComponent(e->pos()); if (selected) { selection = QRect(selStart, selEnd); UpdateSelectionArea(0,0); RecalculateSelectionArea(); origin = ToUVSpace(selection.center().x(), selection.center().y()); originR = QRect(selection.center().x()-RADIUS/2, selection.center().y()-RADIUS/2, RADIUS, RADIUS); this->ChangeMode(EDITFACEMODE); this->ShowFaces(); } break; case Vertex: if (selectedV) { CountVertexes(); selection = QRect(QPoint(selStart.x() - RADIUS/2, selStart.y() - RADIUS/2), QPoint(selEnd.x() + RADIUS/2, selEnd.y() + RADIUS/2)); if (VCount > 1) { UpdateSelectionAreaV(0,0); origin = ToUVSpace(selection.center().x(), selection.center().y()); originR = QRect(selection.center().x()-RADIUS/2, selection.center().y()-RADIUS/2, RADIUS, RADIUS); } this->ChangeMode(EDITVERTEXMODE); } break; } this->update(); } void RenderArea::mouseMoveEvent(QMouseEvent *e) { int sx = (e->x() - oldX)/zoom, sy = (e->y() - oldY)/zoom; if((e->buttons() & Qt::LeftButton) && image != QImage()) { if (mode == View) { tb->Translate(Point3f(- oldX + e->x(), - oldY + e->y(), zoom)); viewport = Point2f(tmpX + sx, tmpY + sy); this->update(); } else if (mode == Edit || mode == EditVert) handleMoveEdit(e); else if (mode == Select || mode == UnifyVert) handleMoveSelect(e); } else if((e->buttons() & Qt::MidButton) && image != QImage()) // Pan { tb->Translate(Point3f(- oldX + e->x(), - oldY + e->y(), zoom)); viewport = Point2f(tmpX + sx, tmpY + sy); this->update(); } else if (image != QImage())// No click { if (mode == Edit || mode == EditVert) { for (unsigned y = 0; y < selRect.size(); y++) { if (selRect[y].contains(e->pos()) && (mode == Edit || (mode == EditVert && VCount > 1))) { if (highlighted != y) this->update(selRect[y]); highlighted = y; return; } } if (originR.contains(e->pos()) && ((mode == Edit && editMode == Rotate) || mode == EditVert)) { if (highlighted != ORIGINRECT) this->update(originR); highlighted = ORIGINRECT; return; } if (selection.contains(e->pos())) { if (highlighted == ORIGINRECT) this->update(originR); else if (highlighted < selRect.size()) this->update(selRect[highlighted]); highlighted = SELECTIONRECT; return; } if (highlighted != NOSEL) { if (highlighted == ORIGINRECT) this->update(originR); else if (highlighted < selRect.size()) this->update(selRect[highlighted]); } highlighted = NOSEL; } } } void RenderArea::handleMoveEdit(QMouseEvent *e) { int tX = tpanX - e->x(); int tY = tpanY - e->y(); if (pressed == SELECTIONRECT) { // Move the selection ara if (mode == Edit) { panX = oldPX + tpanX - e->x(); panY = oldPY + tpanY - e->y(); } if (tX != 0 || tY != 0) { QPoint ta = QPoint(selection.topLeft().x() - tX, selection.topLeft().y() - tY); QPoint tb = QPoint(selection.topRight().x() - tX, selection.topRight().y() - tY); QPoint tc = QPoint(selection.bottomLeft().x() - tX, selection.bottomLeft().y() - tY); QPoint td = QPoint(selection.bottomRight().x() - tX, selection.bottomRight().y() - tY); if (mode == Edit) { posX = tX; posY = tY; selRect[0].moveCenter(ta); selRect[1].moveCenter(tb); selRect[2].moveCenter(tc); selRect[3].moveCenter(td); } else { posVX = tX; posVY = tY; selRect[0].moveBottomRight(ta); selRect[1].moveBottomLeft(tb); selRect[2].moveTopRight(tc); selRect[3].moveTopLeft(td); } } } else if (pressed == ORIGINRECT) { orX = tX; orY = tY; this->update(originR); } else if (pressed > NOSEL && pressed < selRect.size()) { if (editMode == Scale && mode == Edit) HandleScale(e->pos()); else HandleRotate(e->pos()); } this->update(); } void RenderArea::handleMoveSelect(QMouseEvent *e) { if ((mode == Select && selectMode != Connected) || mode == UnifyVert) { end = e->pos(); int x1, x2, y1, y2; if (start.x() < end.x()) {x1 = start.x(); x2 = end.x();} else {x1 = end.x(); x2 = start.x();} if (start.y() < end.y()) {y1 = start.y(); y2 = end.y();} else {y1 = end.y(); y2 = start.y();} area = QRect(x1,y1,x2-x1,y2-y1); if (mode == Select) { if (selectMode == Area) SelectFaces(); else SelectVertexes(); } else if (!locked) SelectVertexes(); this->update(); } } void RenderArea::mouseDoubleClickEvent(QMouseEvent *e) { switch(mode) { case View: // Center the screen on the mouse click position viewport = Point2f(viewport.X() - e->x()/zoom + (this->visibleRegion().boundingRect().width()/zoom)/2, viewport.Y() - e->y()/zoom + (this->visibleRegion().boundingRect().height()/zoom)/2); oldX = 0; oldY = 0; tb->track.SetTranslate(Point3f(viewport.X(), viewport.Y(), 1)); tb->Scale(zoom); this->update(); break; case Edit: // Change edit mode if (selection.contains(e->pos())) { if (editMode == Rotate) editMode = Scale; else editMode = Rotate; this->update(); } break; } } void RenderArea::wheelEvent(QWheelEvent*e) { if (image != QImage()) { // Handle the zoom for any mode int cwx = viewport.X() - (this->visibleRegion().boundingRect().width()/zoom)/2; int cwy = viewport.Y() - (this->visibleRegion().boundingRect().height()/zoom)/2; if (e->delta() > 0) zoom /= 0.75; else zoom *= 0.75; // Change the viewport, putting the center of the screen on the mouseposition cwx += (this->visibleRegion().boundingRect().width()/zoom)/2; cwy += (this->visibleRegion().boundingRect().height()/zoom)/2; viewport = Point2f(cwx, cwy); ResetTrack(false); tb->Scale(zoom); if (selectedV) { if (mode == UnifyVert) UpdateUnify(); else UpdateVertexSelection(); } else if (selected) RecalculateSelectionArea(); originR.moveCenter(ToScreenSpace(origin.x(), origin.y())); initVX = viewport.X(); initVY = viewport.Y(); this->update(); } } void RenderArea::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_H) ResetPosition(); else e->ignore(); } // Remap the uv coord out of border using clamp method void RenderArea::RemapClamp() { for (unsigned i = 0; i < model->cm.face.size(); i++) { if (model->cm.face[i].WT(0).n() == textNum && !model->cm.face[i].IsD()) { model->cm.face[i].ClearUserBit(selBit); for (unsigned j = 0; j < 3; j++) { if (model->cm.face[i].WT(j).u() > 1) model->cm.face[i].WT(j).u() = 1; else if (model->cm.face[i].WT(j).u() < 0) model->cm.face[i].WT(j).u() = 0; if (model->cm.face[i].WT(j).v() > 1) model->cm.face[i].WT(j).v() = 1; else if (model->cm.face[i].WT(j).v() < 0) model->cm.face[i].WT(j).v() = 0; } } } panX = 0; panY = 0; tpanX = 0; tpanY = 0; oldPX = 0; oldPY = 0; ResetTrack(true); vcg::tri::UpdateTopology::FaceFaceFromTexCoord(model->cm); selection = QRect(); this->update(); emit UpdateModel(); } // Remap the uv coord out of border using mod function void RenderArea::RemapMod() { for (unsigned i = 0; i < model->cm.face.size(); i++) { if (model->cm.face[i].WT(0).n() == textNum && !model->cm.face[i].IsD()) { model->cm.face[i].ClearUserBit(selBit); for (unsigned j = 0; j < 3; j++) { float u = model->cm.face[i].WT(j).u(); float v = model->cm.face[i].WT(j).v(); if (u < 0) u = u + (int)u + 1; else if (u > 1) u = u - (int)u; if (v < 0) v = v + (int)v + 1; else if (v > 1) v = v - (int)v; model->cm.face[i].WT(j).u() = u; model->cm.face[i].WT(j).v() = v; } } } panX = 0; panY = 0; tpanX = 0; tpanY = 0; oldPX = 0; oldPY = 0; ResetTrack(true); vcg::tri::UpdateTopology::FaceFaceFromTexCoord(model->cm); selection = QRect(); this->update(); emit UpdateModel(); } // Change the tool's action mode void RenderArea::ChangeMode(int modenumber) { if (mode == UnifyVert && modenumber != UNIFYMODE) { resetUnifyData(); drawnPath.clear(); drawnPath1.clear(); drawP = false; drawP1 = false; } switch(modenumber) { case VIEWMODE: if (mode != View) { mode = View; this->setCursor(Qt::PointingHandCursor); } break; case EDITFACEMODE: if (mode != Edit) { mode = Edit; highlighted = SELECTIONRECT; this->setCursor(Qt::SizeAllCursor); } break; case SELECTMODE: if (mode != Select) { if (selection != QRect()) { if (selectMode == Vertex) { mode = EditVert; selectedV = true; UpdateSelectionAreaV(0,0); for (unsigned i = 0; i < model->cm.face.size(); i++) model->cm.face[i].ClearUserBit(selBit); } else { mode = Edit; selected = true; for (unsigned i = 0; i < model->cm.vert.size(); i++) model->cm.vert[i].ClearUserBit(selVertBit); } this->setCursor(Qt::SizeAllCursor); } else { mode = Select; for (unsigned i = 0; i < model->cm.face.size(); i++) { model->cm.face[i].ClearUserBit(selBit); model->cm.face[i].ClearS(); } for (unsigned i = 0; i < model->cm.vert.size(); i++) model->cm.vert[i].ClearUserBit(selVertBit); emit UpdateModel(); this->setCursor(Qt::CrossCursor); } } break; case SPECIALMODE: // For internal use... reset the selection mode = Select; for (unsigned i = 0; i < model->cm.face.size(); i++) { model->cm.face[i].ClearUserBit(selBit); model->cm.face[i].ClearS(); } selection = QRect(); this->setCursor(Qt::CrossCursor); emit UpdateModel(); break; case EDITVERTEXMODE: if (mode != EditVert) { if (selection != QRect()) { mode = EditVert; selectedV = true; this->setCursor(Qt::SizeAllCursor); } else { mode = Select; this->setCursor(Qt::CrossCursor); } } break; case UNIFYMODE: if (mode != UnifyVert) { mode = UnifyVert; resetUnifyData(); if (selected) {for (unsigned i = 0; i < model->cm.face.size(); i++) model->cm.face[i].ClearS();} selection = QRect(); selected = false; selectedV = false; for (unsigned i = 0; i < model->cm.face.size(); i++) model->cm.face[i].ClearUserBit(selBit); selVertBit = CVertexO::NewBitFlag(); this->setCursor(Qt::CrossCursor); model->updateDataMask(MeshModel::MM_VERTFACETOPO | MeshModel::MM_FACEFACETOPO); //model->cm.face.EnableVFAdjacency(); //model->cm.vert.EnableVFAdjacency(); //model->cm.face.EnableFFAdjacency(); UpdateUnifyTopology(); } break; } this->update(); } void RenderArea::resetUnifyData() { unifyRA = QRect(); unifyRA1 = QRect(); unifyRB = QRect(); unifyRB1 = QRect(); uvertA = QPoint(); uvertA1 = QPoint(); uvertB = QPoint(); uvertB1 = QPoint(); } // Change the selection function void RenderArea::ChangeSelectMode(int selectindex) { switch(selectindex) { case SELECTAREA: if (selectMode != Area && selectMode != Connected) selection = QRect(); selectMode = Area; break; case SELECTCONNECTED: if (selectMode != Connected && selectMode != Area) selection = QRect(); selectMode = Connected; break; case SELECTVERTEX: if (selectMode != Vertex) selection = QRect(); selectMode = Vertex; break; default: selectMode = Area; break; } if (selectedV && selectMode != Vertex) { areaUV = QRectF(); selVertBit = CVertexO::NewBitFlag(); selectedV = false; } if (selected && selectMode == Vertex) { selected = false; //selBit = CFaceO::NewBitFlag(); for (unsigned i = 0; i < model->cm.face.size(); i++) model->cm.face[i].ClearUserBit(selBit); for (unsigned i = 0; i < model->cm.face.size(); i++) model->cm.face[i].ClearS(); emit UpdateModel(); } } void RenderArea::RotateComponent(float theta) { // Calcolate the new position of the vertex of the selected component after a rotation. // The rotation is done around the selected point if (origin != QPoint()) { float sinv = sin(theta); float cosv = cos(theta); if (selected) { for (unsigned i = 0; i < model->cm.face.size(); i++) { if (model->cm.face[i].WT(0).n() == textNum && !model->cm.face[i].IsD() && (!selected || (selected && model->cm.face[i].IsUserBit(selBit)))) for (unsigned j = 0; j < 3; j++) { float u = origin.x() + (cosv * (model->cm.face[i].WT(j).u() - origin.x()) - sinv * (model->cm.face[i].WT(j).v() - origin.y())); float v = origin.y() + (sinv * (model->cm.face[i].WT(j).u() - origin.x()) + cosv * (model->cm.face[i].WT(j).v() - origin.y())); model->cm.face[i].WT(j).u() = u; model->cm.face[i].WT(j).v() = v; } } } else if (selectedV) { for (unsigned i = 0; i < model->cm.face.size(); i++) { for (unsigned j = 0; j < 3; j++) { if (areaUV.contains(QPointF(model->cm.face[i].WT(j).u(),model->cm.face[i].WT(j).v())) && model->cm.face[i].V(j)->IsUserBit(selVertBit) && !model->cm.face[i].V(j)->IsD()) { float u = origin.x() + (cosv * (model->cm.face[i].WT(j).u() - origin.x()) - sinv * (model->cm.face[i].WT(j).v() - origin.y())); float v = origin.y() + (sinv * (model->cm.face[i].WT(j).u() - origin.x()) + cosv * (model->cm.face[i].WT(j).v() - origin.y())); model->cm.face[i].WT(j).u() = u; model->cm.face[i].WT(j).v() = v; QPoint tmp = ToScreenSpace(u, v); UpdateBoundingArea(tmp,tmp); } } } } this->update(); emit UpdateModel(); } } void RenderArea::ScaleComponent(float percX, float percY) { // Scale the selected component. The origin is set to the clicked point for (unsigned i = 0; i < model->cm.face.size(); i++) { if (model->cm.face[i].WT(0).n() == textNum && selected && model->cm.face[i].IsUserBit(selBit) && !model->cm.face[i].IsD()) for (unsigned j = 0; j < 3; j++) { float x = oScale.x() + (model->cm.face[i].WT(j).u() - oScale.x()) * percX; float y = oScale.y() + (model->cm.face[i].WT(j).v() - oScale.y()) * percY; model->cm.face[i].WT(j).u() = x; model->cm.face[i].WT(j).v() = y; } } this->update(); emit UpdateModel(); } void RenderArea::UpdateUV() { // After a move of component, re-calculate the new UV coordinates for (unsigned i = 0; i < model->cm.face.size(); i++) { if (model->cm.face[i].WT(0).n() == textNum && model->cm.face[i].IsUserBit(selBit) && !model->cm.face[i].IsD()) { for (unsigned j = 0; j < 3; j++) { model->cm.face[i].WT(j).u() = model->cm.face[i].WT(j).u() - (float)panX/(AREADIM*zoom); model->cm.face[i].WT(j).v() = model->cm.face[i].WT(j).v() + (float)panY/(AREADIM*zoom); } } } panX = 0; panY = 0; tpanX = 0; tpanY = 0; oldPX = 0; oldPY = 0; this->update(); emit UpdateModel(); } void RenderArea::UpdateVertex() { // After a move of vertex, re-calculate the new UV coordinates for (unsigned i = 0; i < model->cm.face.size(); i++) { if (!isInside(&model->cm.face[i])) { for (unsigned j = 0; j < 3; j++) { if (areaUV.contains(QPointF(model->cm.face[i].WT(j).u(),model->cm.face[i].WT(j).v())) && model->cm.face[i].V(j)->IsUserBit(selVertBit) && !model->cm.face[i].V(j)->IsD()) { model->cm.face[i].WT(j).u() = model->cm.face[i].WT(j).u() - (float)posVX/(AREADIM*zoom); model->cm.face[i].WT(j).v() = model->cm.face[i].WT(j).v() + (float)posVY/(AREADIM*zoom); } } } } areaUV.moveCenter(QPointF(areaUV.center().x() - (float)posVX/(AREADIM*zoom), areaUV.center().y() + (float)posVY/(AREADIM*zoom))); tpanX = 0; tpanY = 0; posVX = 0; posVY = 0; this->update(); emit UpdateModel(); } void RenderArea::ResetTrack(bool resetViewPort) { // Reset the viewport of the trackball tb->center = Point3f(0, 0, 0); tb->track.SetScale(1); if (resetViewPort) viewport = Point2f(0,0); oldX = 0; oldY = 0; tb->track.SetTranslate(Point3f(viewport.X(), viewport.Y(), 1)); } void RenderArea::SelectFaces() { // Check if a face is inside the rectangle of selection and mark it selStart = QPoint(MAX,MAX); selEnd = QPoint(-MAX,-MAX); selected = false; selection = QRect(); CMeshO::FaceIterator fi; for(fi = model->cm.face.begin(); fi != model->cm.face.end(); ++fi) { if ((*fi).WT(0).n() == textNum && !(*fi).IsD()) { (*fi).ClearUserBit(selBit); QVector t = QVector(); t.push_back(ToScreenSpace((*fi).WT(0).u(), (*fi).WT(0).v())); t.push_back(ToScreenSpace((*fi).WT(1).u(), (*fi).WT(1).v())); t.push_back(ToScreenSpace((*fi).WT(2).u(), (*fi).WT(2).v())); QRegion r = QRegion(QPolygon(t)); if (r.intersects(area)) { (*fi).SetUserBit(selBit); UpdateBoundingArea(r.boundingRect().topLeft(), r.boundingRect().bottomRight()); if (!selected) selected = true; } } } } void RenderArea::SelectVertexes() { // Check if a vertex is inside the rectangle of selection selStart = QPoint(MAX,MAX); selEnd = QPoint(-MAX,-MAX); selectedV = false; selection = QRect(); QPointF a = ToUVSpace(area.x(), area.y()); QPointF b = ToUVSpace(area.bottomRight().x(),area.bottomRight().y()); areaUV = QRectF(a, QSizeF(b.x()-a.x(), b.y()-a.y())); CMeshO::FaceIterator fi; for(fi = model->cm.face.begin(); fi != model->cm.face.end(); ++fi) { if ((*fi).WT(0).n() == textNum && !(*fi).IsD()) { for (int j = 0; j < 3; j++) { QPoint tmp = ToScreenSpace((*fi).WT(j).u(), (*fi).WT(j).v()); if (area.contains(tmp)) { (*fi).V(j)->SetUserBit(selVertBit); UpdateBoundingArea(tmp, tmp); if (!selectedV) selectedV = true; if (mode == UnifyVert && !locked) { locked = true; handleUnifySelection(fi, j); return; } } } } } if (mode != UnifyVert) CheckVertex(); } void RenderArea::CheckVertex() { // Search for unselected vertices in UV Space banList.clear(); CMeshO::FaceIterator fi; for(fi = model->cm.face.begin(); fi != model->cm.face.end(); ++fi) { if ((*fi).WT(0).n() == textNum && !(*fi).IsD()) { bool go = false; for (int j = 0; j < 3; j++) { if ((*fi).V(j)->IsUserBit(selVertBit)) { if (!areaUV.contains(QPointF((*fi).WT(j).u(), (*fi).WT(j).v()))) go = true; else {go = false; break;} } } if (go) banList.push_back(&(*fi)); } } } void RenderArea::handleUnifySelection(CMeshO::FaceIterator fi, int j) { if (unifyRA == QRect()) { unifyRA = QRect(QPoint(selStart.x() - VRADIUS, selStart.y() - VRADIUS), QPoint(selEnd.x() + VRADIUS, selEnd.y() + VRADIUS)); unifyA = (*fi).V(j); firstface = &(*fi); uvertA = ToScreenSpace((*fi).WT(j).u(), (*fi).WT(j).v()); tua = (*fi).WT(j).u(); tva = (*fi).WT(j).v(); drawnPath.clear(); drawnPath.push_back(Point2f(tua,tva)); } else if (unifyRB == QRect()) { unifyRB = QRect(QPoint(selStart.x() - VRADIUS, selStart.y() - VRADIUS), QPoint(selEnd.x() + VRADIUS, selEnd.y() + VRADIUS)); unifyB = (*fi).V(j); uvertB = ToScreenSpace((*fi).WT(j).u(), (*fi).WT(j).v()); tub = (*fi).WT(j).u(); tvb = (*fi).WT(j).v(); path.clear(); if (unifyA->IsB() && unifyB->IsB()) { path = FindPath(unifyA, unifyB, firstface, 0); drawP = true; } this->update(); } else if (unifyRA1 == QRect()) { firstface1 = &(*fi); unifyRA1 = QRect(QPoint(selStart.x() - VRADIUS, selStart.y() - VRADIUS), QPoint(selEnd.x() + VRADIUS, selEnd.y() + VRADIUS)); unifyA1 = (*fi).V(j); uvertA1 = ToScreenSpace((*fi).WT(j).u(), (*fi).WT(j).v()); tua1 = (*fi).WT(j).u(); tva1 = (*fi).WT(j).v(); drawnPath1.clear(); drawnPath1.push_back(Point2f(tua1,tva1)); } else if (unifyRB1 == QRect()) { unifyRB1 = QRect(QPoint(selStart.x() - VRADIUS, selStart.y() - VRADIUS), QPoint(selEnd.x() + VRADIUS, selEnd.y() + VRADIUS)); unifyB1 = (*fi).V(j); uvertB1 = ToScreenSpace((*fi).WT(j).u(), (*fi).WT(j).v()); tub1 = (*fi).WT(j).u(); tvb1 = (*fi).WT(j).v(); path1.clear(); if (unifyA1->IsB() && unifyB1->IsB()) { path1 = FindPath(unifyA1, unifyB1, firstface1, 1); drawP1 = true; } this->update(); } } void RenderArea::SelectConnectedComponent(QPoint e) { // Select a series of faces with the same UV coord on the edge selStart = QPoint(MAX,MAX); selEnd = QPoint(-MAX,-MAX); selected = false; //selBit = CFaceO::NewBitFlag(); for (unsigned i = 0; i < model->cm.face.size(); i++) model->cm.face[i].ClearUserBit(selBit); unsigned index = 0; vector Q = vector(); // Search the clicked face for(unsigned i = 0; i < model->cm.face.size(); i++) { if (model->cm.face[i].WT(0).n() == textNum) { QVector t = QVector(); t.push_back(ToScreenSpace(model->cm.face[i].WT(0).u(), model->cm.face[i].WT(0).v())); t.push_back(ToScreenSpace(model->cm.face[i].WT(1).u(), model->cm.face[i].WT(1).v())); t.push_back(ToScreenSpace(model->cm.face[i].WT(2).u(), model->cm.face[i].WT(2).v())); QRegion r = QRegion(QPolygon(t)); if (r.contains(e)) { Q.push_back(&model->cm.face[i]); model->cm.face[i].SetUserBit(selBit); UpdateBoundingArea(r.boundingRect().topLeft(), r.boundingRect().bottomRight()); selected = true; break; } } } // Select all the adjacentfaces while (index < Q.size()) { for (int j = 0; j < 3; j++) { CFaceO* p = Q[index]->FFp(j); if (p != 0 && !p->IsUserBit(selBit)) { p->SetUserBit(selBit); Q.push_back(p); QPoint tmp = ToScreenSpace(p->WT(j).u(), p->WT(j).v()); UpdateBoundingArea(tmp, tmp); } } index++; } } // Clear every selected faces or vertices void RenderArea::ClearSelection() { //selBit = CFaceO::NewBitFlag(); for (unsigned i = 0; i < model->cm.face.size(); i++) { model->cm.face[i].ClearUserBit(selBit); model->cm.face[i].ClearS(); } selVertBit = CVertexO::NewBitFlag(); selection = QRect(); this->update(); emit UpdateModel(); } // Invert selected faces void RenderArea::InvertSelection() { if (selected) { for (unsigned i = 0; i < model->cm.face.size(); i++) { if (model->cm.face[i].WT(0).n() == textNum) { if (model->cm.face[i].IsUserBit(selBit)) model->cm.face[i].ClearUserBit(selBit); else model->cm.face[i].SetUserBit(selBit); } } RecalculateSelectionArea(); originR.moveCenter(selection.center()); origin = ToUVSpace(originR.center().x(), originR.center().y()); this->update(); ShowFaces(); } else if (selectedV) { for (unsigned i = 0; i < model->cm.vert.size(); i++) { if (model->cm.vert[i].IsUserBit(selVertBit)) model->cm.vert[i].ClearUserBit(selVertBit); else model->cm.vert[i].SetUserBit(selVertBit); } UpdateVertexSelection(); this->update(); } } // Flip the selected faces (mode = true -> horizontal, mode = false -> vertical) void RenderArea::Flip(bool mode) { if (selected) { QPointF mid = ToUVSpace(selection.center().x(), selection.center().y()); for (unsigned i = 0; i < model->cm.face.size(); i++) { if (model->cm.face[i].WT(0).n() == textNum) { if (model->cm.face[i].IsUserBit(selBit)) { for (int j = 0; j < 3; j++) { if (mode) model->cm.face[i].WT(j).u() = 2.0f * mid.x() - model->cm.face[i].WT(j).u(); else model->cm.face[i].WT(j).v() = 2.0f * mid.y() - model->cm.face[i].WT(j).v(); } } } } RecalculateSelectionArea(); this->update(); } } // Collapse a couple of vertices void RenderArea::UnifyCouple() { // Calculate the average coordinates and unify a couple of vertices if (VCount == 2) { float tu = (vc1.u() + vc2.u())/2.0; float tv = (vc1.v() + vc2.v())/2.0; for (unsigned i = 0; i < model->cm.face.size(); i++) { for (unsigned j = 0; j < 3; j++) { if (!isInside(&model->cm.face[i]) && (model->cm.face[i].V(j) == collapse1 || model->cm.face[i].V(j) == collapse2)) { model->cm.face[i].WT(j).u() = tu; model->cm.face[i].WT(j).v() = tv; } } } selectedV = false; selVertBit = CVertexO::NewBitFlag(); areaUV = QRectF(); selection = QRect(); this->ChangeMode(SELECTMODE); this->update(); emit UpdateModel(); } } // Unify a set of border vertices among the selected path void RenderArea::UnifySet() { // Unify a set of vertices if (path.size() == path1.size() && drawP && drawP1) { for (unsigned i = 0; i < path.size(); i++) { float mu = (drawnPath[i].X() + drawnPath1[i].X())/2.0; float mv = (drawnPath[i].Y() + drawnPath1[i].Y())/2.0; int n = path[i]->VFi(); CFaceO *next = path[i]->VFp(); while (n != -1) { if (drawnPath[i].X() == next->WT(n).u() && drawnPath[i].Y() == next->WT(n).v()) { next->WT(n).u() = mu; next->WT(n).v() = mv; } CFaceO* previous = next; next = next->VFp(n); n = previous->VFi(n); if (next == 0) break; } int n1 = path1[i]->VFi(); CFaceO *next1 = path1[i]->VFp(); while (n1 != -1) { if (drawnPath1[i].X() == next1->WT(n1).u() && drawnPath1[i].Y() == next1->WT(n1).v()) { next1->WT(n1).u() = mu; next1->WT(n1).v() = mv; } CFaceO* previus1 = next1; next1 = next1->VFp(n1); n1 = previus1->VFi(n1); if (next1 == 0) break; } } UpdateUnifyTopology(); } selectedV = false; for (unsigned i = 0; i < model->cm.vert.size(); i++) model->cm.vert[i].ClearUserBit(selVertBit); areaUV = QRectF(); selection = QRect(); unifyA = 0; unifyB = 0; unifyA1 = 0; unifyB1 = 0; firstface = 0; firstface1 = 0; drawP = false; drawP1 = false; uvertA = QPoint(); uvertA1 = QPoint(); uvertB = QPoint(); uvertB1 = QPoint(); unifyRA = QRect(); unifyRA1 = QRect(); unifyRB = QRect(); unifyRB1 = QRect(); this->update(); emit UpdateModel(); } vector RenderArea::FindPath(CVertexO* begin, CVertexO* end, CFaceO* first, int pathN) { // Find a path from the selected vertex, walking from LEFT TO RIGHT only on border edge... vector path = vector(); unsigned index = 0; vector Q = vector(); Q.push_back(first); path.push_back(begin); first->SetV(); bool finish = false; int notcontrol = -1; int lastadd = 0, lastex = -1; /* Add all the adjacent faces to the vertex. For each face in Stack Q if the edge is a border one and contains the last added vertex and is on the right enqueue all adjacent faces to the other vertex add the other vertex to the path */ float tu, tv; if (pathN == 0) {tu = tua; tv = tva;} else {tu = tua1; tv = tva1;} CFaceO* nextb, *previusb; int nb = begin->VFi(); nextb = begin->VFp(), previusb = first; while (nb != -1) { if (tu == nextb->WT(nb).u() && tv == nextb->WT(nb).v() && nextb != first) Q.push_back(nextb); previusb = nextb; nextb = nextb->VFp(nb); nb = previusb->VFi(nb); if (nextb == 0) break; } bool verso; if (pathN == 0) { if (tua < tub) verso = true; else verso = false; } else { if (tua1 < tub1) verso = true; else verso = false; } if (begin->IsB() && end->IsB()) while (index < Q.size()) { bool excluded = false; int oldsize = Q.size(); CFaceO* p = Q[index]; float oldu; if (pathN == 0) oldu = tua; else oldu = tua1; if (!p->IsV() || (p->IsV() && index == 0)) { for (int j = 0; j < 3; j++) { if (p->IsB(j) && j != notcontrol) { notcontrol = j; CVertexO *tmp1 = p->V(j); CVertexO *tmp2 = p->V((j+1)%3); bool added = false; int n, oldn; CFaceO *next, *previous; float tu, tv; if (tmp1 == path[path.size()-1] && ((verso && p->WT(j).u() >= oldu) || (!verso && p->WT(j).u() <= oldu))) { oldu = p->WT(j).u(); path.push_back(tmp2); if (pathN == 0) drawnPath.push_back(Point2f(p->WT((j+1)%3).u(),p->WT((j+1)%3).v())); else drawnPath1.push_back(Point2f(p->WT((j+1)%3).u(),p->WT((j+1)%3).v())); if (tmp2 == end) {finish = true; break;} n = tmp2->VFi(), oldn = (j+1)%3; next = tmp2->VFp(), previous = p; added = true; lastadd = index; lastex = j; } else if (tmp2 == path[path.size()-1] && ((verso && p->WT(j).u() >= oldu) || (!verso && p->WT(j).u() <= oldu))) { oldu = p->WT(j).u(); path.push_back(tmp1); if (pathN == 0) drawnPath.push_back(Point2f(p->WT(j).u(),p->WT(j).v())); else drawnPath1.push_back(Point2f(p->WT(j).u(),p->WT(j).v())); if (tmp1 == end) {finish = true; break;} n = tmp1->VFi(), oldn = j; next = tmp1->VFp(), previous = p; added = true; lastadd = index; lastex = j; } if (added) { index = Q.size()-1; tu = p->WT(oldn).u(); tv = p->WT(oldn).v(); while (n != -1) { if (tu == next->WT(n).u() && tv == next->WT(n).v() && next != p && !next->IsV()) Q.push_back(next); previous = next; oldn = n; next = next->VFp(n); n = previous->VFi(n); if (next == 0) break; } break; } else excluded = true; } } p->SetV(); if (!p->IsB(0) && !p->IsB(1) && !p->IsB(2) && index != 0) excluded = true; } if (finish) break; // casi degeneri... if (oldsize == Q.size() && !excluded) { if (index == Q.size()-1) {index = lastadd; p->ClearV();} // Force to search again the same face... } else notcontrol = -1; index++; if (index == Q.size() && end != path[path.size()-1]) { index = lastadd; notcontrol = lastex; Q[index]->ClearV(); } } for (unsigned i = 0; i < model->cm.face.size(); i++) model->cm.face[i].ClearV(); return path; } void RenderArea::HandleScale(QPoint e) { // Move the rectangle of scaling and resize the selection area int tx = rectX - tpanX + e.x(); int ty = rectY - tpanY + e.y(); switch(highlighted) { case 0: // Top Left if (tx > selection.x() + selection.width() - RECTDIM) tx = selection.x() + selection.width() - RECTDIM; if (ty > selection.y() + selection.height() - RECTDIM) ty = selection.y() + selection.height() - RECTDIM; selRect[0].moveCenter(QPoint(tx, ty)); selRect[2] = QRect(tx - RECTDIM/2, selRect[2].y(), RECTDIM, RECTDIM); selRect[1] = QRect(selRect[1].x(), ty - RECTDIM/2, RECTDIM, RECTDIM); selection.setTopLeft(selRect[0].center()); selection.setBottomLeft(selRect[2].center()); selection.setTopRight(selRect[1].center()); this->update(selRect[1]); break; case 1: // Top Right if (tx < selection.x() + RECTDIM) tx = selection.x() + RECTDIM; if (ty > selection.y() + selection.height() - RECTDIM) ty = selection.y() + selection.height() - RECTDIM; selRect[1].moveCenter(QPoint(tx, ty)); selRect[3] = QRect(tx - RECTDIM/2, selRect[3].y(), RECTDIM, RECTDIM); selRect[0] = QRect(selRect[0].x(), ty - RECTDIM/2, RECTDIM, RECTDIM); selection.setTopRight(selRect[1].center()); selection.setTopLeft(selRect[0].center()); selection.setBottomRight(selRect[3].center()); this->update(selRect[0]); break; case 2: // Bottom Left if (tx > selection.x() + selection.width() - RECTDIM) tx = selection.x() + selection.width() - RECTDIM; if (ty < selection.y() + RECTDIM) ty = selection.y() + RECTDIM; selRect[2].moveCenter(QPoint(tx, ty)); selRect[0] = QRect(tx - RECTDIM/2, selRect[0].y(), RECTDIM, RECTDIM); selRect[3] = QRect(selRect[3].x(), ty - RECTDIM/2, RECTDIM, RECTDIM); selection.setTopLeft(selRect[0].center()); selection.setBottomLeft(selRect[2].center()); selection.setBottomRight(selRect[3].center()); this->update(selRect[3]); break; case 3: // Bottom Right if (tx < selection.x() + RECTDIM) tx = selection.x() + RECTDIM; if (ty < selection.y() + RECTDIM) ty = selection.y() + RECTDIM; selRect[3].moveCenter(QPoint(tx, ty)); selRect[1] = QRect(tx - RECTDIM/2, selRect[1].y(), RECTDIM, RECTDIM); selRect[2] = QRect(selRect[2].x(), ty - RECTDIM/2, RECTDIM, RECTDIM); selection.setTopRight(selRect[1].center()); selection.setBottomLeft(selRect[2].center()); selection.setBottomRight(selRect[3].center()); this->update(selRect[2]); break; } this->update(selRect[highlighted]); this->update(selRect[(highlighted+2)%selRect.size()]); originR.moveCenter(selection.center()); origin = ToUVSpace(originR.center().x(), originR.center().y()); // calculate scaling scaleX = (float)selection.width() / oldSRX; scaleY = (float)selection.height() / oldSRY; this->update(selection); } void RenderArea::HandleRotate(QPoint e) { // Calculate the angle of rotazion float A2 = (e.x() - originR.center().x())*(e.x() - originR.center().x()) + (e.y() - originR.center().y())*(e.y() - originR.center().y()); float C2 = (rectX - e.x())*(rectX - e.x()) + (rectY - e.y()) * (rectY -e.y()); degree = acos((C2 - A2 - B2) / (-2*sqrt(A2)*sqrt(B2))); float ny = (float) Rm * e.x() + Rq; switch(highlighted) { case 0: if (ny > e.y()) degree = -degree; break; case 1: if (ny < e.y()) degree = -degree; break; case 2: if (ny > e.y()) degree = -degree; break; case 3: if (ny < e.y()) degree = -degree; break; } this->update(); } // Find the new size of the selection rectangle after an edit void RenderArea::RecalculateSelectionArea() { selStart = QPoint(MAX,MAX); selEnd = QPoint(-MAX,-MAX); CMeshO::FaceIterator fi; for(fi = model->cm.face.begin(); fi != model->cm.face.end(); ++fi) { if ((*fi).IsUserBit(selBit) && !(*fi).IsD()) { QPoint a = ToScreenSpace((*fi).WT(0).u(), (*fi).WT(0).v()); QPoint b = ToScreenSpace((*fi).WT(1).u(), (*fi).WT(1).v()); QPoint c = ToScreenSpace((*fi).WT(2).u(), (*fi).WT(2).v()); // >> There's a BUG IN QREGION: I can't create a region if points are too near!!!! << SetUpRegion(a, b, c); } } if (selected && selStart.x() < selEnd.x() && selStart.y() < selEnd.y()) { selection = QRect(selStart, selEnd); UpdateSelectionArea(0,0); } } void RenderArea::SetUpRegion(QPoint a, QPoint b, QPoint c) { // Avoid a bug in Qt by calculating the region manually if (a.x() < selStart.x()) selStart.setX(a.x()); if (b.x() < selStart.x()) selStart.setX(b.x()); if (c.x() < selStart.x()) selStart.setX(c.x()); if (a.y() < selStart.y()) selStart.setY(a.y()); if (b.y() < selStart.y()) selStart.setY(b.y()); if (c.y() < selStart.y()) selStart.setY(c.y()); if (a.x() > selEnd.x()) selEnd.setX(a.x()); if (b.x() > selEnd.x()) selEnd.setX(b.x()); if (c.x() > selEnd.x()) selEnd.setX(c.x()); if (a.y() > selEnd.y()) selEnd.setY(a.y()); if (b.y() > selEnd.y()) selEnd.setY(b.y()); if (c.y() > selEnd.y()) selEnd.setY(c.y()); } void RenderArea::UpdateSelectionArea(int x, int y) { // Update the buttons of the selection area selection.moveCenter(QPoint(selection.center().x() + x, selection.center().y() + y)); selRect[0].moveCenter(selection.topLeft()); selRect[1].moveCenter(selection.topRight()); selRect[2].moveCenter(selection.bottomLeft()); selRect[3].moveCenter(selection.bottomRight()); } void RenderArea::UpdateSelectionAreaV(int x, int y) { // Update the buttons of the selection area for vertices selection.moveCenter(QPoint(selection.center().x() + x, selection.center().y() + y)); selRect[0].moveBottomRight(selection.topLeft()); selRect[1].moveBottomLeft(selection.topRight()); selRect[2].moveTopRight(selection.bottomLeft()); selRect[3].moveTopLeft(selection.bottomRight()); } void RenderArea::UpdateVertexSelection() { // Recalculate the rectangle for selection of vertices selStart = QPoint(MAX,MAX); selEnd = QPoint(-MAX,-MAX); selectedV = false; selection = QRect(); CMeshO::FaceIterator fi; for(fi = model->cm.face.begin(); fi != model->cm.face.end(); ++fi) { if ((*fi).WT(0).n() == textNum && !(*fi).IsD()) { for (int j = 0; j < 3; j++) { QPoint tmp = ToScreenSpace((*fi).WT(j).u(), (*fi).WT(j).v()); if ((*fi).V(j)->IsUserBit(selVertBit) && areaUV.contains(QPointF((*fi).WT(j).u(), (*fi).WT(j).v()))) { UpdateBoundingArea(tmp,tmp); if (!selectedV) selectedV = true; } } } } selection = QRect(QPoint(selStart.x() - VRADIUS, selStart.y() - VRADIUS), QPoint(selEnd.x() + VRADIUS, selEnd.y() + VRADIUS)); QPointF a = ToUVSpace(selection.x(), selection.y()); QPointF b = ToUVSpace(selection.bottomRight().x(),selection.bottomRight().y()); areaUV = QRectF(a, QSizeF(b.x()-a.x(), b.y()-a.y())); UpdateSelectionAreaV(0,0); } void RenderArea::UpdateUnify() { // Update the Unify infrastructure after a zoom if (unifyRA != QRect()) { unifyRA.moveCenter(ToScreenSpace(tua, tva)); uvertA = unifyRA.topLeft(); } if (unifyRA1 != QRect()) { unifyRA1.moveCenter(ToScreenSpace(tua1, tva1)); uvertA1 = unifyRA1.center(); } if (unifyRB != QRect()) { unifyRB.moveCenter(ToScreenSpace(tub, tvb)); uvertB = unifyRB.center(); } if (unifyRB1 != QRect()) { unifyRB1.moveCenter(ToScreenSpace(tub1, tvb1)); uvertB1 = unifyRB1.center(); } } QPointF RenderArea::ToUVSpace(int x, int y) { // Convert a point from screen-space to uv-space return QPointF((float)(x - viewport.X()*zoom)/(AREADIM*zoom), (float)(AREADIM*zoom - y + viewport.Y()*zoom)/(AREADIM*zoom)); } QPoint RenderArea::ToScreenSpace(float u, float v) { // Convert a point from uv-space to screen space return QPoint(u * AREADIM*zoom + viewport.X()*zoom, AREADIM*zoom - (v * AREADIM*zoom) + viewport.Y()*zoom); } void RenderArea::DrawCircle(QPoint origin) { // Draw a circle in the RenderArea using openGL float DEG2RAD = 3.14159f/180.0f; float r = (float)VRADIUS/zoom; glBegin(GL_TRIANGLE_FAN); for (int i = 0; i < 360; i++) { float degInRad = i*DEG2RAD; glVertex3f(origin.x() + cos(degInRad)*r,origin.y() + sin(degInRad)*r,2.0f); } glEnd(); } void RenderArea::UpdateBoundingArea(QPoint topLeft, QPoint topRight) { // Update the bounding box (selection rectangle) of the selected faces/vertices if (topLeft.x() < selStart.x()) selStart.setX(topLeft.x()); if (topLeft.y() < selStart.y()) selStart.setY(topLeft.y()); if (topRight.x() > selEnd.x()) selEnd.setX(topRight.x()); if (topRight.y() > selEnd.y()) selEnd.setY(topRight.y()); } // Import the face selected from the meshlab GLArea in the pluging void RenderArea::ImportSelection() { for (unsigned i = 0; i < model->cm.face.size(); i++) model->cm.face[i].ClearUserBit(selBit); selStart = QPoint(MAX,MAX); selEnd = QPoint(-MAX,-MAX); CMeshO::FaceIterator fi; for(fi = model->cm.face.begin(); fi != model->cm.face.end(); ++fi) { if ((*fi).IsS() && !(*fi).IsD()) { if (!selected) selected = true; (*fi).SetUserBit(selBit); QPoint a = ToScreenSpace((*fi).WT(0).u(), (*fi).WT(0).v()); QPoint b = ToScreenSpace((*fi).WT(1).u(), (*fi).WT(1).v()); QPoint c = ToScreenSpace((*fi).WT(2).u(), (*fi).WT(2).v()); SetUpRegion(a, b, c); } } if (selected) { selection = QRect(selStart, selEnd); UpdateSelectionArea(0,0); originR.moveCenter(ToScreenSpace(origin.x(), origin.y())); origin = ToUVSpace(originR.center().x(), originR.center().y()); } ChangeMode(EDITFACEMODE); this->update(); } void RenderArea::CountVertexes() { // Count the number of selected UV vertices (not so easy...) VCount = 0; collapse1 = 0; collapse2 = 0; CMeshO::FaceIterator fi; vector< TexCoord2 > tmpCoord = vector< TexCoord2 >(); for(fi = model->cm.face.begin(); fi != model->cm.face.end(); ++fi) { if ((*fi).WT(0).n() == textNum) { for (int j = 0; j < 3; j++) { if ((*fi).V(j)->IsUserBit(selVertBit)) { if (!isInside(tmpCoord, (*fi).WT(j)) && areaUV.contains(QPointF((*fi).WT(j).u(), (*fi).WT(j).v()))) { VCount++; if (collapse1 == 0) {collapse1 = (*fi).V(j); vc1 = (*fi).WT(j); } else if (collapse2 == 0) {collapse2 = (*fi).V(j); vc2 = (*fi).WT(j); } tmpCoord.push_back((*fi).WT(j)); } } } } } } void RenderArea::UpdateUnifyTopology() { // Update the topology needed for unify of edge vcg::tri::UpdateTopology::FaceFaceFromTexCoord(model->cm); vcg::tri::UpdateTopology::VertexFace(model->cm); vcg::tri::UpdateFlags::FaceBorderFromFF(model->cm); vcg::tri::UpdateFlags::VertexBorderFromFaceBorder(model->cm); } bool RenderArea::isInside(vector > tmpCoord, TexCoord2 act) { // Support function for search in a vector for (unsigned i = 0; i < tmpCoord.size(); i++) { if (tmpCoord[i] == act) return true; } return false; } bool RenderArea::isInside(CFaceO* face) { for (int h = 0; h < banList.size(); h++) { if (face == banList[h]) return true; } return false; } void RenderArea::ShowFaces() { // Set up model for display the selected faces for (unsigned i = 0; i < model->cm.face.size(); i++) { if (model->cm.face[i].IsUserBit(selBit)) model->cm.face[i].SetS(); else model->cm.face[i].ClearS(); } emit UpdateModel(); } // Reset the position of the viewport void RenderArea::ResetPosition() { zoom = 1; ResetTrack(true); if (selected) RecalculateSelectionArea(); else if (selectedV) UpdateVertexSelection(); this->update(); }