=== Custom Transform Orientation ===
[blender.git] / source / blender / src / transform_snap.c
1 /**
2  * $Id: 
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): Martin Poirier
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  */
32  
33 #include <stdlib.h>
34 #include <math.h>
35 #include <stdio.h>
36
37 #include "PIL_time.h"
38
39 #include "DNA_object_types.h"
40 #include "DNA_scene_types.h"
41 #include "DNA_meshdata_types.h" // Temporary, for snapping to other unselected meshes
42 #include "DNA_space_types.h"
43 #include "DNA_screen_types.h"
44 #include "DNA_userdef_types.h"
45 #include "DNA_view3d_types.h"
46
47 #include "BLI_arithb.h"
48 #include "BLI_editVert.h"
49
50 #include "BDR_drawobject.h"
51
52 #include "editmesh.h"
53 #include "BIF_editsima.h"
54 #include "BIF_gl.h"
55 #include "BIF_glutil.h"
56 #include "BIF_mywindow.h"
57 #include "BIF_resources.h"
58 #include "BIF_screen.h"
59 #include "BIF_editsima.h"
60 #include "BIF_drawimage.h"
61
62 #include "BKE_global.h"
63 #include "BKE_utildefines.h"
64 #include "BKE_DerivedMesh.h"
65 #include "BKE_object.h"
66
67 #include "BSE_view.h"
68
69 #include "MEM_guardedalloc.h"
70
71 #include "transform.h"
72 #include "mydevice.h"           /* for KEY defines      */
73 #include "blendef.h" /* for selection modes */
74
75 /********************* PROTOTYPES ***********************/
76
77 void setSnappingCallback(TransInfo *t);
78
79 void ApplySnapTranslation(TransInfo *t, float vec[3]);
80 void ApplySnapRotation(TransInfo *t, float *vec);
81
82 void CalcSnapGrid(TransInfo *t, float *vec);
83 void CalcSnapGeometry(TransInfo *t, float *vec);
84
85 void TargetSnapMedian(TransInfo *t);
86 void TargetSnapCenter(TransInfo *t);
87 void TargetSnapClosest(TransInfo *t);
88
89 float RotationBetween(TransInfo *t, float p1[3], float p2[3]);
90 float TranslationBetween(TransInfo *t, float p1[3], float p2[3]);
91
92 // Trickery
93 int findNearestVertFromObjects(int *dist, float *loc, int selected);
94
95 /****************** IMPLEMENTATIONS *********************/
96
97 int BIF_snappingSupported(void)
98 {
99         int status = 0;
100         
101         if (G.obedit == NULL || G.obedit->type==OB_MESH) /* only support object or mesh */
102         {
103                 status = 1;
104         }
105         
106         return status;
107 }
108
109 void drawSnapping(TransInfo *t)
110 {
111         if ((t->tsnap.status & (SNAP_ON|POINT_INIT|TARGET_INIT)) == (SNAP_ON|POINT_INIT|TARGET_INIT) &&
112                 (G.qual & LR_CTRLKEY)) {
113                 
114                 char col[4];
115                 BIF_GetThemeColor3ubv(TH_TRANSFORM, col);
116                 glColor4ub(col[0], col[1], col[2], 128);
117                 
118                 if (t->spacetype==SPACE_VIEW3D) {
119                         float unitmat[4][4];
120                         float size;
121                         
122                         glDisable(GL_DEPTH_TEST);
123         
124                         size = get_drawsize(G.vd);
125                         
126                         size *= 0.5f * BIF_GetThemeValuef(TH_VERTEX_SIZE);
127                         
128                         glPushMatrix();
129                         
130                         glTranslatef(t->tsnap.snapPoint[0], t->tsnap.snapPoint[1], t->tsnap.snapPoint[2]);
131                         
132                         /* sets view screen aligned */
133                         glRotatef( -360.0f*saacos(G.vd->viewquat[0])/(float)M_PI, G.vd->viewquat[1], G.vd->viewquat[2], G.vd->viewquat[3]);
134                         
135                         Mat4One(unitmat);
136                         drawcircball(GL_LINE_LOOP, unitmat[3], size, unitmat);
137                         
138                         glPopMatrix();
139                         
140                         if(G.vd->zbuf) glEnable(GL_DEPTH_TEST);
141                 } else if (t->spacetype==SPACE_IMAGE) {
142                         /*This will not draw, and Im nor sure why - campbell */
143                         
144                         /*                      
145                         float xuser_asp, yuser_asp;
146                         int wi, hi;
147                         float w, h;
148                         
149                         calc_image_view(G.sima, 'f');   // float
150                         myortho2(G.v2d->cur.xmin, G.v2d->cur.xmax, G.v2d->cur.ymin, G.v2d->cur.ymax);
151                         glLoadIdentity();
152                         
153                         aspect_sima(G.sima, &xuser_asp, &yuser_asp);
154                         
155                         transform_width_height_tface_uv(&wi, &hi);
156                         w = (((float)wi)/256.0f)*G.sima->zoom * xuser_asp;
157                         h = (((float)hi)/256.0f)*G.sima->zoom * yuser_asp;
158                         
159                         cpack(0xFFFFFF);
160                         glTranslatef(t->tsnap.snapPoint[0], t->tsnap.snapPoint[1], 0.0f);
161                         
162                         //glRectf(0,0,1,1);
163                         
164                         setlinestyle(0);
165                         cpack(0x0);
166                         fdrawline(-0.020/w, 0, -0.1/w, 0);
167                         fdrawline(0.1/w, 0, .020/w, 0);
168                         fdrawline(0, -0.020/h, 0, -0.1/h);
169                         fdrawline(0, 0.1/h, 0, 0.020/h);
170                         
171                         glTranslatef(-t->tsnap.snapPoint[0], -t->tsnap.snapPoint[1], 0.0f);
172                         setlinestyle(0);
173                         */
174                         
175                 }
176         }
177 }
178
179 int  handleSnapping(TransInfo *t, int event)
180 {
181         int status = 0;
182         
183         if (BIF_snappingSupported() && event == TABKEY && (G.qual & LR_SHIFTKEY) == LR_SHIFTKEY)
184         {
185                 /* toggle snap and reinit */
186                 G.scene->snap_flag ^= SCE_SNAP;
187                 initSnapping(t);
188                 status = 1;
189         }
190         
191         return status;
192 }
193
194 void applySnapping(TransInfo *t, float *vec)
195 {
196         if ((t->tsnap.status & SNAP_ON) &&
197                 (G.qual & LR_CTRLKEY))
198         {
199                 double current = PIL_check_seconds_timer();
200                 
201                 // Time base quirky code to go around findnearest slowness
202                 if (current - t->tsnap.last  >= 0.25)
203                 {
204                         t->tsnap.calcSnap(t, vec);
205                         t->tsnap.targetSnap(t);
206         
207                         t->tsnap.last = current;
208                 }
209                 if ((t->tsnap.status & (POINT_INIT|TARGET_INIT)) == (POINT_INIT|TARGET_INIT))
210                 {
211                         t->tsnap.applySnap(t, vec);
212                 }
213         }
214 }
215
216 void resetSnapping(TransInfo *t)
217 {
218         t->tsnap.status = 0;
219         t->tsnap.modePoint = 0;
220         t->tsnap.modeTarget = 0;
221         t->tsnap.last = 0;
222         t->tsnap.applySnap = NULL;
223 }
224
225 void initSnapping(TransInfo *t)
226 {
227         resetSnapping(t);
228         
229         if (t->spacetype == SPACE_VIEW3D || t->spacetype == SPACE_IMAGE) { // Only 3D view or UV
230                 setSnappingCallback(t);
231
232                 /* Edit mode */
233                 if (t->tsnap.applySnap != NULL && // A snapping function actually exist
234                         (G.scene->snap_flag & SCE_SNAP) && // Only if the snap flag is on
235                         (G.obedit != NULL && G.obedit->type==OB_MESH) && // Temporary limited to edit mode meshes
236                         ((t->flag & T_PROP_EDIT) == 0) ) // No PET, obviously
237                 {
238                         t->tsnap.status |= SNAP_ON;
239                         t->tsnap.modePoint = SNAP_GEO;
240                 }
241                 /* Object mode */
242                 else if (t->tsnap.applySnap != NULL && // A snapping function actually exist
243                         (G.scene->snap_flag & SCE_SNAP) && // Only if the snap flag is on
244                         (G.obedit == NULL) ) // Object Mode
245                 {
246                         t->tsnap.status |= SNAP_ON;
247                         t->tsnap.modePoint = SNAP_GEO;
248                 }
249                 else
250                 {       
251                         /* Grid if snap is not possible */
252                         t->tsnap.modePoint = SNAP_GRID;
253                 }
254         }
255         else
256         {
257                 /* Always grid outside of 3D view */
258                 t->tsnap.modePoint = SNAP_GRID;
259         }
260 }
261
262 void setSnappingCallback(TransInfo *t)
263 {
264         t->tsnap.calcSnap = CalcSnapGeometry;
265
266         switch(G.scene->snap_target)
267         {
268                 case SCE_SNAP_TARGET_CLOSEST:
269                         t->tsnap.modeTarget = SNAP_CLOSEST;
270                         t->tsnap.targetSnap = TargetSnapClosest;
271                         break;
272                 case SCE_SNAP_TARGET_CENTER:
273                         t->tsnap.modeTarget = SNAP_CENTER;
274                         t->tsnap.targetSnap = TargetSnapCenter;
275                         break;
276                 case SCE_SNAP_TARGET_MEDIAN:
277                         t->tsnap.modeTarget = SNAP_MEDIAN;
278                         t->tsnap.targetSnap = TargetSnapMedian;
279                         break;
280         }
281
282         switch (t->mode)
283         {
284         case TFM_TRANSLATION:
285                 t->tsnap.applySnap = ApplySnapTranslation;
286                 t->tsnap.distance = TranslationBetween;
287                 break;
288         case TFM_ROTATION:
289                 t->tsnap.applySnap = ApplySnapRotation;
290                 t->tsnap.distance = RotationBetween;
291                 
292                 // Can't do TARGET_CENTER with rotation, use TARGET_MEDIAN instead
293                 if (G.scene->snap_target == SCE_SNAP_TARGET_CENTER) {
294                         t->tsnap.modeTarget = SNAP_MEDIAN;
295                         t->tsnap.targetSnap = TargetSnapMedian;
296                 }
297                 break;
298         default:
299                 t->tsnap.applySnap = NULL;
300                 break;
301         }
302 }
303
304 /********************** APPLY **************************/
305
306 void ApplySnapTranslation(TransInfo *t, float vec[3])
307 {
308         VecSubf(vec, t->tsnap.snapPoint, t->tsnap.snapTarget);
309 }
310
311 void ApplySnapRotation(TransInfo *t, float *vec)
312 {
313         if (t->tsnap.modeTarget == SNAP_CLOSEST) {
314                 *vec = t->tsnap.dist;
315         }
316         else {
317                 *vec = RotationBetween(t, t->tsnap.snapTarget, t->tsnap.snapPoint);
318         }
319 }
320
321
322 /********************** DISTANCE **************************/
323
324 float TranslationBetween(TransInfo *t, float p1[3], float p2[3])
325 {
326         return VecLenf(p1, p2);
327 }
328
329 float RotationBetween(TransInfo *t, float p1[3], float p2[3])
330 {
331         float angle, start[3], end[3], center[3];
332         
333         VECCOPY(center, t->center);     
334         if(t->flag & (T_EDIT|T_POSE)) {
335                 Object *ob= G.obedit?G.obedit:t->poseobj;
336                 Mat4MulVecfl(ob->obmat, center);
337         }
338
339         VecSubf(start, p1, center);
340         VecSubf(end, p2, center);       
341                 
342         // Angle around a constraint axis (error prone, will need debug)
343         if (t->con.applyRot != NULL && (t->con.mode & CON_APPLY)) {
344                 float axis[3], tmp[3];
345                 
346                 t->con.applyRot(t, NULL, axis);
347
348                 Projf(tmp, end, axis);
349                 VecSubf(end, end, tmp);
350                 
351                 Projf(tmp, start, axis);
352                 VecSubf(start, start, tmp);
353                 
354                 Normalize(end);
355                 Normalize(start);
356                 
357                 Crossf(tmp, start, end);
358                 
359                 if (Inpf(tmp, axis) < 0.0)
360                         angle = -acos(Inpf(start, end));
361                 else    
362                         angle = acos(Inpf(start, end));
363         }
364         else {
365                 float mtx[3][3];
366                 
367                 Mat3CpyMat4(mtx, t->viewmat);
368
369                 Mat3MulVecfl(mtx, end);
370                 Mat3MulVecfl(mtx, start);
371                 
372                 angle = atan2(start[1],start[0]) - atan2(end[1],end[0]);
373         }
374         
375         if (angle > M_PI) {
376                 angle = angle - 2 * M_PI;
377         }
378         else if (angle < -(M_PI)) {
379                 angle = 2 * M_PI + angle;
380         }
381         
382         return angle;
383 }
384
385 /********************** CALC **************************/
386
387 void CalcSnapGrid(TransInfo *t, float *vec)
388 {
389         snapGridAction(t, t->tsnap.snapPoint, BIG_GEARS);
390 }
391
392 void CalcSnapGeometry(TransInfo *t, float *vec)
393 {
394         if (G.obedit == NULL)
395         {
396                 if (t->spacetype == SPACE_VIEW3D)
397                 {
398                         float vec[3];
399                         int found = 0;
400                         int dist = 40; // Use a user defined value here
401                         
402                         found = findNearestVertFromObjects(&dist, vec, 0);
403                         if (found == 1)
404                         {
405                                 VECCOPY(t->tsnap.snapPoint, vec);
406                                 
407                                 t->tsnap.status |=  POINT_INIT;
408                         }
409                         else
410                         {
411                                 t->tsnap.status &= ~POINT_INIT;
412                         }
413                 }
414         }
415         else if (G.obedit != NULL && G.obedit->type==OB_MESH)
416         {
417                 /*if (G.scene->selectmode & B_SEL_VERT)*/
418                 
419                 if (t->spacetype == SPACE_VIEW3D)
420                 {
421                         EditVert *nearest=NULL;
422                         float vec[3];
423                         int found = 0;
424                         int dist = 40; // Use a user defined value here
425                         
426                         // use findnearestverts in vert mode, others in other modes
427                         nearest = findnearestvert(&dist, SELECT, 1);
428                         
429                         found = findNearestVertFromObjects(&dist, vec, SELECT);
430                         if (found == 1)
431                         {
432                                 VECCOPY(t->tsnap.snapPoint, vec);
433                                 
434                                 t->tsnap.status |=  POINT_INIT;
435                         }
436                         /* If there's no outside vertex nearer, but there's one in this mesh
437                          */
438                         else if (nearest != NULL)
439                         {
440                                 VECCOPY(t->tsnap.snapPoint, nearest->co);
441                                 Mat4MulVecfl(G.obedit->obmat, t->tsnap.snapPoint);
442                                 
443                                 t->tsnap.status |=  POINT_INIT;
444                         }
445                         else
446                         {
447                                 t->tsnap.status &= ~POINT_INIT;
448                         }
449                 }
450                 else if (t->spacetype == SPACE_IMAGE)
451                 {       /* same as above but for UV's */
452                         MTFace *nearesttf=NULL;
453                         int face_corner;
454                         
455                         find_nearest_uv(&nearesttf, NULL, NULL, &face_corner);
456                         
457                         if (nearesttf != NULL)
458                         {
459                                 VECCOPY2D(t->tsnap.snapPoint, nearesttf->uv[face_corner]);
460                                 //Mat4MulVecfl(G.obedit->obmat, t->tsnap.snapPoint);
461                                 
462                                 t->tsnap.status |=  POINT_INIT;
463                         }
464                         else
465                         {
466                                 t->tsnap.status &= ~POINT_INIT;
467                         }
468                 }
469                 
470                 
471                 /*
472                 if (G.scene->selectmode & B_SEL_EDGE)
473                 {
474                         EditEdge *nearest=NULL;
475                         int dist = 50; // Use a user defined value here
476                         
477                         // use findnearestverts in vert mode, others in other modes
478                         nearest = findnearestedge(&dist);
479                         
480                         if (nearest != NULL)
481                         {
482                                 VecAddf(t->tsnap.snapPoint, nearest->v1->co, nearest->v2->co);
483                                 
484                                 VecMulf(t->tsnap.snapPoint, 0.5f); 
485                                 
486                                 Mat4MulVecfl(G.obedit->obmat, t->tsnap.snapPoint);
487                                 
488                                 t->tsnap.status |=  POINT_INIT;
489                         }
490                         else
491                         {
492                                 t->tsnap.status &= ~POINT_INIT;
493                         }
494                 }
495                 */
496         }
497 }
498
499 /********************** TARGET **************************/
500
501 void TargetSnapCenter(TransInfo *t)
502 {
503         // Only need to calculate once
504         if ((t->tsnap.status & TARGET_INIT) == 0)
505         {
506                 VECCOPY(t->tsnap.snapTarget, t->center);        
507                 if(t->flag & (T_EDIT|T_POSE)) {
508                         Object *ob= G.obedit?G.obedit:t->poseobj;
509                         Mat4MulVecfl(ob->obmat, t->tsnap.snapTarget);
510                 }
511                 
512                 t->tsnap.status |= TARGET_INIT;         
513         }
514 }
515
516 void TargetSnapMedian(TransInfo *t)
517 {
518         // Only need to calculate once
519         if ((t->tsnap.status & TARGET_INIT) == 0)
520         {
521                 TransData *td = NULL;
522                 int i;
523
524                 t->tsnap.snapTarget[0] = 0;
525                 t->tsnap.snapTarget[1] = 0;
526                 t->tsnap.snapTarget[2] = 0;
527                 
528                 for(td = t->data, i = 0 ; i < t->total && td->flag & TD_SELECTED ; i++, td++)
529                 {
530                         VecAddf(t->tsnap.snapTarget, t->tsnap.snapTarget, td->center);
531                 }
532                 
533                 VecMulf(t->tsnap.snapTarget, 1.0 / t->total);
534                 
535                 if(t->flag & (T_EDIT|T_POSE)) {
536                         Object *ob= G.obedit?G.obedit:t->poseobj;
537                         Mat4MulVecfl(ob->obmat, t->tsnap.snapTarget);
538                 }
539                 
540                 t->tsnap.status |= TARGET_INIT;         
541         }
542 }
543
544 void TargetSnapClosest(TransInfo *t)
545 {
546         // Only valid if a snap point has been selected
547         if (t->tsnap.status & POINT_INIT)
548         {
549                 TransData *closest = NULL, *td = NULL;
550                 
551                 /* Object mode */
552                 if (t->flag & T_OBJECT)
553                 {
554                         int i;
555                         for(td = t->data, i = 0 ; i < t->total && td->flag & TD_SELECTED ; i++, td++)
556                         {
557                                 struct BoundBox *bb = object_get_boundbox(td->ob);
558                                 
559                                 /* use boundbox if possible */
560                                 if (bb)
561                                 {
562                                         int j;
563                                         
564                                         for (j = 0; j < 8; j++) {
565                                                 float loc[3];
566                                                 float dist;
567                                                 
568                                                 VECCOPY(loc, bb->vec[j]);
569                                                 Mat4MulVecfl(td->ext->obmat, loc);
570                                                 
571                                                 dist = t->tsnap.distance(t, loc, t->tsnap.snapPoint);
572                                                 
573                                                 if (closest == NULL || fabs(dist) < fabs(t->tsnap.dist))
574                                                 {
575                                                         VECCOPY(t->tsnap.snapTarget, loc);
576                                                         closest = td;
577                                                         t->tsnap.dist = dist; 
578                                                 }
579                                         }
580                                 }
581                                 /* use element center otherwise */
582                                 else
583                                 {
584                                         float loc[3];
585                                         float dist;
586                                         
587                                         VECCOPY(loc, td->center);
588                                         
589                                         dist = t->tsnap.distance(t, loc, t->tsnap.snapPoint);
590                                         
591                                         if (closest == NULL || fabs(dist) < fabs(t->tsnap.dist))
592                                         {
593                                                 VECCOPY(t->tsnap.snapTarget, loc);
594                                                 closest = td;
595                                                 t->tsnap.dist = dist; 
596                                         }
597                                 }
598                         }
599                 }
600                 else
601                 {
602                         int i;
603                         for(td = t->data, i = 0 ; i < t->total && td->flag & TD_SELECTED ; i++, td++)
604                         {
605                                 float loc[3];
606                                 float dist;
607                                 
608                                 VECCOPY(loc, td->center);
609                                 
610                                 if(t->flag & (T_EDIT|T_POSE)) {
611                                         Object *ob= G.obedit?G.obedit:t->poseobj;
612                                         Mat4MulVecfl(ob->obmat, loc);
613                                 }
614                                 
615                                 dist = t->tsnap.distance(t, loc, t->tsnap.snapPoint);
616                                 
617                                 if (closest == NULL || fabs(dist) < fabs(t->tsnap.dist))
618                                 {
619                                         VECCOPY(t->tsnap.snapTarget, loc);
620                                         closest = td;
621                                         t->tsnap.dist = dist; 
622                                 }
623                         }
624                 }
625                 
626                 t->tsnap.status |= TARGET_INIT;
627         }
628 }
629 /*================================================================*/
630
631 int findNearestVertFromObjects(int *dist, float *loc, int selected) {
632         Base *base;
633         int retval = 0;
634         short mval[2];
635         
636         getmouseco_areawin(mval);
637         
638         base= FIRSTBASE;
639         for ( base = FIRSTBASE; base != NULL; base = base->next ) {
640                 if ( base != BASACT && BASE_SELECTABLE(base) && (base->flag & SELECT) == selected ) {
641                         Object *ob = base->object;
642                         
643                         if (ob->type == OB_MESH) {
644                                 Mesh *me = ob->data;
645                                 
646                                 if (me->totvert > 0) {
647                                         int test = 1;
648                                         int i;
649                                         
650                                         /* If number of vert is more than an arbitrary limit,
651                                          * test against boundbox first
652                                          * */
653                                         if (me->totvert > 16) {
654                                                 struct BoundBox *bb = object_get_boundbox(ob);
655                                                 
656                                                 int minx = 0, miny = 0, maxx = 0, maxy = 0;
657                                                 int i;
658                                                 
659                                                 for (i = 0; i < 8; i++) {
660                                                         float gloc[3];
661                                                         int sloc[2];
662                                                         
663                                                         VECCOPY(gloc, bb->vec[i]);
664                                                         Mat4MulVecfl(ob->obmat, gloc);
665                                                         project_int(gloc, sloc);
666                                                         
667                                                         if (i == 0) {
668                                                                 minx = maxx = sloc[0];
669                                                                 miny = maxy = sloc[1];
670                                                         }
671                                                         else {
672                                                                 if (minx > sloc[0]) minx = sloc[0];
673                                                                 else if (maxx < sloc[0]) maxx = sloc[0];
674                                                                 
675                                                                 if (miny > sloc[1]) miny = sloc[1];
676                                                                 else if (maxy < sloc[1]) maxy = sloc[1];
677                                                         }
678                                                 }
679                                                 
680                                                 /* Pad with distance */
681         
682                                                 minx -= *dist;
683                                                 miny -= *dist;
684                                                 maxx += *dist;
685                                                 maxy += *dist;
686                                                 
687                                                 if (mval[0] > maxx || mval[0] < minx ||
688                                                         mval[1] > maxy || mval[1] < miny) {
689                                                         
690                                                         test = 0;
691                                                 }
692                                         }
693                                         
694                                         if (test == 1) {
695                                                 float *verts = mesh_get_mapped_verts_nors(ob);
696                                                 
697                                                 if (verts != NULL) {
698                                                         float *fp;
699                                                         
700                                                         fp = verts;
701                                                         for( i = 0; i < me->totvert; i++, fp += 6) {
702                                                                 float gloc[3];
703                                                                 int sloc[2];
704                                                                 int curdist;
705                                                                 
706                                                                 VECCOPY(gloc, fp);
707                                                                 Mat4MulVecfl(ob->obmat, gloc);
708                                                                 project_int(gloc, sloc);
709                                                                 
710                                                                 sloc[0] -= mval[0];
711                                                                 sloc[1] -= mval[1];
712                                                                 
713                                                                 curdist = abs(sloc[0]) + abs(sloc[1]);
714                                                                 
715                                                                 if (curdist < *dist) {
716                                                                         *dist = curdist;
717                                                                         retval = 1;
718                                                                         VECCOPY(loc, gloc);
719                                                                 }
720                                                         }
721                                                 }
722
723                                                 MEM_freeN(verts);
724                                         }
725                                 }
726                         }
727                 }
728         }
729         
730         return retval;
731 }
732
733 /*================================================================*/
734
735 static void applyGrid(TransInfo *t, float *val, int max_index, float fac[3], GearsType action);
736
737
738 void snapGridAction(TransInfo *t, float *val, GearsType action) {
739         float fac[3];
740
741         fac[NO_GEARS]    = t->snap[0];
742         fac[BIG_GEARS]   = t->snap[1];
743         fac[SMALL_GEARS] = t->snap[2];
744         
745         applyGrid(t, val, t->idx_max, fac, action);
746 }
747
748
749 void snapGrid(TransInfo *t, float *val) {
750         int invert;
751         GearsType action;
752
753         // Only do something if using Snap to Grid
754         if (t->tsnap.modePoint != SNAP_GRID)
755                 return;
756
757         if(t->mode==TFM_ROTATION || t->mode==TFM_WARP || t->mode==TFM_TILT || t->mode==TFM_TRACKBALL || t->mode==TFM_BONE_ROLL)
758                 invert = U.flag & USER_AUTOROTGRID;
759         else if(t->mode==TFM_RESIZE || t->mode==TFM_SHEAR || t->mode==TFM_BONESIZE || t->mode==TFM_SHRINKFATTEN || t->mode==TFM_CURVE_SHRINKFATTEN)
760                 invert = U.flag & USER_AUTOSIZEGRID;
761         else
762                 invert = U.flag & USER_AUTOGRABGRID;
763
764         if(invert) {
765                 action = (G.qual & LR_CTRLKEY) ? NO_GEARS: BIG_GEARS;
766         }
767         else {
768                 action = (G.qual & LR_CTRLKEY) ? BIG_GEARS : NO_GEARS;
769         }
770         
771         if (action == BIG_GEARS && (G.qual & LR_SHIFTKEY)) {
772                 action = SMALL_GEARS;
773         }
774
775         snapGridAction(t, val, action);
776 }
777
778
779 static void applyGrid(TransInfo *t, float *val, int max_index, float fac[3], GearsType action)
780 {
781         int i;
782         float asp[3] = {1.0f, 1.0f, 1.0f}; // TODO: Remove hard coded limit here (3)
783
784         // Early bailing out if no need to snap
785         if (fac[action] == 0.0)
786                 return;
787         
788         /* evil hack - snapping needs to be adapted for image aspect ratio */
789         if((t->spacetype==SPACE_IMAGE) && (t->mode==TFM_TRANSLATION)) {
790                 transform_aspect_ratio_tface_uv(asp, asp+1);
791         }
792
793         for (i=0; i<=max_index; i++) {
794                 val[i]= fac[action]*asp[i]*(float)floor(val[i]/(fac[action]*asp[i]) +.5);
795         }
796 }