From ef1ee88bfcf892b9fa38d4fdc6f3281d1b02c878 Mon Sep 17 00:00:00 2001 From: Andrea Bernabei bernabei Date: Tue, 15 Apr 2008 06:48:58 +0000 Subject: [PATCH] Brush shape selection, clone tool, clone preview window, all original tools now included and many more improvements. Still many bugs to fix and heavy refactoring on some code necessary. GUI incomplete --- src/fgt/editpaint/editpaint.cpp | 540 ++++++++++++++++++++++++-------- src/fgt/editpaint/editpaint.h | 216 +++++++++---- src/fgt/editpaint/meshlab.qrc | 1 + src/fgt/editpaint/paintbox.cpp | 38 ++- src/fgt/editpaint/paintbox.h | 131 ++++++-- src/fgt/editpaint/paintbox.ui | 387 ++++++++++++----------- 6 files changed, 905 insertions(+), 408 deletions(-) diff --git a/src/fgt/editpaint/editpaint.cpp b/src/fgt/editpaint/editpaint.cpp index e62cf98e0..252f0ff71 100644 --- a/src/fgt/editpaint/editpaint.cpp +++ b/src/fgt/editpaint/editpaint.cpp @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -43,8 +42,13 @@ EditPaintPlugin::EditPaintPlugin() foreach(editAction, actionList) editAction->setCheckable(true); zbuffer = NULL; + color_buffer = NULL; + mark = 0; + disable_decorate = false; circle = generateCircle(); dense_circle = generateCircle(64); + square = generateSquare(); + dense_square = generateSquare(16); } EditPaintPlugin::~EditPaintPlugin() @@ -84,9 +88,17 @@ void EditPaintPlugin::StartEdit(QAction *, MeshModel& m, GLArea * parent) tri::UpdateBounding::Box(m.cm); m.updateDataMask(MeshModel::MM_VERTFACETOPO); - parent->getCurrentRenderMode().colorMode=vcg::GLW::CMPerVert; - parent->mm()->ioMask|=MeshModel::IOM_VERTCOLOR; + m.updateDataMask(MeshModel::MM_FACEMARK); + + if ((m.currentDataMask & MeshModel::IOM_VERTCOLOR) == 0) + { + Color4b color(100, 100, 100, 255); + for (CMeshO::VertexIterator i = m.cm.vert.begin(); i != m.cm.vert.end(); i++) (*i).C() = color; + } + parent->mm()->ioMask|=MeshModel::IOM_VERTQUALITY; + parent->mm()->ioMask|=MeshModel::IOM_VERTCOLOR; + parent->getCurrentRenderMode().colorMode=vcg::GLW::CMPerVert; QObject::connect(paintbox, SIGNAL(undo()), this, SLOT(update())); QObject::connect(paintbox, SIGNAL(redo()), this, SLOT(update())); @@ -115,7 +127,7 @@ void EditPaintPlugin::mousePressEvent(QAction * , QMouseEvent * event, MeshModel gla->update(); } -void EditPaintPlugin::mouseMoveEvent(QAction *, QMouseEvent* event, MeshModel &, GLArea * gla) +void EditPaintPlugin::mouseMoveEvent(QAction *, QMouseEvent* event, MeshModel & m, GLArea * gla) { event_queue.enqueue(*event); gla->update(); @@ -127,13 +139,30 @@ void EditPaintPlugin::mouseReleaseEvent (QAction *,QMouseEvent * event, MeshMod gla->update(); } +void EditPaintPlugin::projectCursor(MeshModel & m, GLArea * gla) +{ + if (paintbox->getSizeUnit() == 0){ + if (paintbox->getBrush() == CIRCLE) drawSimplePolyLine(gla, cursor, paintbox->getSize(), circle); + if (paintbox->getBrush() == SQUARE) drawSimplePolyLine(gla, cursor, paintbox->getSize(), square); + } + if (paintbox->getSizeUnit() == 1) { + if (paintbox->getBrush() == CIRCLE) drawPercentualPolyLine(gla, gl_cursor, m, zbuffer, modelview_matrix, projection_matrix, viewport, paintbox->getRadius(), dense_circle); + if (paintbox->getBrush() == SQUARE) drawPercentualPolyLine(gla, gl_cursor, m, zbuffer, modelview_matrix, projection_matrix, viewport, paintbox->getRadius(), dense_square); + } +} + /** * Since only on a Decorate call it is possible to obtain correct values * from OpenGL, all operations are performed during the execution of this * method and not where mouse events are processed. + * + * TODO If update() is called from within the loop on events, there will be a "read ahead" of + * events that will disrupt the status */ void EditPaintPlugin::Decorate(QAction*, MeshModel &m, GLArea * gla) { + if (disable_decorate) return; + glarea = gla; if (event_queue.isEmpty()) return; @@ -157,7 +186,7 @@ void EditPaintPlugin::Decorate(QAction*, MeshModel &m, GLArea * gla) if (event.type() == QEvent::MouseButtonPress) button = event.button(); - prev_cursor = cursor; + prev_cursor = cursor; //TODO what if the cursor was not initialized??? cursor = event.pos(); gl_cursor.setX(cursor.x()); gl_cursor.setY(gla->curSiz.height() - cursor.y()); @@ -194,6 +223,7 @@ void EditPaintPlugin::Decorate(QAction*, MeshModel &m, GLArea * gla) color.setRgb(pixel[0], pixel[1], pixel[2], 255); (button == Qt::LeftButton) ? paintbox->setForegroundColor(color) : paintbox->setBackgroundColor(color); } + paintbox->restorePreviousType(); } continue; @@ -205,37 +235,20 @@ void EditPaintPlugin::Decorate(QAction*, MeshModel &m, GLArea * gla) gla->update(); } continue; - - case COLOR_CLONE : - if (event.type() == QEvent::MouseButtonPress && button == Qt::RightButton) - { - //store position - }else if (event.type() == QEvent::MouseButtonPress && button == Qt::LeftButton) - { - //inizialize cloning - }else if (event.type() == QEvent::MouseMove && button == Qt::LeftButton) - { - //clone - }else if (event.type() == QEvent::MouseButtonRelease && button == Qt::LeftButton) - { - //end cloning - } default : break; } - if (paintbox->getSizeUnit() == 0) drawSimplePolyLine(gla, cursor, paintbox->getSize(), circle); - if (paintbox->getSizeUnit() == 1) drawPercentualPolyLine(gla, gl_cursor, m, zbuffer, modelview_matrix, projection_matrix, viewport, paintbox->getRadius(), dense_circle); - - vector< pair > * vertices; - vertices = new vector< pair >(); //TODO don't need vertices for face selection! - - updateSelection(m, vertices); + + vertices.clear(); //TODO don't need vertices for face selection! switch (paintbox->getCurrentType()) { case MESH_SELECT: + projectCursor(m, glarea); + updateSelection(m); + if (event.type() == QEvent::MouseButtonPress) { paintbox->getUndoStack()->beginMacro("Select"); @@ -252,64 +265,323 @@ void EditPaintPlugin::Decorate(QAction*, MeshModel &m, GLArea * gla) } break; - case MESH_PULL : - case MESH_PUSH : - if (event.type() != QEvent::MouseMove) break; - drawNormalPercentualCircle(gla, gl_cursor, m, zbuffer, - modelview_matrix, projection_matrix, viewport, paintbox->getRadius()); + case MESH_PULL : + projectCursor(m, glarea); + updateSelection(m, & vertices); + + if (event.type() == QEvent::MouseButtonPress) { displaced_vertices.clear(); paintbox->getUndoStack()->beginMacro("Pull"); } + else if (event.type() == QEvent::MouseMove) sculpt(& vertices, 1 / (float) m.cm.bbox.Diag()); + else if (event.type() == QEvent::MouseButtonRelease) paintbox->getUndoStack()->endMacro(); break; + case MESH_PUSH :glarea->setLight(false); + + projectCursor(m, glarea); + updateSelection(m, & vertices); + + if (event.type() == QEvent::MouseButtonPress) { displaced_vertices.clear(); paintbox->getUndoStack()->beginMacro("Push"); } + else if (event.type() == QEvent::MouseMove) sculpt(& vertices, - 1 / (float) m.cm.bbox.Diag()); + else if (event.type() == QEvent::MouseButtonRelease) paintbox->getUndoStack()->endMacro(); + break; + + case COLOR_PAINT : + projectCursor(m, glarea); + updateSelection(m, & vertices); + if (event.type() == QEvent::MouseButtonPress) { paintbox->getUndoStack()->beginMacro("Paint"); visited_vertices.clear(); + paint(& vertices); } - else if (event.type() == QEvent::MouseMove) paint(vertices); + else if (event.type() == QEvent::MouseMove) paint(& vertices); else if (event.type() == QEvent::MouseButtonRelease) paintbox->getUndoStack()->endMacro(); break; + + case COLOR_SMOOTH: + case MESH_SMOOTH: + projectCursor(m, glarea); + updateSelection(m, & vertices); + + if (event.type() == QEvent::MouseButtonPress) + { + paintbox->getUndoStack()->beginMacro("Smooth"); + mark++; //TODO BAD mark usage + } + else if (event.type() == QEvent::MouseMove) smooth(& vertices); + else if (event.type() == QEvent::MouseButtonRelease) paintbox->getUndoStack()->endMacro(); + break; + + case COLOR_CLONE : + if (event.type() == QEvent::MouseButtonPress && button == Qt::RightButton) + { + if (color_buffer != NULL) delete color_buffer; + disable_decorate = true; + glarea->update(); + color_buffer = new GLubyte[gla->curSiz.width()*gla->curSiz.height()*3]; + clone_zbuffer = new GLfloat[gla->curSiz.width()*gla->curSiz.height()]; + glReadPixels(0,0,gla->curSiz.width(), gla->curSiz.height(), GL_RGB, GL_UNSIGNED_BYTE, color_buffer); + glReadPixels(0,0,gla->curSiz.width(), gla->curSiz.height(), GL_DEPTH_COMPONENT, GL_FLOAT, clone_zbuffer); + start_cursor.setX(gl_cursor.x()); start_cursor.setY(gl_cursor.y()); //storing now source click position + + //TODO refactor + QImage image(glarea->width(), glarea->height(), QImage::Format_RGB32); + for (int x = 0; x < glarea->width(); x++){ + for (int y = 0; y < glarea->height(); y++){ + int index = (y * glarea->width() + x)*3; + image.setPixel(x, glarea->height() - y, qRgb((int)color_buffer[index], (int)color_buffer[index + 1], (int)color_buffer[index + 2])); + } + } + + paintbox->setClonePixmap(image); + paintbox->setPixmapCenter(-cursor.x(), -cursor.y()); + disable_decorate = false; + }else if (event.type() == QEvent::MouseButtonPress && button == Qt::LeftButton) + { + projectCursor(m, glarea); + updateSelection(m, & vertices); + paintbox->getUndoStack()->beginMacro("Clone"); + visited_vertices.clear(); + start_cursor.setX(start_cursor.x() - gl_cursor.x()); start_cursor.setY(start_cursor.y() - gl_cursor.y()); + + }else if (event.type() == QEvent::MouseMove && button == Qt::LeftButton) + { + projectCursor(m, glarea); + updateSelection(m, & vertices); + paintbox->setPixmapCenter(-cursor.x() - start_cursor.x(), -cursor.y() + start_cursor.y() ); + paint( & vertices); + }else if (event.type() == QEvent::MouseButtonRelease && button == Qt::LeftButton) + { + projectCursor(m, glarea); + updateSelection(m, & vertices); + paintbox->getUndoStack()->endMacro(); + } + break; + case COLOR_NOISE : + //TODO Perlin noise application default : break; } - - delete vertices; } } -void EditPaintPlugin::paint(vector< pair > * vertices) + +void EditPaintPlugin::smooth(vector< pair > * vertices) +{ + QHash > originals; + + Color4b newcol, destCol; + int opac = paintbox->getOpacity(); + int decrease_pos = paintbox->getSmoothPercentual(); + int c_r, c_g, c_b; + float p_x, p_y, p_z; + float newpos[3]; + + for (unsigned int k = 0; k < vertices->size(); k++) //forach selected vertices + { + pair data = vertices->at(k); + + CVertexO * v = data.first; + VertexDistance * vd = & data.second; + + pair pc_data; //save its color and position + + for (int k = 0; k < 4; k++) pc_data.second[k] = v->C()[k]; + for (int k = 0; k < 3; k++) pc_data.first[k] = v->P()[k]; + + if (v->IMark() != mark) + { + if (paintbox->getCurrentType() == COLOR_SMOOTH) paintbox->getUndoStack()->push(new SingleColorUndo(v, v->C())); + else paintbox->getUndoStack()->push(new SinglePositionUndo(v, v->P())); + v->IMark() = mark; + } + + if (!originals.contains(v)) originals.insert(v, pc_data); //save original color/position data + + CFaceO * one_face = v->VFp(); //one of the faces adjacent to the vertex + int pos = v->VFi(); //index of vertex v on face one_face + c_r = c_g = c_b = 0; + p_x = p_y = p_z = 0; + int count_me = 0; + CFaceO * f = one_face; + + do + { /// calc the sum of the surrounding vertexes pos or and vertexes color + CFaceO * temp = one_face->VFp(pos); //next face in VF list + if (one_face != 0 && !one_face->IsD()) + { + for (int k = 0; k < 3; k++) + { + if (pos != k) + { + CVertexO * v = one_face->V(k); + Color4b tco = v->C(); + if (originals.contains(v)) + { + pair pc_data_k = originals[v]; + + tco = pc_data_k.second; + + p_x += pc_data_k.first[0]; p_y += pc_data_k.first[1]; p_z += pc_data_k.first[2]; + }else if (paintbox->getCurrentType() == MESH_SMOOTH) + { + p_x += v->P()[0]; p_y += v->P()[1]; p_z += v->P()[2]; + } + c_r += tco[0]; c_g += tco[1]; c_b += tco[2]; + } + } + + pos=one_face->VFi(pos); + count_me+=2; + } + one_face = temp; + } while (one_face != f && one_face != 0); + + if (count_me > 0) /// calc the new color or pos + { + float op = brush(paintbox->getBrush(), vd->distance, vd->rel_position.x(), vd->rel_position.y(), decrease_pos); + + if (paintbox->getCurrentType() == COLOR_SMOOTH) + { + newcol[0] = c_r/count_me; + newcol[1] = c_g/count_me; + newcol[2] = c_b/count_me; + + mergeColors((float)(op*opac)/100.0, newcol, v->C(), &destCol); + + v->C()[0] = destCol[0]; + v->C()[1] = destCol[1]; + v->C()[2] = destCol[2]; + } else + { + newpos[0] = p_x/(float)count_me; + newpos[1] = p_y/(float)count_me; + newpos[2] = p_z/(float)count_me; + float po[3]; for (int lauf=0; lauf<3; lauf++) po[lauf]=v->P()[lauf]; + mergePositions((float)(op*opac)/100.0,newpos,po,newpos); + + for (int lauf=0; lauf<3; lauf++) v->P()[lauf]=newpos[lauf]; + } + } + + if (paintbox->getCurrentType() == MESH_SMOOTH) + { + updateNormal(v); + } + } +} + +//TODO Begin modularization of paint to allow different tools to leverage the same architecture +void EditPaintPlugin::sculpt(vector< pair > * vertices, float s) +{ + int opac = paintbox->getOpacity(); + int decrease_pos = paintbox->getHardness(); + float strength = s * paintbox->getDisplacement() / (float) 100; + + Point3f normal(0.0, 0.0, 0.0); + + //TODO Normal should be calculated during updateSelection + for (unsigned int k = 0; k < vertices->size(); k++) + { + normal += vertices->at(k).first->N() / vertices->size(); + } + + for (unsigned int k = 0; k < vertices->size(); k++) + { + pair data = vertices->at(k); + + float op = brush(paintbox->getBrush(), data.second.distance, data.second.rel_position.x(), data.second.rel_position.y(), decrease_pos); + + if (!displaced_vertices.contains(data.first)) + { + displaced_vertices.insert(data.first, pair( + Point3f(data.first->P()[0], data.first->P()[1], data.first->P()[2]), + opac * op) ); + + paintbox->getUndoStack()->push(new SinglePositionUndo(data.first, data.first->P())); + + displaceAlongVector(data.first, normal, (opac * op) * strength); + + } else if (displaced_vertices[data.first].second < opac * op) + { + displaced_vertices[data.first].second = opac * op; + Point3f temp = displaced_vertices[data.first].first; + data.first->P()[0]=temp[0]; data.first->P()[1]=temp[1]; data.first->P()[2]=temp[2]; + displaceAlongVector(data.first, normal, (opac * op)* strength); + } + } + + for (unsigned int k = 0; k < vertices->size(); k++) updateNormal(vertices->at(k).first); +} + +/** + * Painting and Cloning + */ +void EditPaintPlugin::paint(vector< pair > * vertices) { int opac = paintbox->getOpacity(); int decrease_pos = paintbox->getHardness(); - QColor newcol = (button == Qt::LeftButton) ? paintbox->getForegroundColor() : paintbox->getBackgroundColor(); - Color4b color(newcol.red(), newcol.green(), newcol.blue(), newcol.alpha()); - + int index = 0; + Color4b color; + + if (paintbox->getCurrentType() == COLOR_PAINT){ + QColor newcol = (button == Qt::LeftButton) ? paintbox->getForegroundColor() : paintbox->getBackgroundColor(); + color[0] = newcol.red(); color[1] = newcol.green(); color[2] = newcol.blue(); color[3] = newcol.alpha(); + } + for (unsigned int k = 0; k < vertices->size(); k++) { - pair data = vertices->at(k); + pair data = vertices->at(k); + + float op = brush(paintbox->getBrush(), data.second.distance, data.second.rel_position.x(), data.second.rel_position.y(), decrease_pos); - float op = 0.0; - if (data.second * 100.0 > (float)decrease_pos) - { - op = (data.second * 100.0 - (float)decrease_pos)/ (float) (100 - decrease_pos); - } if (!visited_vertices.contains(data.first)) { visited_vertices.insert(data.first, pair( Color4b(data.first->C()[0], data.first->C()[1], data.first->C()[2], data.first->C()[3]), - opac-op*opac) ); + op*opac) ); paintbox->getUndoStack()->push(new SingleColorUndo(data.first, data.first->C())); - applyColor(data.first, color, (int)( opac - op * opac) ); - } else if (visited_vertices[data.first].second < opac - op * opac) + if (paintbox->getCurrentType() == COLOR_CLONE) + { + index = ((data.second.position.y() + start_cursor.y()) * viewport[2] + + data.second.position.x() + start_cursor.x())*3; + + //TODO Optimize the division by three + if (index < glarea->curSiz.width()*glarea->curSiz.height()*3 - 3 && index > 0) + { + if (clone_zbuffer[index / 3] < 1.0){ + color[0] = color_buffer[index]; color[1] = color_buffer[index + 1]; color[2] = color_buffer[index + 2]; + applyColor(data.first, color, (int)(op * opac)); + + } + } + }else applyColor(data.first, color, (int)(op * opac) ); + + } else if (visited_vertices[data.first].second < op * opac) { - visited_vertices[data.first].second = opac - op * opac; + visited_vertices[data.first].second = op * opac; Color4b temp = visited_vertices[data.first].first; data.first->C()[0]=temp[0]; data.first->C()[1]=temp[1]; data.first->C()[2]=temp[2]; - applyColor(data.first, color, (int)(opac - op * opac )); + + if (paintbox->getCurrentType() == COLOR_CLONE) //TODO refactor in a method + { + index = ((data.second.position.y() + start_cursor.y()) * viewport[2] + + data.second.position.x() + start_cursor.x())*3; + + if (index < glarea->curSiz.width()*glarea->curSiz.height()*3 - 3 && index > 0) + { + if (clone_zbuffer[index / 3] < 1.0){ + color[0] = color_buffer[index]; color[1] = color_buffer[index + 1]; color[2] = color_buffer[index + 2]; + applyColor(data.first, color, (int)(op * opac)); + } + } + }else applyColor(data.first, color, (int)(op * opac) ); } } } @@ -332,7 +604,7 @@ void EditPaintPlugin::fill(MeshModel & ,CFaceO * face) Color4b color(newcol.red(), newcol.green(), newcol.blue(), newcol.alpha()); - std::cout << newcol.red() <<" "<< newcol.green() <<" "<< newcol.blue() << std::endl; +// std::cout << newcol.red() <<" "<< newcol.green() <<" "<< newcol.blue() << std::endl; paintbox->getUndoStack()->beginMacro("Fill Color"); @@ -408,7 +680,6 @@ void EditPaintPlugin::gradient(MeshModel & m,GLArea * gla) { if (gradient_form==0) { double r=(dx-p0.x())*(p1.x()-p0.x())+(dy-p0.y())*(p1.y()-p0.y()); - //r=r/(l*l); r=r/l_square; float px=p0.x()+r*(p1.x()-p0.x()); @@ -418,7 +689,7 @@ void EditPaintPlugin::gradient(MeshModel & m,GLArea * gla) { py=py-dy; if (gradient_type==0) { - if (r>=0 && r<=1 /*&& (px*px+py*py=0 && r<=1) { mergeColors(r,c1,c2,&merger); applyColor(vertex,merger,opac); } else if (r>1) { @@ -427,13 +698,10 @@ void EditPaintPlugin::gradient(MeshModel & m,GLArea * gla) { applyColor(vertex,c2,opac); } } else { - if (r>=0 && r<=1 /*&& (px*px+py*py=0 && r<=1) { applyColor(vertex,c1,(int)((opac * 0.01) * r * 100.0)); } else if (r>1) { applyColor(vertex,c1,opac); - } else if (r<0) { - //colorize((*fi).V(lauf),c2,opac); } } } else { @@ -455,19 +723,27 @@ void EditPaintPlugin::gradient(MeshModel & m,GLArea * gla) { paintbox->getUndoStack()->endMacro(); } + /** * Updates currently selected faces by using one of the * two methods. Just a new version of computeBrushedFaces + * + * It's inlined because it's only called once, inside Decorate. + * Should it be called in other places, the inlining must be removed! */ -void EditPaintPlugin::updateSelection(MeshModel &m, vector< pair > * vertex_result) +inline void EditPaintPlugin::updateSelection(MeshModel &m, vector< pair > * vertex_result) { - QHash visited_faces; /*< */ - QHash visited_vertices; vector::iterator fpi; - vector temp; + vector temp; //TODO maybe temp can be placed inside the class for better performance + + vector surround; /*< surrounding faces of a given face*/ + surround.reserve(6); + + mark++; if (selection->size() == 0) { CMeshO::FaceIterator fi; + temp.reserve(m.cm.fn); //avoid unnecessary expansions for(fi = m.cm.face.begin(); fi != m.cm.face.end(); ++fi){ if(!(*fi).IsD()) { temp.push_back((&*fi)); @@ -475,6 +751,8 @@ void EditPaintPlugin::updateSelection(MeshModel &m, vector< pairsize()); //avoid unnecessary expansions + vertex_result->reserve(selection->size()); for(fpi = selection->begin();fpi != selection->end(); ++fpi) { temp.push_back(*fpi); @@ -492,6 +770,8 @@ void EditPaintPlugin::updateSelection(MeshModel &m, vector< pairgetPaintBackFace(); bool invisible = paintbox->getPaintInvisible(); bool percentual = paintbox->getSizeUnit() == 1; + float pen_radius = paintbox->getRadius(); + int pen_size = paintbox->getSize(); double EPSILON = 0.003; @@ -517,7 +797,7 @@ void EditPaintPlugin::updateSelection(MeshModel &m, vector< pairgetRadius() * scale_fac * viewport[3] * fov / distance[j]) : paintbox->getSize(); + VertexDistance vd; //TODO make it a pointer + + float radius = percentual ? (pen_radius * scale_fac * viewport[3] * fov / distance[j]) : pen_size; - if (isIn(gl_cursorf, gl_prev_cursorf, p[j].x(), p[j].y(), radius , &dist)) + if (isIn(gl_cursorf, gl_prev_cursorf, p[j].x(), p[j].y(), radius , & vd.distance, vd.rel_position)) { intern = true; if (vertex_result == NULL) continue; - else if(!visited_vertices.contains(fac->V(j))) + else if (fac->V(j)->IMark() != mark) { - pair data(fac->V(j), dist); + vd.position.setX((int)p[j].x()); vd.position.setY((int)p[j].y()); + pair data(fac->V(j), vd); vertex_result->push_back(data); - visited_vertices.insert(fac->V(j),fac->V(j)); + fac->V(j)->IMark() = mark; } } - QPointF pos_res; - if (vertex_result == NULL && !intern && lineHitsCircle(p[j],p[(j+1)%3], gl_cursorf, radius, &pos_res)) + + if (vertex_result == NULL && !intern && lineHitsCircle(p[j],p[(j+1)%3], gl_cursorf, radius)) { intern = true; continue; @@ -578,18 +860,18 @@ void EditPaintPlugin::updateSelection(MeshModel &m, vector< pairIMark() != mark) { - visited_faces.insert((fac),(fac)); + fac->IMark() = mark; selection->push_back(fac); - - vector surround; + surround.clear(); for (int lauf=0; lauf<3; lauf++) getSurroundingFacesVF(fac,lauf,&surround); - for (unsigned int lauf3=0; lauf3IMark() != mark) { - temp.push_back(surround[lauf3]); + temp.push_back(surround[lauf3]); } } } @@ -610,6 +892,7 @@ void EditPaintPlugin::update() Q_EXPORT_PLUGIN(EditPaintPlugin) + /*********OpenGL Drawing Routines************/ /** @@ -680,15 +963,15 @@ void drawNormalPercentualCircle(GLArea * , QPoint &glcur, MeshModel &m, GLfloat* glReadPixels((int)sx, (int)sy, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &_z); sz = _z; - std::cout << "2D: " << sx << " " << sy << " " << sz << std::endl; +// std::cout << "2D: " << sx << " " << sy << " " << sz << std::endl; - if (GL_FALSE == gluUnProject(sx, sy, sz, mvmatrix, prmatrix, vmatrix, &x, &y, &z)) std::cout << "false"; +// if (GL_FALSE == gluUnProject(sx, sy, sz, mvmatrix, prmatrix, vmatrix, &x, &y, &z)) std::cout << "false"; - std::cout << "3D: " << x << " " << y << " " << z << std::endl; +// std::cout << "3D: " << x << " " << y << " " << z << std::endl; gluProject(x, y, z, mvmatrix, prmatrix, vmatrix, &sx, &sy, &sz); - std::cout << "re2D: " << sx << " " << sy << " " << sz << std::endl; +// std::cout << "re2D: " << sx << " " << sy << " " << sz << std::endl; glPushAttrib(GL_COLOR_BUFFER_BIT); //---Begin of Point Drawing glDisable(GL_DEPTH_TEST); @@ -714,7 +997,7 @@ void drawNormalPercentualCircle(GLArea * , QPoint &glcur, MeshModel &m, GLfloat* normal = fp->N(); MULT /= normal.Norm(); tx = x + MULT * normal.Ext(0); ty = y + MULT * normal.Ext(1); tz = z + MULT * normal.Ext(2); - std::cout << "displaced point: " << tx << " " << ty << " " << tz << std::endl; + // std::cout << "displaced point: " << tx << " " << ty << " " << tz << std::endl; glPushAttrib(GL_COLOR_BUFFER_BIT); //---Begin of Point Drawing glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); @@ -728,51 +1011,13 @@ void drawNormalPercentualCircle(GLArea * , QPoint &glcur, MeshModel &m, GLfloat* glEnable(GL_DEPTH_TEST); glPopAttrib(); //---End of Point Drawing - //TODO push matrici - /* glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0,gla->curSiz.width(),gla->curSiz.height(),0,-1,1); //TODO troppo - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - gluLookAt(tx, ty, tz, x, y, z, 0, 1, 0); - - //gluUnProject() di una circonferenza centrata a metà schermo e salva i punti - GLdouble cx, cy; //centro della circonferenza - GLdouble xs[4]; //x dei punti - GLdouble ys[4]; //y dei punti - GLdouble rad; - - cx = gla->curSiz.width()/2; cy = gla->curSiz.height()/2; -// xs[0] = cx + rad; ys[0] = cy; -// xs[1] = cx; ys[1] = cy + rad; -// xs[2] = cx - rad; ys[2] = cy; -// xs[3] = cx; ys[3] = cy - rad; - -// GLUquadric glq(); -// gluDisk(glq, 10, 11, 16, 1); - - //TODO algoritmo basturdo: - //Proietta linee che partono - - //Bresenham line rastering beginning from normal apex going through circumpherence points - //OR a quicktime approach to the problem. (line length == bb.diag) - //TODO think about it - - //TODO pop matrici - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - */ - //gluProject() dei punti della circonferenza calcolati sopra }else { - std::cout << "face doesn't have normal!" < * generatePolygon(int sides, int segments) +{ + vector * temp = new vector(); + + float step = sides / 2.0; + + float start_angle = M_PI / sides; + + for (int k = 0; k < sides; k++) + { + temp->push_back(QPointF(sin((M_PI * (float)k / step) + start_angle), cos((M_PI * (float)k / step) + start_angle))); + } + + if (segments > 1) + { + vector * result = new vector(); + int sk; + for (int k = 0; k < sides; k++ ) + { + sk = (k + 1) % sides; + QPointF kv = temp->at(k); + QPointF skv = temp->at(sk); + QPointF d = (skv - kv)/segments; + result->push_back(kv); + for (int j = 1; j < segments; j++) + { + result->push_back(kv + d * j); + } + } + return result; + }else return temp; +} + /** * Generates the same circle points that Gfrei's algorithm does - * With the same procedure too. */ vector * generateCircle(int segments) { - vector * result = new vector(); - - float step = segments / 2.0; - - for (int k = 0; k < segments; k++) result->push_back(QPointF(sin(M_PI * (float)k / step), cos(M_PI * (float)k / step))); - - return result; + return generatePolygon(segments, 1); } -vector * generateSquare() +vector * generateSquare(int segments) { - return generateCircle(4); + return generatePolygon(4, segments); } diff --git a/src/fgt/editpaint/editpaint.h b/src/fgt/editpaint/editpaint.h index 5c4877b77..6677f125c 100644 --- a/src/fgt/editpaint/editpaint.h +++ b/src/fgt/editpaint/editpaint.h @@ -62,18 +62,25 @@ public: virtual void mousePressEvent (QAction *, QMouseEvent *event, MeshModel &/*m*/, GLArea * ); virtual void mouseMoveEvent (QAction *,QMouseEvent *event, MeshModel &/*m*/, GLArea * ); virtual void mouseReleaseEvent (QAction *,QMouseEvent *event, MeshModel &/*m*/, GLArea * ); +// virtual void tabletEvent (QAction *, QTabletEvent *, MeshModel & , GLArea *); //TODO Make them come! glarea virtual QList actions() const ; public slots: void update(); private: - void updateSelection(MeshModel &m, std::vector< std::pair > * vertex_result = NULL); + struct VertexDistance { QPoint position; QPointF rel_position; float distance;}; - void paint(std::vector< std::pair > * vertices); + void updateSelection(MeshModel &m, vector< pair > * vertex_result = NULL); + + void paint(vector< pair > * vertices); + void sculpt(vector< pair > * vertices, float strength); + void smooth(vector< pair > * vertices); void fill(MeshModel & m,CFaceO * face); void gradient(MeshModel & m,GLArea * gla); + void projectCursor(MeshModel & m, GLArea * gla); + QPoint cursor; /*< indicates the last known cursor position (for now) */ QPoint gl_cursor; //same cursor but in OpenGl coordinates QPoint start_cursor; //indicates last position at mouse click @@ -84,8 +91,11 @@ private: GLint viewport[4]; GLfloat* zbuffer; /*< last known zbuffer, always kept up-to-date */ - - Qt::MouseButton button; + + GLubyte* color_buffer; /*< buffer used as color source in cloning*/ + GLfloat* clone_zbuffer; /* event_queue; /*< Queue used to store UI events in order to postpone their processing during a Decorate call*/ @@ -94,11 +104,19 @@ private: GLArea * glarea; /*< current glarea */ - std::vector * selection; //currently selected faces - QHash > visited_vertices; //active vertices during paint + vector * selection; //currently selected faces + vector< pair > vertices; //touched vertices during last updateSelection + QHash > visited_vertices; //active vertices during painting + QHash > displaced_vertices; //active vertices during sculpting - std::vector * circle; - std::vector * dense_circle; + bool disable_decorate; + + int mark; + + vector * circle; + vector * dense_circle; + vector * square; + vector * dense_square; }; /** @@ -111,19 +129,39 @@ class SingleColorUndo : public QUndoCommand { public: - SingleColorUndo(CVertexO * v, vcg::Color4b c, QUndoCommand * parent = 0) : QUndoCommand(parent){ + SingleColorUndo(CVertexO * v, Color4b c, QUndoCommand * parent = 0) : QUndoCommand(parent){ vertex = v; original = c; // setText("Single Vertex Color Change"); } - virtual void undo() { vcg::Color4b temp = vertex->C(); vertex->C() = original; original = temp;} + virtual void undo() {Color4b temp = vertex->C(); vertex->C() = original; original = temp;} virtual void redo() {undo();} virtual int id() {return COLOR_PAINT;} private: CVertexO* vertex; - vcg::Color4b original; + Color4b original; }; +//TODO Create MultipleColorUndo + +class SinglePositionUndo : public QUndoCommand +{ + +public: + SinglePositionUndo(CVertexO * v, Point3f p, QUndoCommand * parent = 0) : QUndoCommand(parent){ + vertex = v; original = p; + } + + virtual void undo() {Point3f temp = vertex->P(); vertex->P() = original; original = temp;} + virtual void redo() {undo();} + virtual int id() {return MESH_PULL;} + +private: + CVertexO* vertex; + Point3f original; +}; + + /** * Undoes changes in selected faces. * @@ -154,12 +192,12 @@ private : * * O(1) complexity */ -inline void applyColor(CVertexO * vertex, const vcg::Color4b& newcol, int opac) +inline void applyColor(CVertexO * vertex, const Color4b& newcol, int opac) { - vcg::Color4b orig = vertex->C(); + Color4b orig = vertex->C(); for (int i = 0; i < 4; i ++) - orig[i] = std::min(255,( (newcol[i]-orig[i])*opac + orig[i]*(100) )/100); + orig[i] = min(255,( (newcol[i]-orig[i])*opac + orig[i]*(100) )/100); vertex->C() = orig; } @@ -169,10 +207,10 @@ inline void applyColor(CVertexO * vertex, const vcg::Color4b& newcol, int opac) * * O(1) complexity */ -inline void mergeColors(double percent,const vcg::Color4b& c1,const vcg::Color4b& c2, vcg::Color4b* dest) +inline void mergeColors(double percent,const Color4b& c1,const Color4b& c2,Color4b* dest) { for (int i = 0; i < 4; i ++) - (*dest)[i] = (char)std::min(255.0,((c1[i]-c2[i])*percent+c2[i])); + (*dest)[i] = (char)min(255.0,((c1[i]-c2[i])*percent+c2[i])); } /** @@ -231,7 +269,7 @@ inline int getNearest(QPointF center, QPointF *points, int num) } /** - * Checks if a point (dx, dy) is contained in either: + * Checks if a point (x, y) is contained in either: * * a) the rectangle aligned to the segment connecting p0 to p1 * whose height is the distance between p0 and p1 and width @@ -240,7 +278,7 @@ inline int getNearest(QPointF center, QPointF *points, int num) * c) the circle centered in p1 with same radius * * To check condition (a): let v be the vector going from p0 to p1 - * and w the vector from p0 to (dx, dy). If theta is the angle between + * and w the vector from p0 to (x, y). If theta is the angle between * v and w, then: * * |v| |w| cos(theta) |w| @@ -251,36 +289,42 @@ inline int getNearest(QPointF center, QPointF *points, int num) * of vector w on vector v. * * Since w' = v * r is the projection of w on v, pf = p0 + w' is - * the orthogonal projection of (dx, dy) on the segment - * connecting p0 to p1. If the distance between (dx, dy) and pf is less - * than radius, then (dx, dy) lays inside the rectangle + * the orthogonal projection of (x, y) on the segment + * connecting p0 to p1. If the distance between (x, y) and pf is less + * than radius, then (x, y) lays inside the rectangle * */ -inline bool isIn(const QPointF &p0,const QPointF &p1,float dx,float dy,float radius,float *dist) +inline bool isIn(const QPointF &p0,const QPointF &p1, float x,float y,float radius, float *dist, QPointF &pos) { - if (p0 != p1) //(a) condition needs not to be tested: rectangle is null + float radius_sq = radius * radius; + + if (p0 != p1) //otherwise condition (a) needs not to be tested: rectangle is null { float v_x = (p1.x()-p0.x()); float v_y = (p1.y()-p0.y()); float v_length_squared = v_x * v_x + v_y * v_y; - float w_x = dx - p0.x(); - float w_y = dy - p0.y(); + float w_x = x - p0.x(); + float w_y = y - p0.y(); - float prod = w_x * v_x + w_y * v_y; //scalar product of v and w + float v_dot_w = w_x * v_x + w_y * v_y; //scalar product of v and w - float r = prod / v_length_squared; + float r = v_dot_w / v_length_squared; float pf_x = p0.x() + r * v_x; float pf_y = p0.y() + r * v_y; - float delta_x = pf_x - dx; - float delta_y = pf_y - dy; + float delta_x = x - pf_x; + float delta_y = y - pf_y; - if (r >= 0 && r <= 1 && (delta_x * delta_x + delta_y * delta_y < radius * radius)) + if (r >= 0 && r <= 1 && (delta_x * delta_x + delta_y * delta_y < radius_sq)) { - *dist = sqrt(delta_x * delta_x + delta_y * delta_y)/radius; + float delta_len = sqrt(delta_x * delta_x + delta_y * delta_y); + + *dist = delta_len / radius; + pos.setY(delta_y / radius); + pos.setX(delta_x / radius); return true; } } @@ -288,21 +332,43 @@ inline bool isIn(const QPointF &p0,const QPointF &p1,float dx,float dy,float rad // there could be some problem when point is nearer p0 or p1 and viceversa // so i have to check both. is only needed with smooth_borders bool found = false; - float x1=(dx-p1.x()); - float y1=(dy-p1.y()); - float bla1=x1*x1+y1*y1; - if (bla1 0 there are two. */ -inline bool lineHitsCircle(QPointF& LineStart,QPointF& LineEnd,QPointF& CircleCenter, float radius, QPointF* const pOut = 0) +inline bool lineHitsCircle(QPointF& LineStart,QPointF& LineEnd,QPointF& CircleCenter, float radius) { const float radius_sq = radius * radius; QPointF Delta = LineStart - CircleCenter; float delta_length_sq = Delta.x()*Delta.x()+Delta.y()*Delta.y(); - if(delta_length_sq <= radius_sq) { /// startpoint is in circle - if(pOut) *pOut = LineStart; - return true; - } + if(delta_length_sq <= radius_sq) return true; QPointF Direction = LineEnd - LineStart; @@ -390,18 +453,12 @@ inline bool lineHitsCircle(QPointF& LineStart,QPointF& LineEnd,QPointF& CircleCe { const float s = - direction_dot_delta / direction_length_sq; if(s < 0.0f || s > 1.0f) return false; - else { - if(pOut) *pOut = LineStart + s * Direction; - return true; - } + else return true; }else //two intersections { const float s = (- direction_dot_delta - sqrtf(d)) / direction_length_sq; //only one intersection is chosen if(s < 0.0f || s > 1.0f) return false; - else { - if(pOut) *pOut = LineStart + s * Direction; - return true; - } + else return true; } } @@ -413,9 +470,9 @@ inline bool lineHitsCircle(QPointF& LineStart,QPointF& LineEnd,QPointF& CircleCe * * O(1) */ -inline void displaceAlongNormal(CVertexO* vp, float displacement) +inline void displaceAlongVector(CVertexO* vp, Point3f vector, float displacement) { - (*vp).P() += vcg::Point3f( ((*vp).N().Ext(0)) * displacement, ((*vp).N().Ext(1)) * displacement, ((*vp).N().Ext(2)) * displacement); + (*vp).P() += vector * displacement; } /** @@ -434,7 +491,7 @@ inline bool getVertexAtMouse(MeshModel &m,CMeshO::VertexPointer& value, QPoint& //TODO e se i vertici sono stati cancellati? (IsD()) - if (vcg::GLPickTri::PickNearestFace(cursor.x(), cursor.y(), m.cm, fp, 2, 2)) + if (GLPickTri::PickNearestFace(cursor.x(), cursor.y(), m.cm, fp, 2, 2)) { QPointF point[3]; @@ -461,7 +518,7 @@ inline bool getVertexAtMouse(MeshModel &m,CMeshO::VertexPointer& value, QPoint& /** * calcs the surrounding faces of a vertex with VF topology */ -inline void getSurroundingFacesVF(CFaceO * fac,int vert_pos, std::vector *surround) { +inline void getSurroundingFacesVF(CFaceO * fac,int vert_pos,vector *surround) { CVertexO * vert=fac->V(vert_pos); int pos=vert->VFi(); CFaceO * first_fac=vert->VFp(); @@ -478,21 +535,44 @@ inline void getSurroundingFacesVF(CFaceO * fac,int vert_pos, std::vectorVFp(); + CFaceO * one_face = f; + int pos = v->VFi(); + v->N()[0]=0;v->N()[1]=0;v->N()[2]=0; + do { + CFaceO * temp=one_face->VFp(pos); + if (one_face!=0 && !one_face->IsD()) + { + for (int lauf=0; lauf<3; lauf++) + if (pos!=lauf) { + v->N()+=one_face->V(lauf)->cN(); + } + face::ComputeNormalizedNormal(*one_face); + pos=one_face->VFi(pos); + } + one_face=temp; + } while (one_face!=f && one_face!=0); + v->N().Normalize(); +} + /*********OpenGL Drawing Routines************/ + void drawNormalPercentualCircle(GLArea *, QPoint &, MeshModel &, GLfloat* , double* , double* , GLint* , float ); void drawVertex(CVertexO* ); void drawLine(GLArea *, QPoint &, QPoint &); -void drawSimplePolyLine(GLArea * gla, QPoint & gl_cur, float scale, std::vector * points); -void drawPercentualPolyLine(GLArea * , QPoint &, MeshModel &, GLfloat* , double* , double* , GLint* , float , std::vector * ); +void drawSimplePolyLine(GLArea * gla, QPoint & gl_cur, float scale, vector * points); +void drawPercentualPolyLine(GLArea * , QPoint &, MeshModel &, GLfloat* , double* , double* , GLint* , float , vector * ); -std::vector * generateCircle(int segments = 18); -std::vector * generateSquare(); +vector * generateCircle(int segments = 18); +vector * generateSquare(int segments = 1); #endif diff --git a/src/fgt/editpaint/meshlab.qrc b/src/fgt/editpaint/meshlab.qrc index efe28bd9f..f055c031f 100644 --- a/src/fgt/editpaint/meshlab.qrc +++ b/src/fgt/editpaint/meshlab.qrc @@ -25,6 +25,7 @@ images/sculpt-22.png images/smooth_me.png images/smudge-22.png + images/square.png images/swap-colors-12.png images/switch.png images/undo-24.png diff --git a/src/fgt/editpaint/paintbox.cpp b/src/fgt/editpaint/paintbox.cpp index 041be70c9..e2f1339b1 100644 --- a/src/fgt/editpaint/paintbox.cpp +++ b/src/fgt/editpaint/paintbox.cpp @@ -37,6 +37,24 @@ Paintbox::Paintbox(QWidget * parent, Qt::WindowFlags flags) : QWidget(parent, fl QPoint p=parent->mapToGlobal(QPoint(0,0)); setGeometry(p.x()+parent->width()-width(),p.y(),width(), 100); + brush_viewer->setScene(new QGraphicsScene()); + clone_source_view->setScene(new QGraphicsScene()); + + item = NULL; +/* QPen pen; + getCloneScene()->addLine((qreal)(clone_source_view->width()/2 ), + (qreal)(clone_source_view->height()/2 + 8), + (qreal)(clone_source_view->width()/2), + (qreal)(clone_source_view->height()/2 - 8), pen)->setZValue(1); + + getCloneScene()->addLine((qreal)(clone_source_view->width()/2 + 8), + (qreal)(clone_source_view->height()/2 ), + (qreal)(clone_source_view->width()/2 - 8), + (qreal)(clone_source_view->height()/2), pen)->setZValue(1); + +*/ + //TODO BRUTTO!!!!!!!!!!! + on_brush_box_currentIndexChanged(0); } void Paintbox::setUndoStack(QUndoStack * qus) @@ -46,14 +64,10 @@ void Paintbox::setUndoStack(QUndoStack * qus) QIcon undo = undo_button->icon(); undo_button->setDefaultAction(stack->createUndoAction(undo_button)); undo_button->defaultAction()->setIcon(undo); - mesh_undo_button->setDefaultAction(stack->createUndoAction(mesh_undo_button)); - mesh_undo_button->defaultAction()->setIcon(undo); - + QIcon redo = redo_button->icon(); redo_button->setDefaultAction(stack->createRedoAction(redo_button)); redo_button->defaultAction()->setIcon(redo); - mesh_redo_button->setDefaultAction(stack->createRedoAction(mesh_redo_button)); - mesh_redo_button->defaultAction()->setIcon(redo); } void Paintbox::on_default_colors_clicked() @@ -69,6 +83,20 @@ void Paintbox::on_switch_colors_clicked() background_frame->setColor(temp); } +void Paintbox::on_brush_box_currentIndexChanged(int i) +{ + + if (item != NULL) brush_viewer->scene()->removeItem(item); + + item = brush_viewer->scene()->addPixmap(QPixmap::fromImage( + raster(getBrush(), (int) ((brush_viewer->width()-2) * size_slider->value() / 100.0), + (int)((brush_viewer->height()-2) * size_slider->value() / 100.0), getHardness()) + ) + ); + + brush_viewer->setSceneRect(item->boundingRect()); +} + void Paintbox::setForegroundColor(QColor & c) { foreground_frame->setColor(c); diff --git a/src/fgt/editpaint/paintbox.h b/src/fgt/editpaint/paintbox.h index 3adf221f9..9ebbb1efe 100644 --- a/src/fgt/editpaint/paintbox.h +++ b/src/fgt/editpaint/paintbox.h @@ -24,15 +24,17 @@ #ifndef PAINTBOX_H_ #define PAINTBOX_H_ +#include #include "ui_paintbox.h" /** * The types of tools MUST be defined in the same order as * the buttons inside the tabs. */ -typedef enum {COLOR_PAINT, COLOR_FILL, COLOR_GRADIENT, COLOR_SMOOTH, COLOR_CLONE, COLOR_PICK, +typedef enum {COLOR_PAINT, COLOR_FILL, COLOR_GRADIENT, COLOR_SMOOTH, COLOR_CLONE, COLOR_PICK, COLOR_NOISE, MESH_SELECT, MESH_SMOOTH, MESH_PUSH, MESH_PULL} ToolType; + /** * Mnemonics to address the tabs numbers. These MUST be declared * in the same order as the actual tabs @@ -41,6 +43,8 @@ enum {COLOR_TAB, MESH_TAB, PREFERENCES_TAB}; //TODO add mnemonics for all settings +typedef enum {CIRCLE, SQUARE, PIXMAP} Brush; + /** * This class manages the user interface and is concerned * with emitting appropriate signals. It should not be @@ -53,6 +57,11 @@ class Paintbox : public QWidget, private Ui::Paintbox private: ToolType active[2]; //the active tool for each page QUndoStack * stack; + + ToolType previous_type; + + QGraphicsPixmapItem * item; + inline int getCurrentTab() {return tabs_container->currentIndex();} public: @@ -71,9 +80,9 @@ public: inline int getSizeUnit() { if (getCurrentTab()==MESH_TAB) return mesh_pen_unit->currentIndex(); else return pen_unit->currentIndex();} - inline QPixmap getBrush(int w, int h) { - if (getCurrentTab()==MESH_TAB) return mesh_brush_box->itemIcon(mesh_brush_box->currentIndex()).pixmap(w, h); - else return brush_box->itemIcon(brush_box->currentIndex()).pixmap(w, h);} + inline Brush getBrush() { + if (getCurrentTab()==MESH_TAB) return (Brush)mesh_brush_box->currentIndex(); + else return (Brush)brush_box->currentIndex();} inline int getSearchMode() { return search_mode->currentIndex() + 1; } inline int getOpacity() {return opacity_slider->value();} inline int getSmoothPercentual() { @@ -108,28 +117,114 @@ public: void setForegroundColor(QColor & c); void setBackgroundColor(QColor & c); + //TODO Refactor + QGraphicsScene * getCloneScene() {return clone_source_view->scene();} + QGraphicsPixmapItem * getClonePixmap() {return item;} + void setClonePixmap(QImage & image) + { + if (item != NULL) getCloneScene()->removeItem(item); + item = getCloneScene()->addPixmap(QPixmap::fromImage(image)); + } + void setPixmapCenter(qreal x, qreal y) + { + item->setPos(x + clone_source_view->width()/2.0, y + clone_source_view->height()/2.0); + } + + void restorePreviousType() + { + //TODO Only works as long as types are declared in the same order as buttons + dynamic_cast(hboxLayout1->itemAt(previous_type)->widget())->toggle() ; + } public slots: - void on_pen_button_clicked() {active[COLOR_TAB] = COLOR_PAINT;} - void on_fill_button_clicked() {active[COLOR_TAB] = COLOR_FILL;} - void on_gradient_button_clicked() {active[COLOR_TAB] = COLOR_GRADIENT;} - void on_smooth_button_clicked(){active[COLOR_TAB] = COLOR_SMOOTH;} - void on_clone_button_clicked(){active[COLOR_TAB] = COLOR_CLONE;} - void on_pick_button_clicked(){active[COLOR_TAB] = COLOR_PICK;} - void on_mesh_pick_button_clicked(){active[MESH_TAB] = MESH_SELECT;} - void on_mesh_smooth_button_clicked(){active[MESH_TAB] = MESH_SMOOTH;} - void on_mesh_sculpt_button_clicked(){active[MESH_TAB] = MESH_PUSH;} - void on_mesh_add_button_clicked(){active[MESH_TAB] = MESH_PULL;} - void on_undo_button_clicked(){emit undo();} - void on_redo_button_clicked(){emit redo();} - void on_mesh_undo_button_clicked(){emit undo();} - void on_mesh_redo_button_clicked(){emit redo();} + void on_pen_button_toggled(bool checked) {if(checked) active[COLOR_TAB] = COLOR_PAINT;} + void on_fill_button_toggled(bool checked) {if(checked) active[COLOR_TAB] = COLOR_FILL;} + void on_gradient_button_toggled(bool checked) {if(checked) active[COLOR_TAB] = COLOR_GRADIENT;} + void on_smooth_button_toggled(bool checked){if(checked) active[COLOR_TAB] = COLOR_SMOOTH;} + void on_clone_button_toggled(bool checked){if(checked) active[COLOR_TAB] = COLOR_CLONE;} + void on_pick_button_toggled(bool checked){if(checked) {previous_type = active[COLOR_TAB]; active[COLOR_TAB] = COLOR_PICK;}} + void on_mesh_pick_button_toggled(bool checked){if(checked) active[MESH_TAB] = MESH_SELECT;} + void on_mesh_smooth_button_toggled(bool checked){if(checked) active[MESH_TAB] = MESH_SMOOTH;} + void on_mesh_sculpt_button_toggled(bool checked){if(checked) active[MESH_TAB] = MESH_PUSH;} + void on_mesh_add_button_toggled(bool checked){if(checked) active[MESH_TAB] = MESH_PULL;} + void on_undo_button_toggled(bool checked){if(checked) emit undo();} + void on_redo_button_toggled(bool checked){if(checked) emit redo();} + void on_mesh_undo_button_toggled(bool checked){if(checked) emit undo();} + void on_mesh_redo_button_toggled(bool checked){if(checked) emit redo();} void on_default_colors_clicked(); void on_switch_colors_clicked(); + void on_brush_box_currentIndexChanged(int); + + //TODO Brutti!!! + void on_hardness_slider_valueChanged(int i){on_brush_box_currentIndexChanged(i);} + void on_size_slider_valueChanged(int i){on_brush_box_currentIndexChanged(i);} + + void on_clone_source_load_button_clicked(){QString s = QFileDialog::getOpenFileName(this, + tr("Open Image"), "", tr("Image Files (*.png *.jpg *.bmp)")); + if (!s.isNull()) { + QPixmap pixmap(s); + getCloneScene()->addPixmap(pixmap); + } + } + signals: void undo(); void redo(); }; + +/** + * Returns the "value" of the brush at the given distance from the center + */ +inline float brush(Brush b, float distance, float dx, float dy, float hardness) +{ + float op = 0.0; /*< 0 means totally colored, 1 totally blank, but right before returning, its meaning will be the opposite*/ + + switch (b) + { + case CIRCLE : //circle + if (distance >= 1.0) op = 1.0; + else if (distance * 100.0 > hardness) //if after the treshold of total color + { + op = (distance * 100.0 - hardness)/ (float) (100 - hardness); + } + op = 1 - op; + break; + + case SQUARE : + dx = vcg::math::Abs(dx * 141.4213562); dy = vcg::math::Abs(dy * 141.4213562); //multiply by sqrt(2) and by 100 + if (dx >= 100 || dy >= 100 ) op = 1.0; + else if (dx > hardness || dy > hardness) + { + op = (vcg::math::Max(dx, dy) - hardness)/ (float) (100 - hardness); + } + op = 1 - op; + break; + + case PIXMAP : + + default: + break; + } + + return op; +} + +inline QImage raster(Brush b, int w, int h, float hardness) +{ + float cx = w/2.0; + float cy = h/2.0; + QImage image(w, h, QImage::Format_RGB32); + for (float x = 0; x < w; x++){ + for (float y = 0; y < h; y++){ + float _x = ((x - cx) * 1.4142 * 2)/w; + float _y = ((y - cy) * 1.4142 * 2)/h; + int op = 255 * (1 - brush(b, sqrt(_x * _x + _y * _y), _x, _y, hardness)); + image.setPixel(x, y, qRgb(op, op, op)); + } + } + return image; +} + #endif /*PAINTBOX_H_*/ diff --git a/src/fgt/editpaint/paintbox.ui b/src/fgt/editpaint/paintbox.ui index cff01acac..2a09a7e92 100644 --- a/src/fgt/editpaint/paintbox.ui +++ b/src/fgt/editpaint/paintbox.ui @@ -18,8 +18,64 @@ Vertex Painting - - + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + false + + + + 0 + 0 + + + + Undo + + + :/images/undo-24.png + + + Qt::ToolButtonTextBesideIcon + + + + + + + false + + + + 0 + 0 + + + + Redo + + + :/images/redo-24.png + + + Qt::ToolButtonTextBesideIcon + + + + + + + @@ -113,7 +169,7 @@ - false + true ... @@ -441,55 +497,27 @@ - + QFrame::StyledPanel QFrame::Raised - - - false - + - 11 - 47 - 131 - 30 + 34 + 4 + 81 + 81 - - Redo + + Qt::ScrollBarAlwaysOff - - :/images/redo-24.png - - - Qt::ToolButtonTextBesideIcon - - - - - false - - - - 11 - 11 - 131 - 30 - - - - Undo - - - :/images/undo-24.png - - - Qt::ToolButtonTextBesideIcon + + Qt::ScrollBarAlwaysOff @@ -703,7 +731,7 @@ p, li { white-space: pre-wrap; } - false + true @@ -719,7 +747,20 @@ p, li { white-space: pre-wrap; } Circle - :/brushes/circle.png + :/images/circle.png + + + + + Square + + + :/images/square.png + + + + + Pixmap @@ -1050,6 +1091,35 @@ p, li { white-space: pre-wrap; } + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Qt::ScrollBarAsNeeded + + + QGraphicsView::DontAdjustForAntialiasing + + + + + + + Load From File + + + + + + + Qt::Vertical @@ -1121,7 +1191,7 @@ p, li { white-space: pre-wrap; } - false + true ... @@ -1156,7 +1226,7 @@ p, li { white-space: pre-wrap; } - false + true ... @@ -1195,68 +1265,6 @@ p, li { white-space: pre-wrap; } - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - false - - - - 0 - 0 - - - - Qt::RightToLeft - - - Undo - - - :/images/undo-24.png - - - Qt::ToolButtonTextBesideIcon - - - - - - - false - - - - 0 - 0 - - - - Qt::LeftToRight - - - Redo - - - :/images/redo-24.png - - - Qt::ToolButtonTextBesideIcon - - - - - - @@ -1364,7 +1372,20 @@ p, li { white-space: pre-wrap; } Circle - :/brushes/circle.png + :/images/circle.png + + + + + Square + + + :/images/square.png + + + + + Pixmap @@ -1518,7 +1539,7 @@ p, li { white-space: pre-wrap; } - false + true QFrame::StyledPanel @@ -1714,12 +1735,12 @@ p, li { white-space: pre-wrap; } setValue(int) - 103 - 56 + 204 + 177 - 103 - 56 + 322 + 183 @@ -1730,12 +1751,12 @@ p, li { white-space: pre-wrap; } setValue(int) - 103 - 56 + 228 + 264 - 103 - 56 + 322 + 270 @@ -1746,12 +1767,12 @@ p, li { white-space: pre-wrap; } setValue(int) - 103 - 56 + 222 + 413 - 103 - 56 + 322 + 419 @@ -1762,12 +1783,12 @@ p, li { white-space: pre-wrap; } setValue(int) - 103 - 56 + 322 + 419 - 103 - 56 + 222 + 413 @@ -1778,12 +1799,12 @@ p, li { white-space: pre-wrap; } setValue(int) - 103 - 56 + 322 + 183 - 103 - 56 + 204 + 177 @@ -1798,8 +1819,8 @@ p, li { white-space: pre-wrap; } 199 - 263 - 199 + 322 + 215 @@ -1814,8 +1835,8 @@ p, li { white-space: pre-wrap; } 197 - 247 - 197 + 261 + 212 @@ -1826,12 +1847,12 @@ p, li { white-space: pre-wrap; } setValue(int) - 103 - 56 + 228 + 297 - 103 - 56 + 322 + 303 @@ -1842,12 +1863,12 @@ p, li { white-space: pre-wrap; } setValue(int) - 103 - 56 + 322 + 303 - 103 - 56 + 228 + 297 @@ -1858,12 +1879,12 @@ p, li { white-space: pre-wrap; } setValue(int) - 103 - 56 + 322 + 270 - 103 - 56 + 228 + 264 @@ -1878,8 +1899,8 @@ p, li { white-space: pre-wrap; } 273 - 261 - 275 + 322 + 285 @@ -1890,8 +1911,8 @@ p, li { white-space: pre-wrap; } setValue(int) - 265 - 270 + 322 + 285 247 @@ -1926,8 +1947,8 @@ p, li { white-space: pre-wrap; } 51 - 47 - 56 + 67 + 257 @@ -1974,8 +1995,8 @@ p, li { white-space: pre-wrap; } 47 - 49 - 183 + 60 + 217 @@ -2070,8 +2091,8 @@ p, li { white-space: pre-wrap; } 56 - 130 - 180 + 141 + 214 @@ -2098,12 +2119,12 @@ p, li { white-space: pre-wrap; } setVisible(bool) - 55 - 50 + 87 + 72 - 96 - 56 + 116 + 194 @@ -2130,8 +2151,8 @@ p, li { white-space: pre-wrap; } setVisible(bool) - 131 - 42 + 161 + 72 165 @@ -2166,8 +2187,8 @@ p, li { white-space: pre-wrap; } 49 - 103 - 56 + 123 + 438 @@ -2178,12 +2199,12 @@ p, li { white-space: pre-wrap; } setVisible(bool) - 103 - 56 + 161 + 72 - 103 - 56 + 123 + 438 @@ -2198,8 +2219,8 @@ p, li { white-space: pre-wrap; } 53 - 103 - 56 + 123 + 257 @@ -2214,8 +2235,8 @@ p, li { white-space: pre-wrap; } 56 - 103 - 56 + 123 + 525 @@ -2226,12 +2247,12 @@ p, li { white-space: pre-wrap; } setVisible(bool) - 103 - 56 + 161 + 72 - 103 - 56 + 123 + 525 @@ -2242,12 +2263,12 @@ p, li { white-space: pre-wrap; } setVisible(bool) - 103 - 56 + 161 + 72 - 103 - 56 + 123 + 257 @@ -2262,8 +2283,8 @@ p, li { white-space: pre-wrap; } 56 - 42 - 56 + 62 + 525 @@ -2278,8 +2299,8 @@ p, li { white-space: pre-wrap; } 56 - 72 - 56 + 92 + 257