1 /*
2  * File:         tools.c
3  *
4  * Description:  performs various manipulations on verts, etc.
5  *               Anything more complicated than a simple translation of a
6  *               single vert should end up in here.
7  *
8  *
9  * This source code is part of kludge3d, and is released under the
10  * GNU General Public License.
11  *
12  *
13  */
14 
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <math.h>
20 
21 
22 #include "model.h"
23 #include "tools.h"
24 #include "undo.h"
25 #include "vector.h"
26 #include "misc.h"
27 #include "selection.h"
28 
29 #ifdef MEMWATCH
30 #include "memwatch.h"
31 #endif
32 
33 
34 /* STRUCTS **************************************************************/
35 
36 typedef struct _Edge Edge;
37 struct _Edge {
38 	Vertex *v1;
39 	Vertex *v2;
40 	int flag;
41 };
42 
43 
44 /* PROTOTYPES ***********************************************************/
45 
46 GSList *tools_find_near_verts( GSList *verts, Vertex *v ) ;
47 void tools_find_texcoords_for_vert( Poly *p, Vertex *vert, GSList *verts,
48 	float *u, float *v ) ;
49 void tools_find_texcoords_for_middlevert( Poly *p, float *u, float *v ) ;
50 
51 GSList * tools_convert_edgelist_to_vertlist( GSList *edges ) ;
52 int tools_edgelist_can_be_subdivided( GSList *edges ) ;
53 GSList * tools_sort_edge_into_bins( GSList **bins, Edge *edge ) ;
54 GSList *tools_find_outside_edges( GSList *polys ) ;
55 gboolean tools_edges_isNotEqual( Edge *e1, Edge *e2 ) ;
56 gboolean tools_edges_areConnected( Edge *e1, Edge *e2 ) ;
57 gboolean tools_edge_hasVertex( Edge *e, Vertex *v ) ;
58 
59 
60 /* VERTEX TOOLS *********************************************************/
61 
tools_vertices_move(Model * model,GSList * verts,float deltaX,float deltaY,float deltaZ)62 void tools_vertices_move( Model *model, GSList *verts, float deltaX, float deltaY, float deltaZ ) {
63 
64     for ( ; verts != NULL; verts = verts->next ) {
65         // go through all the vertices and move them
66 
67         vertex_move( ( Vertex * ) verts->data, deltaX, deltaY, deltaZ );
68     }
69 }
70 
71 
tools_vertices_snap_to_grid(Model * model,GSList * verts,float spacing)72 void tools_vertices_snap_to_grid( Model *model, GSList * verts, float spacing ) {
73 
74     Vertex * v = NULL;
75     float mult;
76     int i;
77 
78     while( verts != NULL ) {
79         v = (Vertex*)verts->data;
80 
81         for( i = 0; i < 3; i++ ) {
82             mult = v->v[i] / spacing;
83             v->v[i] = spacing * rint( mult );
84         }
85 
86         verts = verts->next;
87     }
88 
89     action_do( model, ACTION_UNKNOWN, NULL, NULL, NULL, NULL, NULL, NULL );
90 }
91 
92 
tools_vertices_snap_together(Model * model,GSList * plist)93 void tools_vertices_snap_together( Model *model, GSList * plist ) {
94 
95     GSList * pts = NULL;
96     Vertex average;
97     Vertex * p = NULL;
98     int i;
99 
100     vertices_find_mean( plist, &average );
101 
102     pts = plist;
103     while( pts != NULL ) {
104         p = (Vertex*)pts->data;
105 
106         for( i = 0; i < 3; i++ ) {
107             p->v[i] = average.v[i];
108         }
109 
110         pts = pts->next;
111     }
112 
113     action_do( model, ACTION_UNKNOWN, NULL, NULL, NULL, NULL, NULL, NULL );
114 }
115 
116 
tools_vertices_snap_together_along_axis(Model * model,GSList * plist,int axis)117 void tools_vertices_snap_together_along_axis( Model *model, GSList * plist, int axis ) {
118 
119     GSList * pts = NULL;
120     Vertex average;
121     Vertex * p = NULL;
122     int i;
123 
124     vertices_find_mean( plist, &average );
125 
126     pts = plist;
127     while( pts != NULL ) {
128         p = (Vertex*)pts->data;
129 
130         // align vertices along active axis only
131         for( i = 0; i < 3; i++ ) {
132             if( i != axis )
133                 p->v[i] = average.v[i];
134         }
135 
136         pts = pts->next;
137     }
138 
139     action_do( model, ACTION_UNKNOWN, NULL, NULL, NULL, NULL, NULL, NULL );
140 }
141 
142 
tools_vertices_rotate_90(Model * model,GSList * pts,int axis)143 void tools_vertices_rotate_90( Model *model, GSList * pts, int axis ) {
144 
145     float tempf[4];
146     int tempi[1];
147 
148     tempf[0] = tempf[1] = tempf[2] = 0.0f;
149     tempf[3] = M_PI / 2.0;
150     tempi[0] = axis;
151 
152     action_do( model, ACTION_VERTEX_ROT, pts, NULL, NULL, tempf, tempi, NULL );
153 
154     while( pts != NULL ) {
155 
156         Vertex* p = (Vertex*)(pts->data);
157         float temp;
158 
159         if( axis == 0 ) {
160             temp = p->v[1];
161             p->v[1] = p->v[2];
162             p->v[2] = temp * -1.0;
163         } else if( axis == 1 ) {
164             temp = p->v[0];
165             p->v[0] = p->v[2];
166             p->v[2] = temp * -1.0;
167         } else if( axis == 2 ) {
168             temp = p->v[0];
169             p->v[0] = p->v[1];
170             p->v[1] = temp * -1.0;
171         }
172 
173         pts = pts->next;
174     }
175 
176 }
177 
tools_vertices_rotate(Model * model,GSList * pts,int axis,float angle)178 void tools_vertices_rotate( Model *model, GSList * pts, int axis, float angle ) {
179 
180     float tempf[4];
181     int tempi[1];
182     float cosine = cos(angle);
183     float sine = sin(angle);
184 
185     tempf[0] = tempf[1] = tempf[2] = 0.0f;
186     tempf[3] = angle;
187     tempi[0] = axis;
188 
189     action_do( model, ACTION_VERTEX_ROT, pts, NULL, NULL, tempf, tempi, NULL );
190 
191     while( pts != NULL ) {
192 
193         Vertex* p = (Vertex*)(pts->data);
194         float temp0, temp1, temp2;
195 
196         if( axis == 0 ) {
197             temp1 = p->v[1];
198             temp2 = p->v[2];
199 
200 	    p->v[1] = temp1*cosine - temp2*sine;
201 	    p->v[2] = temp1*sine + temp2*cosine;
202         } else if( axis == 1 ) {
203             temp0 = p->v[0];
204             temp2 = p->v[2];
205 
206 	    p->v[0] =   temp0*cosine + temp2*sine;
207 	    p->v[2] = - temp0*sine + temp2*cosine;
208         } else if( axis == 2 ) {
209             temp0 = p->v[0];
210             temp1 = p->v[1];
211 
212 	    p->v[0] = temp0*cosine - temp1*sine;
213 	    p->v[1] = temp0*sine + temp1*cosine;
214         }
215 
216         pts = pts->next;
217     }
218 
219 }
220 
tools_vertices_rotate_about_point(Model * model,GSList * pts,int axis,float pt[],float angle,int register_with_undo_sys)221 void tools_vertices_rotate_about_point( Model *model,
222 	GSList * pts, int axis, float pt[], float angle,
223 	int register_with_undo_sys )
224 {
225 
226 	float tempf[4];
227 	int tempi[1];
228     float cosine = cos(angle);
229     float sine = sin(angle);
230 
231     if( pt == NULL )
232         return;
233 
234 	if( register_with_undo_sys ) {
235 		vector_copy( tempf, pt );
236 		tempf[3] = angle;
237 		tempi[0] = axis;
238 		action_do( model, ACTION_VERTEX_ROT, pts, NULL, NULL, tempf, tempi, NULL );
239 	}
240 
241     while( pts != NULL ) {
242 
243         Vertex* p = (Vertex*)(pts->data);
244         float temp0, temp1, temp2;
245 
246         if( axis == 0 ) {
247             temp1 = p->v[1] - pt[1];
248             temp2 = p->v[2] - pt[2];
249 
250 	    p->v[1] = temp1*cosine - temp2*sine + pt[1];
251 	    p->v[2] = temp1*sine + temp2*cosine + pt[2];
252         } else if( axis == 1 ) {
253             temp0 = p->v[0] - pt[0];
254             temp2 = p->v[2] - pt[2];
255 
256 	    p->v[0] =   temp0*cosine + temp2*sine + pt[0];
257 	    p->v[2] = - temp0*sine + temp2*cosine + pt[2];
258         } else if( axis == 2 ) {
259             temp0 = p->v[0] - pt[0];
260             temp1 = p->v[1] - pt[1];
261 
262 	    p->v[0] = temp0*cosine - temp1*sine + pt[0];
263 	    p->v[1] = temp0*sine + temp1*cosine + pt[1];
264         }
265 
266         pts = pts->next;
267     }
268 
269 }
270 
tools_vertices_flip(Model * model,GSList * pts,int axis)271 void tools_vertices_flip( Model *model, GSList * pts, int axis ) {
272 
273     Vertex average;
274 
275     if( axis < 0 || axis > 2 )
276         return;
277 
278     vertices_find_mean( pts, &average );
279 
280     while( pts != NULL ) {
281 
282         Vertex* p = (Vertex*)(pts->data);
283 
284         p->v[axis] -= average.v[axis];
285         p->v[axis] = p->v[axis] * -1.0;
286         p->v[axis] += average.v[axis];
287 
288         pts = pts->next;
289     }
290 
291     action_do( model, ACTION_UNKNOWN, NULL, NULL, NULL, NULL, NULL, NULL );
292 }
293 
294 
tools_vertices_scale_about_point(Model * model,GSList * plist,float * factor,float * point,int register_with_undo_sys)295 void tools_vertices_scale_about_point( Model *model,
296 	GSList * plist, float *factor, float *point, int register_with_undo_sys )
297 {
298 
299 	GSList * pts = NULL;
300 	Vertex * p = NULL;
301 
302 	if( plist == NULL ) return;
303 	if( factor == NULL || point == NULL ) return;
304 
305 	pts = plist;
306 	while( pts != NULL ) {
307 		p = (Vertex*)(pts->data);
308 
309 		vector_sub( p->v, p->v, point );
310 		vector_mul_piecewise( p->v, p->v, factor );
311 		vector_add( p->v, p->v, point );
312 
313 		pts = pts->next;
314 	}
315 
316 	if( register_with_undo_sys ) {
317 		float temp[6];
318 		vector_copy( temp, factor );
319 		vector_copy( temp + 3, point );
320 		action_do( model, ACTION_VERTEX_SCALE,
321 			(plist == model->selected_verts ? NULL : plist),
322 			NULL, NULL, temp, NULL, NULL );
323 	}
324 
325 }
326 
327 
tools_vertices_scale_about_center(Model * model,GSList * plist,float * factor,int register_with_undo_sys)328 void tools_vertices_scale_about_center( Model *model,
329 	GSList * plist, float *factor, int register_with_undo_sys )
330 {
331 	Vertex average;
332 	vertices_find_mean( plist, &average );
333 	tools_vertices_scale_about_point( model,
334 		plist, factor, average.v, register_with_undo_sys );
335 }
336 
337 
tools_vertices_scale_symmetrically_about_center(Model * model,GSList * plist,float factor,int register_with_undo_sys)338 void tools_vertices_scale_symmetrically_about_center( Model *model,
339 	GSList * plist, float factor, int register_with_undo_sys )
340 {
341 	float f_v[3];
342 	f_v[0] = f_v[1] = f_v[2] = factor;
343 	tools_vertices_scale_about_center( model, plist, f_v, register_with_undo_sys );
344 }
345 
346 
tools_vertices_scale_about_origin(Model * model,GSList * plist,float factor,int register_with_undo_sys)347 void tools_vertices_scale_about_origin( Model *model,
348 	GSList * plist, float factor, int register_with_undo_sys )
349 {
350 	float f_v[3], pt[3];
351 	f_v[0] = f_v[1] = f_v[2] = factor;
352 	pt[0] = pt[1] = pt[2] = 0.0;
353 	tools_vertices_scale_about_point( model, plist, f_v, pt, register_with_undo_sys );
354 }
355 
356 
tools_vertices_weld_list(Model * model,GSList * verts)357 void tools_vertices_weld_list( Model *model, GSList *verts ) {
358 
359 	GSList *l;
360 	Vertex *first;
361 
362 	if( verts == NULL ) return;
363 	if( g_slist_length( verts ) < 2 ) return;
364 
365 	tools_vertices_snap_together( model, verts );
366 
367 	l = verts;
368 	first = (Vertex*)l->data;
369 	l = l->next;
370 	for( ; l; l = l->next ) {
371 		tools_vertices_weld( model, first, (Vertex*)l->data );
372 	}
373 }
374 
375 
376 /* replaces all instances of v2 with v1 */
tools_vertices_weld(Model * model,Vertex * v1,Vertex * v2)377 void tools_vertices_weld( Model *model, Vertex *v1, Vertex *v2 ) {
378 	int i, num_polys;
379 	Poly *poly;
380 	Poly **polylist;
381 
382 	if( v1 == NULL || v2 == NULL ) return;
383 	if( v1 == v2 ) return;
384 
385 	/*
386 	for every poly using v2
387 		if poly uses both v1 & v2
388 			remove v2 from poly
389 		else (poly uses only v2)
390 			replace v2 with v1 in poly
391 		if v2 is no longer used by any polys in poly->mesh
392 			remove v2 from poly->mesh
393 		if poly has fewer than 3 verts
394 			remove poly from model
395 	remove v2 from model
396 	*/
397 
398 	num_polys = v2->num_polys;
399 	polylist = (Poly**)malloc( num_polys * sizeof( Poly* ) );
400 	memcpy( polylist, v2->polys, num_polys * sizeof( Poly* ) );
401 
402 	for( i = 0; i < num_polys; i++ ) {
403 		poly = polylist[i];
404 		if( poly_has_vertex( poly, v1 ) ) {
405 			poly_remove_vertex( poly, v2 );
406 		} else {
407 			poly_replace_vertex( poly, v1,
408 				array_find( (void**)poly->verts, poly->num_verts, v2 ) );
409 		}
410 
411 		if( ! mesh_is_using_vertex( poly->mesh, v2 ) ) {
412 			mesh_vertex_remove( poly->mesh, v2 );
413 		}
414 
415 		/* yes, 3.  not 2.  while 2-vertex polys *are* legal, it is expected
416 		behavior to remove such polys when performing a weld. */
417 		if( poly->num_verts < 3 ) {
418 			model_polygon_remove( model, poly );
419 		}
420 	}
421 
422 	free( polylist );
423 
424 	model_vertex_remove( model, v2 );
425 }
426 
427 
428 /* searches model for verts that are close together, and welds them */
tools_vertices_weld_modelwide(Model * model)429 void tools_vertices_weld_modelwide( Model *model ) {
430 	/*
431 	copy model's vert list
432 	for every vert1 in copied list
433 		if vert1 still exists in model vert list
434 			find all verts near vert1, and weld them all together
435 	*/
436 
437 	GSList *listcopy, *l, *nearverts;
438 	Vertex *v;
439 
440 	if( model == NULL ) return;
441 	if( model->verts == NULL ) return;
442 
443 	listcopy = g_slist_copy( model->verts );
444 	for( l = listcopy; l; l = l->next ) {
445 		v = (Vertex *)l->data;
446 
447 		if( g_slist_find( model->verts, v ) == NULL )
448 			continue;
449 
450 		nearverts = tools_find_near_verts( model->verts, v );
451 		if( nearverts ) {
452 			tools_vertices_weld_list( model, nearverts );
453 			g_slist_free( nearverts );
454 		}
455 	}
456 
457 	g_slist_free( listcopy );
458 }
459 
460 
461 /* returns a list of all verts near v, including v */
tools_find_near_verts(GSList * verts,Vertex * v)462 GSList *tools_find_near_verts( GSList *verts, Vertex *v ) {
463 	GSList *result = NULL;
464 
465 	if( verts == NULL || v == NULL ) return NULL;
466 
467 	while( verts ) {
468 		verts = g_slist_find_custom( verts, v,
469 									(GCompareFunc) vertices_isNotEqual );
470 		if( verts ) {
471 			result = g_slist_append( result, verts->data );
472 			verts = verts->next;
473 		}
474 	}
475 
476 	return result;
477 }
478 
479 
480 
481 
482 /* POLYGON TOOLS ********************************************************/
483 
484 /* Deprecated */
tools_polys_move(Model * model,GSList * polys,float dX,float dY,float dZ)485 void tools_polys_move( Model *model, GSList *polys, float dX, float dY, float dZ ) {
486 
487     GSList *verts = NULL;
488 
489     verts = polys_get_verts_unique( polys );
490 
491     tools_vertices_move( model, verts, dX, dY, dZ );
492     g_slist_free( verts );
493 }
494 
495 
496 
497 /* Deprecated */
tools_poly_move(Model * model,Poly * p,float dX,float dY,float dZ)498 void tools_poly_move( Model *model, Poly * p, float dX, float dY, float dZ ) {
499 
500     int i;
501     if( p == NULL ) return;
502 
503     for( i = 0; i < p->num_verts; i++ ) {
504         vertex_move( p->verts[i], dX, dY, dZ );
505     }
506 }
507 
508 
509 /* extrudes members of 'polys' individually.  returns list of polys created,
510  * with members of 'polys' at head of list.  returned list must be freed.
511  */
tools_polys_extrude(Model * model,GSList * polys)512 GSList * tools_polys_extrude( Model *model, GSList *polys ) {
513 	GSList *result = NULL, *temp;
514 
515 	result = g_slist_copy( polys );
516 
517     for( ; polys != NULL; polys = polys->next ) {
518         temp = tools_poly_extrude( model, (Poly*)polys->data );
519 		/* the first item in temp is polys->data.  remove it. */
520 		temp = g_slist_delete_link( temp, temp );
521 		result = g_slist_concat( result, temp );
522     }
523 
524 	return result;
525 }
526 
527 
528 /* extrudes poly p.  returns list of polys created, with p at head of list.
529  * returned list must be freed.
530  */
tools_poly_extrude(Model * model,Poly * p)531 GSList * tools_poly_extrude( Model *model, Poly *p ) {
532 	GSList *result = NULL;
533 	int i;
534     Vertex *original_verts[POLY_MAX_VERTS];
535     Poly *new_poly;
536     Vertex *new_vert;
537 
538     if( p == NULL )
539         return NULL;
540     if( p->mesh == NULL || p->mesh->polygons == NULL )
541         return NULL;
542 
543 	result = g_slist_append( result, p );
544 
545     /* first, re-calc the normal, just to be safe */
546     poly_find_normal( p );
547 
548     /* create n new verts, dupes of the original n */
549     for( i = 0; i < p->num_verts; i++ ) {
550         original_verts[i] = p->verts[i];
551         new_vert = vertex_dup( p->verts[i] );
552         model_vertex_add( model, new_vert );
553         mesh_vertex_add( p->mesh, new_vert );
554 
555         /* replace the old with the new */
556 		poly_replace_vertex( p, new_vert, i );
557     }
558 
559     /* create n new polys */
560     for( i = 0; i < p->num_verts; i++ ) {
561         new_poly = poly_new();
562         poly_add_vertex( new_poly, original_verts[i] );
563         poly_add_vertex( new_poly, original_verts[ (i+1) % p->num_verts ] );
564         poly_add_vertex( new_poly, p->verts[ (i+1) % p->num_verts ] );
565         poly_add_vertex( new_poly, p->verts[i] );
566         mesh_polygon_add( p->mesh, new_poly );
567 		result = g_slist_append( result, new_poly );
568     }
569 
570 	return result;
571 }
572 
573 
574 /* extrudes members of 'polys' such that they will remain connected.
575  * returns list of polys created, with members of 'polys' at head of list.
576  * returned list must be freed.
577  */
tools_polys_extrude_connected(Model * model,GSList * polys)578 GSList * tools_polys_extrude_connected( Model *model, GSList *polys ) {
579 	GSList *result = NULL;
580     GSList *l;
581     GHashTable *verts_old2new_hash;
582     GHashTable *verts_new2old_hash;
583     Poly *p;
584     int i;
585     Poly *new_poly;
586     Vertex *new_vert;
587     Vertex *orig_vert1, *orig_vert2;
588 
589 	result = g_slist_copy( polys );
590 
591     verts_old2new_hash = g_hash_table_new( g_direct_hash,
592                                    (GCompareFunc)vertices_isIdentical );
593     verts_new2old_hash = g_hash_table_new( g_direct_hash,
594                                    (GCompareFunc)vertices_isIdentical );
595 
596     /* note that it's *probably* ok to pass in a list of polys from various
597        meshes (as opposed to polys from the *same* mesh), but I haven't
598        tested this well...  */
599     for( l = polys; l != NULL; l = l->next ) {
600         p = (Poly*)l->data;
601 
602         /* create n new verts, dupes of the original n */
603         /* remember them in the hash so other polys can use them too */
604         for( i = 0; i < p->num_verts; i++ ) {
605             new_vert = g_hash_table_lookup( verts_old2new_hash, p->verts[i] );
606             if( new_vert == NULL ) {
607                 new_vert = vertex_dup( p->verts[i] );
608                 g_hash_table_insert( verts_old2new_hash, p->verts[i], new_vert );
609                 g_hash_table_insert( verts_new2old_hash, new_vert, p->verts[i] );
610                 model_vertex_add( model, new_vert );
611                 mesh_vertex_add( p->mesh, new_vert );
612             }
613 
614             /* replace the old with the new */
615 			poly_replace_vertex( p, new_vert, i );
616         }
617 
618     }
619 
620     for( l = polys; l != NULL; l = l->next ) {
621         p = (Poly*)l->data;
622 
623         /* first, re-calc the normal, just to be safe */
624         poly_find_normal( p );
625 
626         /* create n new polys */
627         for( i = 0; i < p->num_verts; i++ ) {
628             int j = (i+1) % p->num_verts;
629 
630             orig_vert1 = g_hash_table_lookup( verts_new2old_hash, p->verts[i] );
631             orig_vert2 = g_hash_table_lookup( verts_new2old_hash, p->verts[j] );
632 
633             /* if this edge is shared with others in polys, then skip it */
634             if( polys_count_edge_usage( polys, p->verts[i], p->verts[j]) > 1 ) {
635                 continue;
636             }
637 
638             new_poly = poly_new();
639             poly_add_vertex( new_poly, orig_vert1 );
640             poly_add_vertex( new_poly, orig_vert2 );
641             poly_add_vertex( new_poly, p->verts[j] );
642             poly_add_vertex( new_poly, p->verts[i] );
643             mesh_polygon_add( p->mesh, new_poly );
644 			result = g_slist_append( result, new_poly );
645         }
646     }
647 
648     g_hash_table_destroy( verts_old2new_hash );
649     g_hash_table_destroy( verts_new2old_hash );
650 
651 	return result;
652 }
653 
654 
tools_polys_turn_edge(Model * model,GSList * polys)655 void tools_polys_turn_edge( Model *model, GSList *polys ) {
656 
657 	Poly *p1, *p2;
658 	Vertex *shared_v1, *shared_v2;
659 	Vertex *not_shared_v1, *not_shared_v2;
660 	int i, shared_edges=0;
661 	TexCoord *nsv1tc, *nsv2tc;
662 	float nsv1tc_u, nsv1tc_v, nsv2tc_u, nsv2tc_v;
663 
664 	if( polys == NULL ) return;
665 	if( g_slist_length( polys ) != 2 ) {
666 		printf( "Turn edge: exactly 2 polys must be selected\n" );
667 		return;
668 	}
669 
670 	p1 = (Poly*)polys->data;
671 	p2 = (Poly*)polys->next->data;
672 
673 	/* check to make sure that all polys are triangles */
674 	if( p1->num_verts != 3 || p2->num_verts != 3 ) {
675 		printf( "Turn edge: both polygons must be triangles\n" );
676 		return;
677 	}
678 
679 	/* check to make sure that the two polys are in the same mesh */
680 	if( p1->mesh != p2->mesh ) {
681 		printf( "Turn edge: both polygons must be in the same mesh\n" );
682 		return;
683 	}
684 
685 	/* check to make sure that the two polys share exactly one edge */
686 	for( i = 0; i < p1->num_verts; i++ ) {
687 		if( poly_has_edge( p2,
688 				p1->verts[i], p1->verts[(i+1)%p1->num_verts] ) )
689 		{
690 			shared_edges ++;
691 			shared_v1 = p1->verts[i];
692 			shared_v2 = p1->verts[(i+1)%p1->num_verts];
693 		}
694 	}
695 	if( shared_edges != 1 ) {
696 		printf( "Turn edge: polygons must be adjacent\n" );
697 		return;
698 	}
699 
700 	/* find out which two verts are *not* shared */
701 	for( i = 0; i < p1->num_verts; i++ ) {
702 		if( p1->verts[i] != shared_v1 && p1->verts[i] != shared_v2 )
703 			not_shared_v1 = p1->verts[i];
704 	}
705 	for( i = 0; i < p2->num_verts; i++ ) {
706 		if( p2->verts[i] != shared_v1 && p2->verts[i] != shared_v2 )
707 			not_shared_v2 = p2->verts[i];
708 	}
709 
710 	/* remember the texcoords */
711 	/* remember p2's nsv2, to be used for p1 */
712 	nsv2tc = poly_get_texcoord( p2, not_shared_v2 );
713 	nsv2tc_u = nsv2tc->x;
714 	nsv2tc_v = nsv2tc->y;
715 	/* remember p1's nsv1, to be used for p2 */
716 	nsv1tc = poly_get_texcoord( p1, not_shared_v1 );
717 	nsv1tc_u = nsv1tc->x;
718 	nsv1tc_v = nsv1tc->y;
719 
720 	/* perform the swap */
721 	poly_replace_vertex( p1, not_shared_v2,
722 		array_find( (void**)p1->verts, p1->num_verts, shared_v2 ) );
723 	poly_replace_vertex( p2, not_shared_v1,
724 		array_find( (void**)p2->verts, p2->num_verts, shared_v1 ) );
725 
726 	/* set the texcoords to the proper values */
727 	texcoord_change_values( poly_get_texcoord( p1, not_shared_v2 ),
728 							nsv2tc_u, nsv2tc_v );
729 	texcoord_change_values( poly_get_texcoord( p2, not_shared_v1 ),
730 							nsv1tc_u, nsv1tc_v );
731 }
732 
733 
tools_polys_make_triangles(Model * model,GSList * plist)734 void tools_polys_make_triangles( Model *model, GSList *plist ) {
735 
736 	GSList *l, *polys;
737 	Poly *p, *newpoly;
738 	int i;
739 
740 	if( plist == NULL ) return;
741 
742 	polys = g_slist_copy( plist );
743 
744 	for( l = polys; l; l = l->next ) {
745 		p = (Poly *)l->data;
746 		if( p->num_verts < 4 )
747 			continue;
748 
749 		for( i = 2; i < p->num_verts; i++ ) {
750 			newpoly = poly_new();
751 			poly_add_vertex( newpoly, p->verts[0] );
752 			texcoord_change_values(
753 				poly_get_texcoord( newpoly, p->verts[0] ),
754 				p->tc[0]->x, p->tc[0]->y );
755 			poly_add_vertex( newpoly, p->verts[i-1] );
756 			texcoord_change_values(
757 				poly_get_texcoord( newpoly, p->verts[i-1] ),
758 				p->tc[i-1]->x, p->tc[i-1]->y );
759 			poly_add_vertex( newpoly, p->verts[i] );
760 			texcoord_change_values(
761 				poly_get_texcoord( newpoly, p->verts[i] ),
762 				p->tc[i]->x, p->tc[i]->y );
763 			mesh_polygon_add( p->mesh, newpoly );
764 		}
765 		model_polygon_remove( model, p );
766 	}
767 
768 	g_slist_free( polys );
769 }
770 
771 
772 #define TOOLS_ADD_VERT_AND_CHANGE_TC( THE_VERT ) \
773 	poly_add_vertex( newpoly, THE_VERT );\
774 	tools_find_texcoords_for_vert( p, THE_VERT, verts_for_new_polys, &u, &v );\
775 	texcoord_change_values( poly_get_texcoord( newpoly, THE_VERT ), u, v );
776 #define TOOLS_ADD_MIDDLEVERT_AND_CHANGE_TC( THE_VERT ) \
777 	poly_add_vertex( newpoly, THE_VERT );\
778 	tools_find_texcoords_for_middlevert( p, &u, &v );\
779 	texcoord_change_values( poly_get_texcoord( newpoly, THE_VERT ), u, v );
780 
tools_polys_subdivide_midpoint(Model * model,GSList * plist)781 void tools_polys_subdivide_midpoint( Model *model, GSList *plist ) {
782 
783 	GSList *l, *ll, *polys, *nearverts, *verts_for_new_polys = NULL;
784 	Poly *p, *newpoly;
785 	Vertex *newvert, *middlevert;
786 	int i;
787 	float u, v;
788 
789 	if( plist == NULL ) return;
790 
791 	polys = g_slist_copy( plist );
792 
793 	for( l = polys; l; l = l->next ) {
794 		p = (Poly *)l->data;
795 		if( p->num_verts < 3 )
796 			continue;
797 
798 		/* for each edge, insert a vert at the midpoint */
799 		for( i = 0; i < p->num_verts; i++ ) {
800 			int next_i = (i + 1) % p->num_verts;
801 			verts_for_new_polys = g_slist_append( verts_for_new_polys, p->verts[i] );
802 
803 			newvert = vertex_new();
804 
805 			/* find the middle of the edge */
806 			vector_add( newvert->v, p->verts[i]->v, p->verts[next_i]->v );
807 			vector_mul( newvert->v, newvert->v, 1. / 2. );
808 
809 			nearverts = tools_find_near_verts( model->verts, newvert );
810 
811 			if( nearverts != NULL ) {
812 				/* if an existing vert was found near newvert */
813 				vertex_delete( newvert );
814 				newvert = (Vertex*)nearverts->data;
815 				g_slist_free( nearverts );
816 			} else {
817 				model_vertex_add( model, newvert );
818 			}
819 
820 			verts_for_new_polys = g_slist_append( verts_for_new_polys, newvert );
821 		}
822 
823 		/* if this is not a triangle, create one more vert at the middle */
824 		if( p->num_verts >= 4 ) {
825 			middlevert = vertex_new();
826 			vertices_find_mean( verts_for_new_polys, middlevert );
827 			model_vertex_add( model, middlevert );
828 		}
829 
830 		for( ll = verts_for_new_polys; ll && ll->next;  ) {
831 			int first = (ll == verts_for_new_polys);
832 			Vertex *firstvert;
833 			newpoly = poly_new();
834 
835 			if( first ) {
836 				/* if this is the first poly to be created in place of p, then
837 				we have some special-case things to do */
838 				firstvert = (Vertex*)(g_slist_last(verts_for_new_polys)->data);
839 			} else {
840 				firstvert = (Vertex*)ll->data;
841 				ll = ll->next;
842 			}
843 			TOOLS_ADD_VERT_AND_CHANGE_TC( firstvert )
844 
845 			TOOLS_ADD_VERT_AND_CHANGE_TC( (Vertex*)ll->data )
846 			ll = ll->next;
847 			TOOLS_ADD_VERT_AND_CHANGE_TC( (Vertex*)ll->data )
848 
849 			if( p->num_verts >= 4 ) {
850 				/* if this is not a triangle, the final vert in newpoly should
851 				be the middlevert */
852 				TOOLS_ADD_MIDDLEVERT_AND_CHANGE_TC( middlevert )
853 			}
854 
855 			mesh_polygon_add( p->mesh, newpoly );
856 		}
857 
858 		if( p->num_verts == 3 ) {
859 			/* if this is a triangle, add the final (middle) new poly */
860 			newpoly = poly_new();
861 			TOOLS_ADD_VERT_AND_CHANGE_TC(
862 				(Vertex*)verts_for_new_polys->next->data )
863 			TOOLS_ADD_VERT_AND_CHANGE_TC(
864 				(Vertex*)verts_for_new_polys->next->next->next->data )
865 			TOOLS_ADD_VERT_AND_CHANGE_TC(
866 				(Vertex*)(g_slist_last(verts_for_new_polys)->data) )
867 			mesh_polygon_add( p->mesh, newpoly );
868 		}
869 
870 		g_slist_free( verts_for_new_polys );
871 		verts_for_new_polys = NULL;
872 
873 		model_polygon_remove( model, p );
874 	}
875 
876 	g_slist_free( polys );
877 }
878 
879 
tools_polys_subdivide_quads(Model * model,GSList * plist)880 void tools_polys_subdivide_quads( Model *model, GSList *plist ) {
881 
882 	GSList *l, *ll, *polys, *nearverts, *verts_for_new_polys = NULL;
883 	Poly *p, *newpoly;
884 	Vertex *newvert, *middlevert;
885 	int i;
886 	float u, v;
887 
888 	if( plist == NULL ) return;
889 
890 	polys = g_slist_copy( plist );
891 
892 	for( l = polys; l; l = l->next ) {
893 		p = (Poly *)l->data;
894 		if( p->num_verts < 3 )
895 			continue;
896 
897 		/* for each edge, insert a vert at the midpoint */
898 		for( i = 0; i < p->num_verts; i++ ) {
899 			int next_i = (i + 1) % p->num_verts;
900 			verts_for_new_polys = g_slist_append( verts_for_new_polys, p->verts[i] );
901 
902 			newvert = vertex_new();
903 
904 			/* find the middle of the edge */
905 			vector_add( newvert->v, p->verts[i]->v, p->verts[next_i]->v );
906 			vector_mul( newvert->v, newvert->v, 1. / 2. );
907 
908 			nearverts = tools_find_near_verts( model->verts, newvert );
909 
910 			if( nearverts != NULL ) {
911 				/* if an existing vert was found near newvert */
912 				vertex_delete( newvert );
913 				newvert = (Vertex*)nearverts->data;
914 				g_slist_free( nearverts );
915 			} else {
916 				model_vertex_add( model, newvert );
917 			}
918 
919 			verts_for_new_polys = g_slist_append( verts_for_new_polys, newvert );
920 		}
921 
922 		/* create one more vert at the middle */
923 		middlevert = vertex_new();
924 		vertices_find_mean( verts_for_new_polys, middlevert );
925 		model_vertex_add( model, middlevert );
926 
927 		for( ll = verts_for_new_polys; ll && ll->next;  ) {
928 			int first = (ll == verts_for_new_polys);
929 			Vertex *firstvert;
930 			newpoly = poly_new();
931 
932 			if( first ) {
933 				/* if this is the first poly to be created in place of p, then
934 				we have some special-case things to do */
935 				firstvert = (Vertex*)(g_slist_last(verts_for_new_polys)->data);
936 			} else {
937 				firstvert = (Vertex*)ll->data;
938 				ll = ll->next;
939 			}
940 			TOOLS_ADD_VERT_AND_CHANGE_TC( firstvert )
941 
942 			TOOLS_ADD_VERT_AND_CHANGE_TC( (Vertex*)ll->data )
943 			ll = ll->next;
944 			TOOLS_ADD_VERT_AND_CHANGE_TC( (Vertex*)ll->data )
945 
946 			/* the fourth (final) vert in newpoly should be the middlevert */
947 			TOOLS_ADD_MIDDLEVERT_AND_CHANGE_TC( middlevert )
948 
949 			mesh_polygon_add( p->mesh, newpoly );
950 		}
951 
952 		g_slist_free( verts_for_new_polys );
953 		verts_for_new_polys = NULL;
954 
955 		model_polygon_remove( model, p );
956 	}
957 
958 	g_slist_free( polys );
959 }
960 
961 
tools_polys_subdivide_tris(Model * model,GSList * plist)962 void tools_polys_subdivide_tris( Model *model, GSList *plist ) {
963 
964 	GSList *l, *polys, *verts_for_new_polys = NULL;
965 	Poly *p, *newpoly;
966 	Vertex *middlevert;
967 	int i;
968 	float u, v;
969 
970 	if( plist == NULL ) return;
971 
972 	polys = g_slist_copy( plist );
973 
974 	for( l = polys; l; l = l->next ) {
975 		p = (Poly *)l->data;
976 		if( p->num_verts < 3 )
977 			continue;
978 
979 		/* build a list of the verts, for use by vertices_find_mean and the
980 		texture coordinate stuff */
981 		for( i = 0; i < p->num_verts; i++ ) {
982 			verts_for_new_polys = g_slist_append( verts_for_new_polys, p->verts[i] );
983 		}
984 
985 		/* create one vert at the middle */
986 		middlevert = vertex_new();
987 		vertices_find_mean( verts_for_new_polys, middlevert );
988 		model_vertex_add( model, middlevert );
989 
990 		/* for each edge, create a triangle using the two verts on the edge
991 		and the middle vert */
992 		for( i = 0; i < p->num_verts; i++ ) {
993 			int next_i = (i + 1) % p->num_verts;
994 			newpoly = poly_new();
995 
996 			TOOLS_ADD_VERT_AND_CHANGE_TC( p->verts[i] )
997 			TOOLS_ADD_VERT_AND_CHANGE_TC( p->verts[next_i] )
998 			TOOLS_ADD_MIDDLEVERT_AND_CHANGE_TC( middlevert )
999 
1000 			mesh_polygon_add( p->mesh, newpoly );
1001 		}
1002 
1003 		g_slist_free( verts_for_new_polys );
1004 		verts_for_new_polys = NULL;
1005 
1006 		model_polygon_remove( model, p );
1007 	}
1008 
1009 	g_slist_free( polys );
1010 }
1011 
1012 
tools_find_texcoords_for_vert(Poly * p,Vertex * vert,GSList * verts,float * u,float * v)1013 void tools_find_texcoords_for_vert( Poly *p, Vertex *vert, GSList *verts,
1014 	float *u, float *v )
1015 {
1016 	TexCoord *tc, *nexttc, *prevtc;
1017 	GSList *listelem, *nextelem, *prevelem;
1018 	int elem_index;
1019 
1020 	// fixme - check for NULL
1021 
1022 	*u = 0.;
1023 	*v = 0.;
1024 
1025 	/* if the vert is already in the poly, we can retrieve its
1026 	texcoord vals directly */
1027 	tc = poly_get_texcoord( p, vert );
1028 	if( tc ) {
1029 		*u = tc->x;
1030 		*v = tc->y;
1031 		return;
1032 	}
1033 
1034 	/* if it is not in the poly, find its location in the list, as well as
1035 	the next and previous verts in the list */
1036 	listelem = g_slist_find( verts, vert );
1037 	if( listelem == NULL )
1038 		return;
1039 
1040 	nextelem = listelem->next;
1041 	if( nextelem == NULL )
1042 		/* loop around to beginning of list */
1043 		nextelem = verts;
1044 
1045 	elem_index = g_slist_position( verts, listelem );
1046 	if( elem_index == 0 )
1047 		/* loop around to end of list */
1048 		elem_index = g_slist_length( verts ) - 1;
1049 	else
1050 		elem_index--;
1051 	prevelem = g_slist_nth( verts, elem_index );
1052 
1053 	/* Because of the way in which the verts list is set up (see the
1054 	tools_polys_subdivide funcs) the verts in the list alternate between
1055 	new and existing vertices.  As such, since we know that 'vert' is a new
1056 	vert, the verts before and after v in the list *should* exist in poly p,
1057 	and hence have texture coordinates.  We will use those texture coordinates
1058 	to calculate the coords for 'vert'. */
1059 
1060 	nexttc = poly_get_texcoord( p, (Vertex*)nextelem->data );
1061 	prevtc = poly_get_texcoord( p, (Vertex*)prevelem->data );
1062 
1063 	if( nexttc == NULL || prevtc == NULL ) return;
1064 
1065 	*u = (nexttc->x + prevtc->x) / 2.;
1066 	*v = (nexttc->y + prevtc->y) / 2.;
1067 
1068 }
1069 
1070 
tools_find_texcoords_for_middlevert(Poly * p,float * u,float * v)1071 void tools_find_texcoords_for_middlevert( Poly *p, float *u, float *v )
1072 {
1073 	int i;
1074 	*u = 0.;
1075 	*v = 0.;
1076 	for( i = 0; i < p->num_verts; i++ ) {
1077 		*u += p->tc[i]->x;
1078 		*v += p->tc[i]->y;
1079 	}
1080 	*u /= (float)p->num_verts;
1081 	*v /= (float)p->num_verts;
1082 }
1083 
1084 
tools_polys_smooth(Model * model,GSList * polys,float factor)1085 void tools_polys_smooth( Model *model, GSList *polys, float factor ) {
1086 
1087 	GSList *vertlist, *l;
1088 	GSList *adjacent_polys, *adjacent_verts;
1089 	GSList *templist;
1090 	GHashTable *verthash;
1091 	Vertex *v, avgVert;
1092 	float *tempPt, delta[3];
1093 
1094 	/* verthash will hold the new locations for the vertices */
1095 
1096 	vertlist = polys_get_verts_unique( polys );
1097 	verthash = g_hash_table_new_full( NULL, NULL, NULL, g_free );
1098 	/* populate verthash */
1099 	for( l = vertlist; l; l = l->next ) {
1100 		v = (Vertex *)l->data;
1101 		tempPt = g_malloc( sizeof( float ) * 3 );
1102 		vector_copy( tempPt, v->v );
1103 		g_hash_table_insert( verthash, v, tempPt );
1104 	}
1105 
1106 	/* for each vert, record its new position in the hash table */
1107 	for( l = vertlist; l; l = l->next ) {
1108 		v = (Vertex *)l->data;
1109 		tempPt = g_hash_table_lookup( verthash, v );
1110 
1111 		/* for each vert, find all verts that are adjacent to it */
1112 		templist = g_slist_append( NULL, v );
1113 		adjacent_polys = verts_get_polys_unique( templist );
1114 		g_slist_free( templist );
1115 		adjacent_verts = polys_get_verts_unique( adjacent_polys );
1116 		/* v will be in adjacent_verts, but that's OK */
1117 
1118 		/* using the list of adjacent verts, find their average */
1119 		vertices_find_mean( adjacent_verts, &avgVert );
1120 
1121 		/* move the vert closer to the average */
1122 		vector_sub( delta, avgVert.v, v->v );
1123 		vector_mul( delta, delta, factor );
1124 		vector_add( tempPt, tempPt, delta );
1125 
1126 		g_slist_free( adjacent_polys );
1127 		g_slist_free( adjacent_verts );
1128 	}
1129 
1130 	/* now move all of the verts */
1131 	for( l = vertlist; l; l = l->next ) {
1132 		v = (Vertex *)l->data;
1133 		tempPt = g_hash_table_lookup( verthash, v );
1134 		vector_copy( v->v, tempPt );
1135 	}
1136 
1137 	g_slist_free( vertlist );
1138 	g_hash_table_destroy( verthash );
1139 
1140 	action_do( model, ACTION_UNKNOWN, NULL, NULL, NULL, NULL, NULL, NULL );
1141 }
1142 
1143 /* any more edges than this and it doesn't count as a hole */
1144 #define TOOLS_HOLE_THRESHOLD 4
1145 
tools_polys_find_holes(Model * model,GSList * polys)1146 void tools_polys_find_holes( Model *model, GSList *polys ) {
1147 	/* Finds holes and 'T' junctions in the list of polys.
1148 	Any group of "outside edges" consisting of fewer than a certain number
1149 	of edges will be tagged as a hole.  Note that, because of this and because
1150 	of the way in which outside edges are grouped, it currently can not
1151 	detect "figure-eight" type holes (ie two holes that share a vertex). */
1152 
1153 	/* Here's how this *should* have been implemented:
1154 		- find the outside edges
1155 		- construct a graph of the edges and verts
1156 		- form 'holes' by
1157 			- performing shortest-path on the graph
1158 			- once shortest-loop-from-an-edge-back-to-itself is found,
1159 			  remove involved edges from graph and form a vertlist.  This is
1160 			  one hole.
1161 		- form holes until there are no edges left in graph, or (error state)
1162 		  the remaining edges don't form any loops
1163 		- any holes smaller than a certain threshold can be selected,
1164 		  filled-in by poly, etc.
1165 	*/
1166 
1167 	GSList *l, *ll;
1168 	GSList *outside_edges;
1169 	GSList *holes = NULL;
1170 	GSList *bins = NULL, *bins_copy;
1171 	GSList *bin, *bl;
1172 	int bin_is_part_of_another_bin;
1173 
1174 	outside_edges = tools_find_outside_edges( polys );
1175 
1176 	/* sort the edges into bins */
1177 	for( l = outside_edges; l; l = l->next ) {
1178 		tools_sort_edge_into_bins( &bins, (Edge*)l->data );
1179 	}
1180 
1181 	/* now we must consolidate the bins as much as possible */
1182 	/* For every bin in 'bins', remove it from bins
1183 	and try adding its contents back into 'bins'.  The idea is that, if the
1184 	smaller bin should actually be part of a larger bin, some of its contents
1185 	will be added to that larger bin and the smaller bin (if it re-forms at
1186 	all) will have fewer edges.  However, if the smaller bin *is* its own,
1187 	independent bin, it will be completely re-formed as its contents are
1188 	added back into 'bins'.  If we repeat this process until there are no bins
1189 	that are part of other bins, we will have consolidated all of the bins
1190 	as much as possible. */
1191 	do {
1192 		bin_is_part_of_another_bin = FALSE;
1193 		bins_copy = g_slist_copy( bins );
1194 		for( bin = bins_copy; bin; bin = bin->next ) {
1195 			GSList *binlist, *btemp, *bin_that_first_edge_was_added_to = NULL;
1196 			/* remove bin from bins */
1197 			binlist = bin->data;
1198 			bins = g_slist_remove( bins, binlist );
1199 
1200 			for( bl = binlist; bl; bl = bl->next ) {
1201 				btemp = tools_sort_edge_into_bins( &bins, (Edge*)bl->data );
1202 				if( bin_that_first_edge_was_added_to == NULL ) {
1203 					bin_that_first_edge_was_added_to = btemp;
1204 					if( g_slist_length( bin_that_first_edge_was_added_to->data ) > 1 )
1205 						/* bin is part of another bin */
1206 						bin_is_part_of_another_bin = TRUE;
1207 				}
1208 				if( bin_that_first_edge_was_added_to != btemp ) {
1209 					/* bin is part of another bin */
1210 					bin_is_part_of_another_bin = TRUE;
1211 				}
1212 			}
1213 			g_slist_free( binlist );
1214 		}
1215 		g_slist_free( bins_copy );
1216 	} while( bin_is_part_of_another_bin );
1217 
1218 	/* Now traverse 'bins', and for each bin shorter than the threshold add
1219 	its contents to the holes list.  Finally, free the bin. */
1220 	for( bin = bins; bin; bin = bin->next ) {
1221 		if( !(g_slist_length( bin->data ) > TOOLS_HOLE_THRESHOLD) ) {
1222 			/* holes smaller than the TOOLS_HOLE_THRESHOLD are assumed
1223 			not to be figure-8 holes, and can be converted directly into
1224 			a vertlist */
1225 			holes = g_slist_append( holes,
1226 						tools_convert_edgelist_to_vertlist( bin->data ) );
1227 		} else {
1228 			/* the edgelist may or may not be a figure-8 hole.  we should
1229 			determine if it is a figure-8, and if so attempt to subdivide it. */
1230 			if( tools_edgelist_can_be_subdivided( bin->data ) ) {
1231 				printf( "Found a figure-eight hole, with %i edges\n", g_slist_length( bin->data ) );
1232 			}
1233 		}
1234 
1235 		g_slist_free( bin->data );
1236 		bin->data = NULL;
1237 	}
1238 	g_slist_free( bins );
1239 
1240 	/* free edges */
1241 	for( l = outside_edges; l; l = l->next ) {
1242 		free( l->data );
1243 	}
1244 	g_slist_free( outside_edges );
1245 
1246 	/* We now have a list of lists of verts.  Each list of verts represents
1247 	a 'hole' in the poly-list surface. */
1248 
1249 	/*print the lists, select the verts*/
1250 	sel_vert_unselect_all( model );
1251 	printf( "%s : found %i holes\n", __FUNCTION__, g_slist_length( holes ) );
1252 	for( l = holes; l; l = l->next ) {
1253 		printf( "\tverts: " );
1254 		sel_vert_select_list( l->data );
1255 		for( ll = l->data; ll; ll = ll->next ) {
1256 			printf( "%x ", (unsigned int)ll->data );
1257 		}
1258 		printf( "\n" );
1259 	}
1260 }
1261 
1262 
tools_convert_edgelist_to_vertlist(GSList * edges)1263 GSList * tools_convert_edgelist_to_vertlist( GSList *edges ) {
1264 
1265 	GSList *l, *verts = NULL;
1266 	Edge *e, *prev = NULL;
1267 	int num_edges = g_slist_length( edges );
1268 
1269 	while( g_slist_length(verts) < num_edges ) {
1270 		for( l = edges; l; l = l->next ) {
1271 			e = (Edge *)l->data;
1272 			if( prev == NULL )
1273 			{
1274 				verts = g_slist_append( verts, e->v1 );
1275 				verts = g_slist_append( verts, e->v2 );
1276 				e->flag = TRUE;
1277 				prev = e;
1278 			}
1279 			else if( !(e->flag) &&
1280 						tools_edges_areConnected( e, prev ) &&
1281 						tools_edges_isNotEqual( e, prev ) )
1282 			{
1283 				Vertex *v = NULL;
1284 				/* now figure out which vert should be next */
1285 				if( e->v1 == prev->v1 || e->v1 == prev->v2 )
1286 					v = e->v2;
1287 				else if( e->v2 == prev->v1 || e->v1 == prev->v2 )
1288 					v = e->v1;
1289 
1290 				if( v && g_slist_find( verts, v ) == NULL )
1291 					verts = g_slist_append( verts, v );
1292 				e->flag = TRUE;
1293 				prev = e;
1294 			}
1295 		}
1296 	}
1297 
1298 	return verts;
1299 }
1300 
1301 
tools_edgelist_can_be_subdivided(GSList * edges)1302 int tools_edgelist_can_be_subdivided( GSList *edges ) {
1303 	/*  */
1304 	int figure_eight = FALSE;
1305 	int well_behaved = TRUE;
1306 	GSList *l, *ll;
1307 	Edge *e;
1308 	Vertex *v;
1309 	int edges_with_v;
1310 
1311 	for( l = edges; l; l = l->next ) {
1312 		e = (Edge *)l->data;
1313 		v = e->v1;
1314 		while( v ) {
1315 			edges_with_v = 0;
1316 			for( ll = edges; ll; ll = ll->next ) {
1317 				if( tools_edge_hasVertex( (Edge*)ll->data, v ) )
1318 					edges_with_v++;
1319 			}
1320 			if( edges_with_v > 2 )
1321 				figure_eight = TRUE;
1322 			if( edges_with_v % 2 )
1323 				well_behaved = FALSE;
1324 			v = (v==e->v1) ? e->v2 : NULL;
1325 		}
1326 	}
1327 
1328 	return( figure_eight && well_behaved );
1329 }
1330 
1331 
tools_sort_edge_into_bins(GSList ** bins,Edge * edge)1332 GSList * tools_sort_edge_into_bins( GSList **bins, Edge *edge ) {
1333 	/* returns the bin that edge was placed in */
1334 
1335 	GSList *l, *ll;
1336 	GSList *destination_bin = NULL;
1337 	int largest_matching_bin_size = -1;
1338 
1339 	/* look for a proper bin to put edge into.  It is important that, if an
1340 	edge could go in one of several bins, we add the edge to the largest
1341 	bin. */
1342 	for( l = *bins; l; l = l->next ) {
1343 		int bin_size = g_slist_length( l->data );
1344 		for( ll = l->data; ll; ll = ll->next ) {
1345 			if( tools_edges_areConnected( edge, (Edge*)ll->data ) &&
1346 			    bin_size >= largest_matching_bin_size ) {
1347 				destination_bin = l;
1348 				largest_matching_bin_size = bin_size;
1349 			}
1350 		}
1351 	}
1352 
1353 	/* if couldn't find a proper bin, create one and add it to bins */
1354 	if( destination_bin == NULL ) {
1355 		*bins = g_slist_append( *bins, NULL );
1356 		destination_bin = g_slist_last( *bins );
1357 		destination_bin->data = NULL;
1358 	}
1359 
1360 	/* append edge to destination_bin */
1361 	destination_bin->data = g_slist_append( destination_bin->data, edge );
1362 
1363 	return destination_bin;
1364 }
1365 
1366 
tools_find_outside_edges(GSList * polys)1367 GSList *tools_find_outside_edges( GSList *polys ) {
1368 	GSList *edges = NULL;
1369 	GSList *l;
1370 	Poly *p;
1371 	Edge *e;
1372 	int i, next_i;
1373 
1374 	for( l = polys; l; l = l->next ) {
1375 		p = (Poly *)l->data;
1376 
1377 		for( i = 0; i < p->num_verts; i++ ) {
1378 			next_i = (i+1) % p->num_verts;
1379 
1380 			/* this edge is not an outside edge if it is shared with others
1381 			in 'polys' */
1382 			if( polys_count_edge_usage( polys,
1383 										p->verts[i], p->verts[next_i]) > 1 )
1384 				continue;
1385 
1386 			/* create an Edge for this edge */
1387 			e = (Edge *)malloc( sizeof( Edge ) );
1388 			memset( e, '\0', sizeof( Edge ) );
1389 			e->v1 = p->verts[i];
1390 			e->v2 = p->verts[next_i];
1391 			/* check to see if it is already in the list */
1392 			if( g_slist_find_custom( edges, e,
1393 				(GCompareFunc)tools_edges_isNotEqual ) )
1394 			{
1395 				free( e );
1396 				continue;
1397 			}
1398 			/* and add it to the list */
1399 			edges = g_slist_prepend( edges, e );
1400 		}
1401 	}
1402 
1403 	return edges;
1404 }
1405 
1406 
tools_edges_isNotEqual(Edge * e1,Edge * e2)1407 gboolean tools_edges_isNotEqual( Edge *e1, Edge *e2 ) {
1408 	int equal = FALSE;
1409 
1410 	if( e1->v1 == e2->v1 && e1->v2 == e2->v2 )
1411 		equal = TRUE;
1412 	if( e1->v2 == e2->v1 && e1->v1 == e2->v2 )
1413 		equal = TRUE;
1414 
1415 	return !equal;
1416 }
1417 
1418 
tools_edges_areConnected(Edge * e1,Edge * e2)1419 gboolean tools_edges_areConnected( Edge *e1, Edge *e2 ) {
1420 	int connected = FALSE;
1421 
1422 	if( e1->v1 == e2->v1 || e1->v2 == e2->v2 ||
1423 		e1->v2 == e2->v1 || e1->v1 == e2->v2 )
1424 		connected = TRUE;
1425 
1426 	return connected;
1427 }
1428 
1429 
tools_edge_hasVertex(Edge * e,Vertex * v)1430 gboolean tools_edge_hasVertex( Edge *e, Vertex *v ) {
1431 	int result = FALSE;
1432 
1433 	if( e->v1 == v || e->v2 == v )
1434 		result = TRUE;
1435 
1436 	return result;
1437 }
1438 
1439 
1440 
1441 /* MISC TOOLS ***********************************************************/
1442 
tools_scale_along_normals(Model * model,GSList * polys,GSList * verts,float factor)1443 void tools_scale_along_normals( Model *model, GSList *polys, GSList *verts, float factor ) {
1444 
1445 	/* Note: this will likely *never* be undo-able.  The polygon thin/fatten
1446 	seems to behave predictably, but vertex thin/fatten tends to cause the
1447 	vertices to start "wandering" after a few manipulations.  These errors
1448 	will get very bad, very quickly.  As such, the application of this tool,
1449 	followed by an application of this tool with a negated 'factor', does not
1450 	necessarily return the model to its original state.  This makes it very
1451 	impractical to implement undo-ability for this tool. */
1452 
1453     GSList *l, *list_to_free = NULL;
1454     Vertex *v;
1455     int i;
1456 	float avg_normal[3], temp[3], len;
1457 
1458 	if( polys == NULL && verts == NULL )
1459 		return;
1460 
1461 	if( verts == NULL ) {
1462 		verts = polys_get_verts_unique( polys );
1463 		list_to_free = verts;
1464 	} else if( polys == NULL ) {
1465 		polys = verts_get_polys_unique( verts );
1466 		list_to_free = polys;
1467 	}
1468 
1469 	/* have all polys update their normal */
1470 	for( l = polys; l; l = l->next ) {
1471 		poly_find_normal( (Poly *)l->data );
1472 	}
1473 
1474 	/* for each vert... */
1475 	for( l = verts; l != NULL; l = l->next ) {
1476 		v = (Vertex*)l->data;
1477 
1478 		if( v->num_polys == 0 )
1479 			/* should never happen, but... */
1480 			continue;
1481 
1482 		vector_zero( avg_normal );
1483 
1484 		/* find the average normal, over all polys using this vert */
1485 		for( i = 0; i < v->num_polys; i++ ) {
1486 			vector_add( avg_normal, avg_normal, v->polys[i]->normal );
1487 		}
1488 		vector_mul( avg_normal, avg_normal, 1. / (float)v->num_polys );
1489 
1490 		/* normalize the avg_normal vector, also make sure it wasn't
1491 		   zero-length before it was normalized (which would indicate
1492 		   that something weird happened, like co-planar polys with normals
1493 		   pointing in opposite directions, etc) */
1494 		len = vector_normalize( avg_normal );
1495 		if( len < 0.01 )
1496 			continue;
1497 
1498 		/* scale vertex in direction avg_normal is pointing */
1499 		vector_find_point( temp, v->v, avg_normal, factor );
1500 		vector_copy( v->v, temp );
1501 	}
1502 
1503 	action_do( model, ACTION_UNKNOWN, NULL, NULL, NULL, NULL, NULL, NULL );
1504 
1505 	g_slist_free( list_to_free );
1506 }
1507 
1508 
1509 
1510 
1511 
1512