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