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