1 /****************************************************************************
2 * Rgb Triangulations Plugin *
3 * *
4 * Author: Daniele Panozzo (daniele.panozzo@gmail.com) *
5 * Copyright(C) 2007 *
6 * DISI - Department of Computer Science *
7 * University of Genova *
8 * *
9 * All rights reserved. *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
20 * for more details. *
21 ****************************************************************************/
22
23 /****************************************************************************
24 * The code of the class InteractiveEdit is based on the plugin editpaint, *
25 * developed by Gfrei Andreas. *
26 ****************************************************************************/
27
28 #include "interactiveEdit.h"
29
30 namespace rgbt
31 {
32
InteractiveEdit()33 InteractiveEdit::InteractiveEdit()
34 {
35 pixels = 0;
36 first = 1;
37 isDragging = false;
38
39 }
40
~InteractiveEdit()41 InteractiveEdit::~InteractiveEdit()
42 {
43 }
44
isIn(const QPointF & p0,const QPointF & p1,float dx,float dy,float radius,float * dist)45 inline int isIn(const QPointF &p0, const QPointF &p1, float dx, float dy,
46 float radius, float *dist)
47 {
48 if (p0!=p1)
49 { /** this must be checked first, because of the color decrease tool */
50 float x2=(p1.x()-p0.x());
51 float y2=(p1.y()-p0.y());
52 //double l=sqrt(x2*x2+y2*y2);
53 float l_square=x2*x2+y2*y2;
54 float r=(dx-p0.x())*(p1.x()-p0.x())+(dy-p0.y())*(p1.y()-p0.y());
55 //r=r/(l*l);
56 r=r/l_square;
57
58 float px=p0.x()+r*(p1.x()-p0.x());
59 float py=p0.y()+r*(p1.y()-p0.y());
60
61 px=px-dx;
62 py=py-dy;
63
64 if (r>=0 && r<=1 && (px*px+py*py<radius*radius))
65 {
66 *dist=sqrt(px*px+py*py)/radius;
67 return 1;
68 }
69 }
70
71 // there could be some problem when point is nearer p0 or p1 and viceversa
72 // so i have to check both. is only needed with smooth_borders
73 bool found=0;
74 float x1=(dx-p1.x());
75 float y1=(dy-p1.y());
76 float bla1=x1*x1+y1*y1;
77 if (bla1<radius*radius)
78 {
79 *dist=sqrt(bla1)/radius;
80 found=1;/*return 1;*/
81 }
82
83 if (p0==p1)
84 return found;
85
86 float x0=(dx-p0.x());
87 float y0=(dy-p0.y());
88 float bla0=x0*x0+y0*y0;
89 if (bla0<radius*radius)
90 {
91 if (found==1)
92 *dist=std::min((*dist), (float)sqrt(bla0)/radius);
93 else
94 *dist=sqrt(bla0)/radius;
95 return 1;
96 }
97
98 return found;
99 }
100
101 /** checks if a point is in a triangle (2D) */
pointInTriangle(const QPointF & p,const QPointF & a,const QPointF & b,const QPointF & c)102 inline bool pointInTriangle(const QPointF &p, const QPointF &a,
103 const QPointF &b, const QPointF &c)
104 {
105 float fab=(p.y()-a.y())*(b.x()-a.x()) - (p.x()-a.x())*(b.y()-a.y());
106 float fbc=(p.y()-c.y())*(a.x()-c.x()) - (p.x()-c.x())*(a.y()-c.y());
107 float fca=(p.y()-b.y())*(c.x()-b.x()) - (p.x()-b.x())*(c.y()-b.y());
108 if (fab*fbc>0 && fbc*fca>0)
109 return true;
110 return false;
111 }
112
pointInTriangle(const float p_x,const float p_y,const float a_x,const float a_y,const float b_x,const float b_y,const float c_x,const float c_y)113 inline bool pointInTriangle(const float p_x, const float p_y, const float a_x,
114 const float a_y, const float b_x, const float b_y, const float c_x,
115 const float c_y)
116 {
117 float fab=(p_y-a_y)*(b_x-a_x) - (p_x-a_x)*(b_y-a_y);
118 float fbc=(p_y-c_y)*(a_x-c_x) - (p_x-c_x)*(a_y-c_y);
119 float fca=(p_y-b_y)*(c_x-b_x) - (p_x-b_x)*(c_y-b_y);
120 if (fab*fbc>0 && fbc*fca>0)
121 return true;
122 return false;
123 }
124
125 /** checks if a triangle is front or backfaced */
isFront(const QPointF & a,const QPointF & b,const QPointF & c)126 inline bool isFront(const QPointF &a, const QPointF &b, const QPointF &c)
127 {
128 return (b.x()-a.x())*(c.y()-a.y())-(b.y()-a.y())*(c.x()-a.x())>0;
129 }
130
131 /** checks if a triangle is front or backfaced */
isFront(const float a_x,const float a_y,const float b_x,const float b_y,const float c_x,const float c_y)132 inline bool isFront(const float a_x, const float a_y, const float b_x,
133 const float b_y, const float c_x, const float c_y)
134 {
135 return (b_x-a_x)*(c_y-a_y)-(b_y-a_y)*(c_x-a_x)>0;
136 }
137
lineHitsCircle(QPointF & LineStart,QPointF & LineEnd,QPointF & CircleCenter,float Radius,QPointF * const pOut=0)138 inline bool lineHitsCircle(QPointF& LineStart, QPointF& LineEnd,
139 QPointF& CircleCenter, float Radius, QPointF* const pOut = 0)
140 {
141 const float RadiusSq = Radius * Radius;
142 QPointF PMinusM =LineStart - CircleCenter;
143
144 float pm_squ=PMinusM.x()*PMinusM.x()+PMinusM.y()*PMinusM.y();
145 if (pm_squ <= RadiusSq)
146 { /// startpoint in circle
147 if (pOut)
148 *pOut = LineStart;
149 return true;
150 }
151 QPointF LineDir=LineEnd - LineStart; /// line direction
152 // u * (p - m)
153 const float UDotPMinusM = LineDir.x()*PMinusM.x()+LineDir.y()*PMinusM.y();//Vector2D_Dot(LineDir, PMinusM);
154 // u*u
155 const float LineDirSq = LineDir.x()*LineDir.x()+LineDir.y()*LineDir.y();
156 // (u * (p - m))^2
157 // - (u*u * ((p - m)^2 - r^2))
158 const float d = UDotPMinusM * UDotPMinusM - LineDirSq * (pm_squ - RadiusSq);
159
160 if (d < 0.0f)
161 return false;
162 else if (d < 0.0001f)
163 {
164 const float s = -UDotPMinusM / LineDirSq;
165 if (s< 0.0f || s> 1.0f)return false;
166 else
167 {
168 if(pOut) *pOut = LineStart + s * LineDir;
169 return true;
170 }
171 }
172 else
173 {
174 const float s = (-UDotPMinusM - sqrtf(d)) / LineDirSq;
175 if(s < 0.0f || s > 1.0f) return false;
176 else
177 {
178 if(pOut) *pOut = LineStart + s * LineDir;
179 return true;
180 }
181 }
182 }
183
DrawXORCircle(GLArea * gla,bool doubleDraw)184 void InteractiveEdit::DrawXORCircle(GLArea * gla, bool doubleDraw)
185 {
186 int PEZ=18;
187 /** paint the normal circle in pixel-mode */
188 glMatrixMode(GL_PROJECTION);
189 glPushMatrix();
190 glLoadIdentity();
191 glOrtho(0,gla->curSiz.width(),gla->curSiz.height(),0,-1,1);
192 glMatrixMode(GL_MODELVIEW);
193 glPushMatrix();
194 glLoadIdentity();
195 glPushAttrib(GL_ENABLE_BIT);
196 glDisable(GL_DEPTH_TEST);
197 glDisable(GL_LIGHTING);
198 glDisable(GL_TEXTURE_2D);
199 glEnable(GL_COLOR_LOGIC_OP);
200 glLogicOp(GL_XOR);
201 glColor3f(1,1,1);
202
203 QPoint mid= QPoint(cur.x(),/*gla->curSiz.height()-*/cur.y());
204 if(doubleDraw)
205 {
206 glBegin(GL_LINE_LOOP);
207 for (int lauf=0; lauf<PEZ; lauf++)
208 glVertex2f(mid.x()+sin(M_PI*(float)lauf/9.0)*pen.radius,mid.y()+cos(M_PI*(float)lauf/9.0)*pen.radius);
209 glEnd();
210 }
211
212 glBegin(GL_LINE_LOOP);
213 for (int lauf=0; lauf<PEZ; lauf++)
214 glVertex2f(mid.x()+sin(M_PI*(float)lauf/9.0)*pen.radius,mid.y()+cos(M_PI*(float)lauf/9.0)*pen.radius);
215 glEnd();
216
217 glDisable(GL_LOGIC_OP);
218 // Closing 2D
219 glPopAttrib();
220 glPopMatrix(); // restore modelview
221 glMatrixMode(GL_PROJECTION);
222 glPopMatrix();
223 glMatrixMode(GL_MODELVIEW);
224
225 }
226
227 //getInternFaces(m,&curSel,&newSel,&faceSel,gla,pen,cur,prev,pixels,mvmatrix,projmatrix,viewport);
228 /** finds the faces or vertexes in the circle */
getInternFaces(MeshModel & m,list<int> * actual,vector<Vert_Data> * risult,vector<CMeshO::FacePointer> * face_risult,GLArea * gla,Penn & pen,QPoint & cur,QPoint & prev,GLfloat * pixels,double mvmatrix[16],double projmatrix[16],GLint viewport[4])229 void InteractiveEdit::getInternFaces(MeshModel & m,list<int> *actual,vector<Vert_Data> * risult, vector<CMeshO::FacePointer> * face_risult,
230 GLArea * gla,Penn &pen,QPoint &cur, QPoint &prev, GLfloat * pixels,
231 double mvmatrix[16],double projmatrix[16],GLint viewport[4])
232 {
233
234 QHash <CFaceO *,CFaceO *> selected;
235 QHash <CVertexO *,CVertexO *> sel_vert;
236 list<int>::iterator fpi;
237 vector<CMeshO::FacePointer> temp_po;
238
239 if (actual->size()==0)
240 {
241 CMeshO::FaceIterator fi;
242 for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi)
243 if(!(*fi).IsD())
244 {
245 temp_po.push_back((&*fi));
246 }
247 }
248 else
249 for(fpi=actual->begin();fpi!=actual->end();++fpi)
250 {
251 temp_po.push_back(&(m.cm.face[*fpi]));
252 }
253
254 actual->clear();
255
256 QPointF mid=QPointF(cur.x(),gla->curSiz.height()- cur.y());
257 QPointF mid_prev=QPointF(prev.x(),gla->curSiz.height()- prev.y());
258
259 QPointF p[3],z[3];
260 double tx,ty,tz;
261
262 bool backface=pen.backface;
263 bool invisible=pen.invisible;
264 for (unsigned int lauf2=0; lauf2<temp_po.size(); lauf2++)
265 {
266 CFaceO * fac=temp_po.at(lauf2);
267 //std::cout << fac->IsD() << std::endl;
268 int intern=0;
269 int checkable=0; /// to avoid problems when the pen is smaller then the polys
270 for (int lauf=0; lauf<3; lauf++)
271 {
272 //if ((fac)->V(lauf) - &(m.cm.vert[0]) < 0 || (fac)->V(lauf) - &(m.cm.vert[0]) >= m.cm.vert.size())
273 // continue;
274 if (gluProject((fac)->V(lauf)->P()[0],(fac)->V(lauf)->P()[1],(fac)->V(lauf)->P()[2],
275 mvmatrix,projmatrix,viewport,&tx,&ty,&tz)==GL_TRUE)
276 {
277 checkable++;
278 }
279 if (tz<0 || tz>1)
280 {
281 checkable--;
282 }
283 p[lauf].setX(tx); p[lauf].setY(ty);
284 if (tx>=0 && tx<viewport[2] && ty>=0 && ty<viewport[3])
285 {
286 z[lauf].setX(tz);
287 z[lauf].setY((float)pixels[(int)(((int)ty)*viewport[2]+(int)tx)]);
288 }
289 else
290 {
291 z[lauf].setX(1); z[lauf].setY(0);
292 }
293 }
294 if (backface || isFront(p[0],p[1],p[2]))
295 {
296 //checkable++;
297 /// could be useful to calc the medium of z in the matrix
298 for (int lauf=0; lauf<3; lauf++)
299 {
300 if (invisible || (z[lauf].x()<=z[lauf].y()+0.003))
301 {
302 float dist;
303 if (isIn(mid,mid_prev,p[lauf].x(),p[lauf].y(),pen.radius,&dist)==1)
304 {
305 intern=1;
306 if (!sel_vert.contains(fac->V(lauf)))
307 {
308 Vert_Data d;
309 d.v=fac->V(lauf);
310 d.distance=dist;
311 risult->push_back(/*fac->V(lauf)*/d);
312 sel_vert.insert(fac->V(lauf),fac->V(lauf));
313 }
314 }
315 QPointF pos_res;
316 //if (pen.paintutensil==SELECT && intern==0 && lineHitsCircle(p[lauf],p[(lauf+1)%3],mid,pen.radius,&pos_res))
317 if (intern==0 && lineHitsCircle(p[lauf],p[(lauf+1)%3],mid,pen.radius,&pos_res))
318 {
319 intern=1; continue;
320 }
321 }
322 }
323 if (checkable==3 && intern==0 && (pointInTriangle(mid,p[0],p[1],p[2]) || pointInTriangle(mid_prev,p[0],p[1],p[2])))
324 {
325 intern=-1;
326 }
327 }
328 if (checkable==3 && intern!=0 && !selected.contains(fac))
329 {
330 selected.insert((fac),(fac));
331 actual->push_back(fac->Index());
332 vector <CFaceO *> surround;
333 for (int lauf=0; lauf<3; lauf++)
334 if (!selected.contains(fac->FFp(lauf)))
335 temp_po.push_back(fac->FFp(lauf));
336
337 if (intern!=0)
338 face_risult->push_back(fac);
339 }
340 }
341 }
342
RgbInteractiveEdit(CMeshO & m,RgbInfo & i,TopologicalOpC & to)343 RgbInteractiveEdit::RgbInteractiveEdit(CMeshO& m, RgbInfo& i, TopologicalOpC& to)
344 {
345 this->m = &m;
346 this->info = &i;
347 this->to = &to;
348 }
~RgbInteractiveEdit()349 RgbInteractiveEdit::~RgbInteractiveEdit()
350 {
351
352 }
353
vertexToRemove(RgbVertexC & v,int * level,double * lenght)354 bool RgbInteractiveEdit::vertexToRemove(RgbVertexC& v, int* level, double* lenght)
355 {
356 bool blenght;
357 bool blevel;
358
359 if (lenght)
360 {
361 double edgelenght = maxEdge(v);
362 blenght = edgelenght < *lenght;
363 }
364 else
365 blenght = false;
366
367 if (level)
368 {
369 //int edgelevel = maxEdgeLevel(v);
370 int edgelevel = v.getLevel();
371 blevel = edgelevel > *level;
372 }
373 else
374 blevel = false;
375 //std::cout << blenght << " " << blevel << std::endl;
376 return (blenght || blevel);
377
378 }
edgeToSplit(RgbTriangleC & t,int index,int * level,double * lenght)379 bool RgbInteractiveEdit::edgeToSplit(RgbTriangleC& t,int index, int* level, double* lenght)
380 {
381 bool blenght;
382 bool blevel;
383
384 if (lenght)
385 {
386 double edgelenght = edgeLenght(t,index);
387 blenght = edgelenght > *lenght;
388 }
389 else
390 blenght = false;
391
392 if (level)
393 {
394 int edgelevel = t.getEdgeLevel(index);
395 blevel = edgelevel < *level;
396 }
397 else
398 blevel = false;
399
400 return (blenght || blevel);
401 }
processVertex(int v,int * level,double * lenght)402 void RgbInteractiveEdit::processVertex(int v, int* level, double* lenght)
403 {
404 RgbTriangleC t;
405 int index;
406 if (IsValidVertex(v,m,info,&t,&index,true))
407 {
408 if (vertexToRemove(t.V(index),level,lenght))
409 {
410 if (RgbPrimitives::vertexRemoval_Possible(t,index))
411 {
412 //vector<RgbTriangleC> vt;
413 //vt.reserve(4);
414 RgbPrimitives::vertexRemoval(t,index,*to);
415 }
416 }
417 }
418 }
419
processEdge(int v1,int v2,int * level,double * lenght)420 void RgbInteractiveEdit::processEdge(int v1,int v2, int* level, double* lenght)
421 {
422 RgbTriangleC t;
423 int index;
424
425 if (IsValidEdge(v1,v2,m,info,&t,&index))
426 {
427 if (edgeToSplit(t,index,level,lenght))
428 {
429 vector<RgbTriangleC> vt;
430 RgbPrimitives::recursiveEdgeSplit(t,index,*to,&vt);
431 }
432 }
433 }
434
minEdgeLevel(RgbVertexC & v)435 int RgbInteractiveEdit::minEdgeLevel(RgbVertexC& v)
436 {
437 vector<RgbEdgeC> ve;
438 vector<RgbEdgeC>::iterator it;
439 v.VF(ve);
440 int tmp = ve[0].t.getEdgeLevel(ve[0].index);
441 for (it = ve.begin();it != ve.end(); it++)
442 {
443 int l = it->t.getEdgeLevel(it->index);
444 if (tmp > l)
445 tmp = l;
446 }
447 return tmp;
448 }
449
maxEdgeLevel(RgbVertexC & v)450 bool RgbInteractiveEdit::maxEdgeLevel(RgbVertexC& v)
451 {
452 vector<RgbEdgeC> ve;
453 vector<RgbEdgeC>::iterator it;
454 v.VF(ve);
455 //std::cout << ve.size() << std::endl;
456 int tmp = ve[0].t.getEdgeLevel(ve[0].index);
457 for (it = ve.begin();it != ve.end(); it++)
458 {
459 int l = it->t.getEdgeLevel(it->index);
460 if (tmp > l)
461 tmp = l;
462 }
463 return tmp;
464 }
465
maxEdge(RgbVertexC & v)466 double RgbInteractiveEdit::maxEdge(RgbVertexC& v)
467 {
468 vector<double> vv;
469 vv.reserve(6);
470 VE(v,vv);
471 double value = vv[0];
472 for (unsigned int i = 1; i < vv.size(); ++i)
473 {
474 if (vv[i] > value)
475 value = vv[i];
476 }
477 return value;
478 }
479
edgeLenght(RgbTriangleC & t,int index)480 double RgbInteractiveEdit::edgeLenght(RgbTriangleC& t, int index)
481 {
482 Point v1 = t.getVertexCoord(index);
483 Point v2 = t.getVertexCoord((index+1)%3);
484 return (v2-v1).Norm();
485 }
486
IsValidVertex(int vp,CMeshO * m,RgbInfo * info,RgbTriangleC * t,int * ti,bool ignoreNew)487 bool RgbInteractiveEdit::IsValidVertex(int vp, CMeshO* m,RgbInfo* info, RgbTriangleC* t, int* ti, bool ignoreNew)
488 {
489 assert((unsigned int)vp < m->vert.size());
490 if (m->vert[vp].IsD())
491 return false;
492 VertexType& v = m->vert[vp];
493 if (!v.VFp())
494 return false;
495
496 RgbTriangleC tf = RgbTriangleC(m,info,v.VFp()->Index());
497 assert(!tf.face()->IsD());
498 int tfi = v.VFi();
499 assert(tf.V(tfi).index == vp);
500
501 if (tf.getVertexIsNew(tfi) && !ignoreNew)
502 return false;
503
504 if (t)
505 *t = tf;
506 if (ti)
507 *ti = tfi;
508
509 return true;
510 }
511
IsValidEdge(int v1,int v2,CMeshO * m,RgbInfo * info,RgbTriangleC * t,int * ti)512 bool RgbInteractiveEdit::IsValidEdge(int v1,int v2, CMeshO* m,RgbInfo* info, RgbTriangleC* t, int* ti)
513 {
514 assert((unsigned int)v1 < m->vert.size());
515 assert((unsigned int)v2 < m->vert.size());
516
517 if (m->vert[v1].IsD() || m->vert[v2].IsD())
518 return false;
519
520 VertexType& v = m->vert[v1];
521 RgbTriangleC tf = RgbTriangleC(m,info,v.VFp()->Index());
522 int tfi = v.VFi();
523 assert(tf.V(tfi).index == v1);
524
525 VertexType& va = m->vert[v2];
526 RgbTriangleC tfa = RgbTriangleC(m,info,va.VFp()->Index());
527 int tfia = va.VFi();
528 assert(tfa.V(tfia).index == v2);
529
530 vector<RgbTriangleC> vf;
531 RgbPrimitives::vf(tf,tfi,vf);
532
533 for (unsigned int i = 0; i < vf.size(); ++i)
534 {
535 RgbTriangleC& tt = vf[i];
536 int k = 0;
537 while(tt.V(k).index != v1)
538 {
539 assert(k <= 2);
540 k++;
541 }
542
543 if (tt.V((k+1)%3).index == v2)
544 {
545 if (t)
546 *t = tt;
547 if (ti)
548 *ti = k;
549 return true;
550 }
551
552 }
553 return false;
554 }
555
VE(RgbVertexC & v,vector<double> & vv)556 void RgbInteractiveEdit::VE(RgbVertexC& v, vector<double>& vv)
557 {
558 int i;
559
560 FacePointer fp = v.vert().VFp();
561 int fi = v.vert().VFi();
562 vcg::face::Pos<FaceType> pos(fp,fi);
563 CMeshO::FacePointer first = pos.F();
564
565 RgbTriangleC tmp = RgbTriangleC(v.m,v.rgbInfo,pos.F()->Index());
566 assert(tmp.containVertex(v.index));
567 tmp.containVertex(v.index,&i);
568 assert(i>=0 && i<= 2);
569
570 vv.push_back(edgeLenght(tmp,i));
571
572 pos.FlipF();
573 pos.FlipE();
574
575 while(pos.F() && (pos.F() != first))
576 {
577 RgbTriangleC tmp = RgbTriangleC(v.m,v.rgbInfo,pos.F()->Index());
578 assert(tmp.containVertex(v.index));
579 tmp.containVertex(v.index,&i);
580 assert(i>=0 && i<= 2);
581
582 vv.push_back(edgeLenght(tmp,i));
583
584 pos.FlipF();
585 pos.FlipE();
586 assert(pos.F()->V(0) == fp->V(fi) || pos.F()->V(1) == fp->V(fi) || pos.F()->V(2) == fp->V(fi));
587 assert(!fp->IsD());
588 }
589 }
590
591 }
592