mirror of
https://github.com/lucaspalomodevelop/meshlab.git
synced 2026-03-13 08:09:39 +00:00
2010 lines
74 KiB
C++
2010 lines
74 KiB
C++
/****************************************************************************
|
|
* 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 <wrap/qt/trackball.h>
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
|
|
#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<QRect>();
|
|
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<Point2f>();
|
|
drawnPath1 = vector<Point2f>();
|
|
banList = vector<CFaceO*>();
|
|
drawP = false;
|
|
drawP1 = false;
|
|
path = vector<CVertexO*>();
|
|
path1 = vector<CVertexO*>();
|
|
|
|
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<CMeshO>::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<CMeshO>::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<QPoint> t = QVector<QPoint>();
|
|
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<CFaceO*> Q = vector<CFaceO*>();
|
|
|
|
// Search the clicked face
|
|
for(unsigned i = 0; i < model->cm.face.size(); i++)
|
|
{
|
|
if (model->cm.face[i].WT(0).n() == textNum)
|
|
{
|
|
QVector<QPoint> t = QVector<QPoint>();
|
|
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<CVertexO*> 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<CVertexO*> path = vector<CVertexO*>();
|
|
unsigned index = 0;
|
|
vector<CFaceO*> Q = vector<CFaceO*>();
|
|
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<float> > tmpCoord = vector< TexCoord2<float> >();
|
|
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<CMeshO>::FaceFaceFromTexCoord(model->cm);
|
|
vcg::tri::UpdateTopology<CMeshO>::VertexFace(model->cm);
|
|
vcg::tri::UpdateFlags<CMeshO>::FaceBorderFromFF(model->cm);
|
|
vcg::tri::UpdateFlags<CMeshO>::VertexBorderFromFaceBorder(model->cm);
|
|
}
|
|
|
|
bool RenderArea::isInside(vector<TexCoord2<float> > tmpCoord, TexCoord2<float> 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();
|
|
}
|