Two in one:
[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_space_types.h"
42 #include "DNA_userdef_types.h"
43 #include "DNA_view3d_types.h"
44
45 #include "BLI_arithb.h"
46 #include "BLI_editVert.h"
47
48 #include "BDR_drawobject.h"
49
50 #include "editmesh.h"
51 #include "BIF_editsima.h"
52 #include "BIF_gl.h"
53 #include "BIF_glutil.h"
54 #include "BIF_mywindow.h"
55 #include "BIF_resources.h"
56
57 #include "BKE_global.h"
58 #include "BKE_utildefines.h"
59
60 #include "transform.h"
61 #include "mydevice.h"           /* for KEY defines      */
62 #include "blendef.h" /* for selection modes */
63
64 /********************* PROTOTYPES ***********************/
65
66 void setSnappingCallback(TransInfo *t);
67
68 void ApplySnapTranslation(TransInfo *t, float vec[3]);
69 void ApplySnapRotation(TransInfo *t, float *vec);
70
71 void CalcSnapGrid(TransInfo *t, float *vec);
72 void CalcSnapGeometry(TransInfo *t, float *vec);
73
74 void TargetSnapMedian(TransInfo *t);
75 void TargetSnapCenter(TransInfo *t);
76 void TargetSnapClosest(TransInfo *t);
77
78 float RotationBetween(TransInfo *t, float p1[3], float p2[3]);
79 float TranslationBetween(TransInfo *t, float p1[3], float p2[3]);
80
81 /****************** IMPLEMENTATIONS *********************/
82
83 void drawSnapping(TransInfo *t)
84 {
85         if ((t->tsnap.status & (SNAP_ON|POINT_INIT|TARGET_INIT)) == (SNAP_ON|POINT_INIT|TARGET_INIT) &&
86                 (G.qual & LR_CTRLKEY)) {
87                 float unitmat[4][4];
88                 float size;
89                 char col[4];
90                 
91                 size = get_drawsize(G.vd);
92                 
93                 size *= 0.5f * BIF_GetThemeValuef(TH_VERTEX_SIZE);
94                 
95                 BIF_GetThemeColor3ubv(TH_TRANSFORM, col);
96                 glColor4ub(col[0], col[1], col[2], 128);
97                 
98                 glPushMatrix();
99                 
100                 glTranslatef(t->tsnap.snapPoint[0], t->tsnap.snapPoint[1], t->tsnap.snapPoint[2]);
101                 
102                 /* sets view screen aligned */
103                 glRotatef( -360.0f*saacos(G.vd->viewquat[0])/(float)M_PI, G.vd->viewquat[1], G.vd->viewquat[2], G.vd->viewquat[3]);
104
105                 Mat4One(unitmat);
106                 drawcircball(GL_LINE_LOOP, unitmat[3], size, unitmat);
107                 glPopMatrix();
108         }
109 }
110
111 int  handleSnapping(TransInfo *t, int event)
112 {
113         int status = 0;
114         
115         // Put keyhandling code here
116         
117         return status;
118 }
119
120 void applySnapping(TransInfo *t, float *vec)
121 {
122         if ((t->tsnap.status & SNAP_ON) &&
123                 (G.qual & LR_CTRLKEY))
124         {
125                 double current = PIL_check_seconds_timer();
126                 
127                 // Time base quirky code to go around findnearest slowness
128                 if (current - t->tsnap.last  >= 0.25)
129                 {
130                         t->tsnap.calcSnap(t, vec);
131                         t->tsnap.targetSnap(t);
132         
133                         t->tsnap.last = current;
134                 }
135                 if ((t->tsnap.status & (POINT_INIT|TARGET_INIT)) == (POINT_INIT|TARGET_INIT))
136                 {
137                         t->tsnap.applySnap(t, vec);
138                 }
139         }
140 }
141
142 void resetSnapping(TransInfo *t)
143 {
144         t->tsnap.status = 0;
145         t->tsnap.modePoint = 0;
146         t->tsnap.modeTarget = 0;
147         t->tsnap.last = 0;
148         t->tsnap.applySnap = NULL;
149 }
150
151 void initSnapping(TransInfo *t)
152 {
153         resetSnapping(t);
154         setSnappingCallback(t);
155         
156         if (t->tsnap.applySnap != NULL && // A snapping function actually exist
157                 (G.obedit != NULL && G.obedit->type==OB_MESH) && // Temporary limited to edit mode meshes
158                 (t->spacetype == SPACE_VIEW3D) && // Only 3D view (not UV)
159                 (G.vd->flag2 & V3D_TRANSFORM_SNAP) && // Only if the snap flag is on
160                 (t->flag & T_PROP_EDIT) == 0) // No PET, obviously
161         {
162                 t->tsnap.status |= SNAP_ON;
163                 t->tsnap.modePoint = SNAP_GEO;
164         }
165         else
166         {
167                 t->tsnap.modePoint = SNAP_GRID;
168         }
169 }
170
171 void setSnappingCallback(TransInfo *t)
172 {
173         t->tsnap.calcSnap = CalcSnapGeometry;
174
175         
176         switch(G.vd->snap_target)
177         {
178                 case V3D_SNAP_TARGET_CLOSEST:
179                         t->tsnap.modeTarget = SNAP_CLOSEST;
180                         t->tsnap.targetSnap = TargetSnapClosest;
181                         break;
182                 case V3D_SNAP_TARGET_CENTER:
183                         t->tsnap.modeTarget = SNAP_CENTER;
184                         t->tsnap.targetSnap = TargetSnapCenter;
185                         break;
186                 case V3D_SNAP_TARGET_MEDIAN:
187                         t->tsnap.modeTarget = SNAP_MEDIAN;
188                         t->tsnap.targetSnap = TargetSnapMedian;
189                         break;
190         }
191
192         switch (t->mode)
193         {
194         case TFM_TRANSLATION:
195                 t->tsnap.applySnap = ApplySnapTranslation;
196                 t->tsnap.distance = TranslationBetween;
197                 break;
198         case TFM_ROTATION:
199                 t->tsnap.applySnap = ApplySnapRotation;
200                 t->tsnap.distance = RotationBetween;
201                 
202                 // Can't do TARGET_CENTER with rotation, use TARGET_MEDIAN instead
203                 if (G.vd->snap_target == V3D_SNAP_TARGET_CENTER) {
204                         t->tsnap.modeTarget = SNAP_MEDIAN;
205                         t->tsnap.targetSnap = TargetSnapMedian;
206                 }
207                 break;
208         case TFM_RESIZE:
209                 t->tsnap.applySnap = NULL;
210                 break;
211         default:
212                 t->tsnap.applySnap = NULL;
213                 break;
214         }
215 }
216
217 /********************** APPLY **************************/
218
219 void ApplySnapTranslation(TransInfo *t, float vec[3])
220 {
221         VecSubf(vec, t->tsnap.snapPoint, t->tsnap.snapTarget);
222 }
223
224 void ApplySnapRotation(TransInfo *t, float *vec)
225 {
226         if (t->tsnap.modeTarget == SNAP_CLOSEST) {
227                 *vec = t->tsnap.dist;
228         }
229         else {
230                 *vec = RotationBetween(t, t->tsnap.snapTarget, t->tsnap.snapPoint);
231         }
232 }
233
234
235 /********************** DISTANCE **************************/
236
237 float TranslationBetween(TransInfo *t, float p1[3], float p2[3])
238 {
239         return VecLenf(p1, p2);
240 }
241
242 float RotationBetween(TransInfo *t, float p1[3], float p2[3])
243 {
244         float angle, start[3], end[3], center[3];
245         
246         VECCOPY(center, t->center);     
247         if(t->flag & (T_EDIT|T_POSE)) {
248                 Object *ob= G.obedit?G.obedit:t->poseobj;
249                 Mat4MulVecfl(ob->obmat, center);
250         }
251
252         VecSubf(start, p1, center);
253         VecSubf(end, p2, center);       
254                 
255         // Angle around a constraint axis (error prone, will need debug)
256         if (t->con.applyRot != NULL && (t->con.mode & CON_APPLY)) {
257                 float axis[3], tmp[3];
258                 
259                 t->con.applyRot(t, NULL, axis);
260
261                 Projf(tmp, end, axis);
262                 VecSubf(end, end, tmp);
263                 
264                 Projf(tmp, start, axis);
265                 VecSubf(start, start, tmp);
266                 
267                 Normalise(end);
268                 Normalise(start);
269                 
270                 Crossf(tmp, start, end);
271                 
272                 if (Inpf(tmp, axis) < 0.0)
273                         angle = -acos(Inpf(start, end));
274                 else    
275                         angle = acos(Inpf(start, end));
276         }
277         else {
278                 float mtx[3][3];
279                 
280                 Mat3CpyMat4(mtx, t->viewmat);
281
282                 Mat3MulVecfl(mtx, end);
283                 Mat3MulVecfl(mtx, start);
284                 
285                 angle = atan2(start[1],start[0]) - atan2(end[1],end[0]);
286         }
287         
288         if (angle > M_PI) {
289                 angle = angle - 2 * M_PI;
290         }
291         else if (angle < -(M_PI)) {
292                 angle = 2 * M_PI + angle;
293         }
294         
295         return angle;
296 }
297
298 /********************** CALC **************************/
299
300 void CalcSnapGrid(TransInfo *t, float *vec)
301 {
302         snapGridAction(t, t->tsnap.snapPoint, BIG_GEARS);
303 }
304
305 void CalcSnapGeometry(TransInfo *t, float *vec)
306 {
307         if (G.obedit != NULL && G.obedit->type==OB_MESH)
308         {
309                 /*if (G.scene->selectmode & B_SEL_VERT)*/
310                 {
311                         EditVert *nearest=NULL;
312                         int dist = 40; // Use a user defined value here
313                         
314                         // use findnearestverts in vert mode, others in other modes
315                         nearest = findnearestvert(&dist, SELECT, 1);
316                         
317                         if (nearest != NULL)
318                         {
319                                 VECCOPY(t->tsnap.snapPoint, nearest->co);
320                                 
321                                 Mat4MulVecfl(G.obedit->obmat, t->tsnap.snapPoint);
322                                 
323                                 t->tsnap.status |=  POINT_INIT;
324                         }
325                         else
326                         {
327                                 t->tsnap.status &= ~POINT_INIT;
328                         }
329                 }
330                 /*
331                 if (G.scene->selectmode & B_SEL_EDGE)
332                 {
333                         EditEdge *nearest=NULL;
334                         int dist = 50; // Use a user defined value here
335                         
336                         // use findnearestverts in vert mode, others in other modes
337                         nearest = findnearestedge(&dist);
338                         
339                         if (nearest != NULL)
340                         {
341                                 VecAddf(t->tsnap.snapPoint, nearest->v1->co, nearest->v2->co);
342                                 
343                                 VecMulf(t->tsnap.snapPoint, 0.5f); 
344                                 
345                                 Mat4MulVecfl(G.obedit->obmat, t->tsnap.snapPoint);
346                                 
347                                 t->tsnap.status |=  POINT_INIT;
348                         }
349                         else
350                         {
351                                 t->tsnap.status &= ~POINT_INIT;
352                         }
353                 }
354                 */
355         }
356 }
357
358 /********************** TARGET **************************/
359
360 void TargetSnapCenter(TransInfo *t)
361 {
362         // Only need to calculate once
363         if ((t->tsnap.status & TARGET_INIT) == 0)
364         {
365                 VECCOPY(t->tsnap.snapTarget, t->center);        
366                 if(t->flag & (T_EDIT|T_POSE)) {
367                         Object *ob= G.obedit?G.obedit:t->poseobj;
368                         Mat4MulVecfl(ob->obmat, t->tsnap.snapTarget);
369                 }
370                 
371                 t->tsnap.status |= TARGET_INIT;         
372         }
373 }
374
375 void TargetSnapMedian(TransInfo *t)
376 {
377         // Only need to calculate once
378         if ((t->tsnap.status & TARGET_INIT) == 0)
379         {
380                 TransData *td = NULL;
381
382                 t->tsnap.snapTarget[0] = 0;
383                 t->tsnap.snapTarget[1] = 0;
384                 t->tsnap.snapTarget[2] = 0;
385                 
386                 for (td = t->data; td != NULL && td->flag & TD_SELECTED ; td++)
387                 {
388                         VecAddf(t->tsnap.snapTarget, t->tsnap.snapTarget, td->iloc);
389                 }
390                 
391                 VecMulf(t->tsnap.snapTarget, 1.0 / t->total);
392                 
393                 if(t->flag & (T_EDIT|T_POSE)) {
394                         Object *ob= G.obedit?G.obedit:t->poseobj;
395                         Mat4MulVecfl(ob->obmat, t->tsnap.snapTarget);
396                 }
397                 
398                 t->tsnap.status |= TARGET_INIT;         
399         }
400 }
401
402 void TargetSnapClosest(TransInfo *t)
403 {
404         // Only valid if a snap point has been selected
405         if (t->tsnap.status & POINT_INIT)
406         {
407                 TransData *closest = NULL, *td = NULL;
408                 
409                 // Base case, only one selected item
410                 if (t->total == 1)
411                 {
412                         VECCOPY(t->tsnap.snapTarget, t->data[0].iloc);
413
414                         if(t->flag & (T_EDIT|T_POSE)) {
415                                 Object *ob= G.obedit?G.obedit:t->poseobj;
416                                 Mat4MulVecfl(ob->obmat, t->tsnap.snapTarget);
417                         }
418                 }
419                 // More than one selected item
420                 else
421                         {
422                         for (td = t->data; td != NULL && td->flag & TD_SELECTED ; td++)
423                         {
424                                 float loc[3];
425                                 float dist;
426                                 
427                                 VECCOPY(loc, td->iloc);
428                                 
429                                 if(t->flag & (T_EDIT|T_POSE)) {
430                                         Object *ob= G.obedit?G.obedit:t->poseobj;
431                                         Mat4MulVecfl(ob->obmat, loc);
432                                 }
433                                 
434                                 dist = t->tsnap.distance(t, loc, t->tsnap.snapPoint);
435                                 
436                                 if (closest == NULL || fabs(dist) < fabs(t->tsnap.dist))
437                                 {
438                                         VECCOPY(t->tsnap.snapTarget, loc);
439                                         closest = td;
440                                         t->tsnap.dist = dist; 
441                                 }
442                         }
443                 }
444                 
445                 t->tsnap.status |= TARGET_INIT;
446         }
447 }
448
449 /*================================================================*/
450
451 static void applyGrid(TransInfo *t, float *val, int max_index, float fac[3], GearsType action);
452
453
454 void snapGridAction(TransInfo *t, float *val, GearsType action) {
455         float fac[3];
456
457         fac[NO_GEARS]    = t->snap[0];
458         fac[BIG_GEARS]   = t->snap[1];
459         fac[SMALL_GEARS] = t->snap[2];
460         
461         applyGrid(t, val, t->idx_max, fac, action);
462 }
463
464
465 void snapGrid(TransInfo *t, float *val) {
466         int invert;
467         GearsType action;
468         
469         // Only do something if using Snap to Grid
470         if (t->tsnap.modePoint != SNAP_GRID)
471                 return;
472
473         if(t->mode==TFM_ROTATION || t->mode==TFM_WARP || t->mode==TFM_TILT || t->mode==TFM_TRACKBALL || t->mode==TFM_BONE_ROLL)
474                 invert = U.flag & USER_AUTOROTGRID;
475         else if(t->mode==TFM_RESIZE || t->mode==TFM_SHEAR || t->mode==TFM_BONESIZE || t->mode==TFM_SHRINKFATTEN || t->mode==TFM_CURVE_SHRINKFATTEN)
476                 invert = U.flag & USER_AUTOSIZEGRID;
477         else
478                 invert = U.flag & USER_AUTOGRABGRID;
479
480         if(invert) {
481                 action = (G.qual & LR_CTRLKEY) ? NO_GEARS: BIG_GEARS;
482         }
483         else {
484                 action = (G.qual & LR_CTRLKEY) ? BIG_GEARS : NO_GEARS;
485         }
486         
487         if (action == BIG_GEARS && (G.qual & LR_SHIFTKEY)) {
488                 action = SMALL_GEARS;
489         }
490
491         snapGridAction(t, val, action);
492 }
493
494
495 static void applyGrid(TransInfo *t, float *val, int max_index, float fac[3], GearsType action)
496 {
497         int i;
498         float asp[3] = {1.0f, 1.0f, 1.0f}; // TODO: Remove hard coded limit here (3)
499
500         // Early bailing out if no need to snap
501         if (fac[action] == 0.0)
502                 return;
503         
504         /* evil hack - snapping needs to be adapted for image aspect ratio */
505         if((t->spacetype==SPACE_IMAGE) && (t->mode==TFM_TRANSLATION)) {
506                 transform_aspect_ratio_tface_uv(asp, asp+1);
507         }
508
509         for (i=0; i<=max_index; i++) {
510                 val[i]= fac[action]*asp[i]*(float)floor(val[i]/(fac[action]*asp[i]) +.5);
511         }
512 }