Merging r40598 through r40600 from trunk into soc-2011-tomato
[blender.git] / source / blender / editors / transform / transform_constraints.c
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL 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.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): none yet.
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 /** \file blender/editors/transform/transform_constraints.c
31  *  \ingroup edtransform
32  */
33
34
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <math.h>
39
40 #ifndef WIN32
41 #include <unistd.h>
42 #else
43 #include <io.h>
44 #endif
45
46
47 #include "DNA_object_types.h"
48 #include "DNA_scene_types.h"
49 #include "DNA_screen_types.h"
50 #include "DNA_space_types.h"
51 #include "DNA_view3d_types.h"
52
53 #include "BIF_gl.h"
54 #include "BIF_glutil.h"
55
56 #include "BKE_context.h"
57
58
59 #include "ED_image.h"
60 #include "ED_view3d.h"
61
62 #include "BLI_math.h"
63 #include "BLI_utildefines.h"
64
65 //#include "blendef.h"
66 //
67 //#include "mydevice.h"
68
69 #include "UI_resources.h"
70
71
72 #include "transform.h"
73
74 static void drawObjectConstraint(TransInfo *t);
75
76 /* ************************** CONSTRAINTS ************************* */
77 static void constraintAutoValues(TransInfo *t, float vec[3])
78 {
79         int mode = t->con.mode;
80         if (mode & CON_APPLY)
81         {
82                 float nval = (t->flag & T_NULL_ONE)?1.0f:0.0f;
83
84                 if ((mode & CON_AXIS0) == 0)
85                 {
86                         vec[0] = nval;
87                 }
88                 if ((mode & CON_AXIS1) == 0)
89                 {
90                         vec[1] = nval;
91                 }
92                 if ((mode & CON_AXIS2) == 0)
93                 {
94                         vec[2] = nval;
95                 }
96         }
97 }
98
99 void constraintNumInput(TransInfo *t, float vec[3])
100 {
101         int mode = t->con.mode;
102         if (mode & CON_APPLY) {
103                 float nval = (t->flag & T_NULL_ONE)?1.0f:0.0f;
104
105                 if (getConstraintSpaceDimension(t) == 2) {
106                         int axis = mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
107                         if (axis == (CON_AXIS0|CON_AXIS1)) {
108                                 /* vec[0] = vec[0]; */ /* same */
109                                 /* vec[1] = vec[1]; */ /* same */
110                                 vec[2] = nval;
111                         }
112                         else if (axis == (CON_AXIS1|CON_AXIS2)) {
113                                 vec[2] = vec[1];
114                                 vec[1] = vec[0];
115                                 vec[0] = nval;
116                         }
117                         else if (axis == (CON_AXIS0|CON_AXIS2)) {
118                                 /* vec[0] = vec[0]; */  /* same */
119                                 vec[2] = vec[1];
120                                 vec[1] = nval;
121                         }
122                 }
123                 else if (getConstraintSpaceDimension(t) == 1) {
124                         if (mode & CON_AXIS0) {
125                                 /* vec[0] = vec[0]; */ /* same */
126                                 vec[1] = nval;
127                                 vec[2] = nval;
128                         }
129                         else if (mode & CON_AXIS1) {
130                                 vec[1] = vec[0];
131                                 vec[0] = nval;
132                                 vec[2] = nval;
133                         }
134                         else if (mode & CON_AXIS2) {
135                                 vec[2] = vec[0];
136                                 vec[0] = nval;
137                                 vec[1] = nval;
138                         }
139                 }
140         }
141 }
142
143 static void postConstraintChecks(TransInfo *t, float vec[3], float pvec[3]) {
144         int i = 0;
145
146         mul_m3_v3(t->con.imtx, vec);
147
148         snapGrid(t, vec);
149
150         if (t->num.flag & T_NULL_ONE) {
151                 if (!(t->con.mode & CON_AXIS0))
152                         vec[0] = 1.0f;
153
154                 if (!(t->con.mode & CON_AXIS1))
155                         vec[1] = 1.0f;
156
157                 if (!(t->con.mode & CON_AXIS2))
158                         vec[2] = 1.0f;
159         }
160
161         if (hasNumInput(&t->num)) {
162                 applyNumInput(&t->num, vec);
163                 removeAspectRatio(t, vec);
164                 constraintNumInput(t, vec);
165         }
166
167         /* autovalues is operator param, use that directly but not if snapping is forced */
168         if (t->flag & T_AUTOVALUES && (t->tsnap.status & SNAP_FORCED) == 0)
169         {
170                 mul_v3_m3v3(vec, t->con.imtx, t->auto_values);
171                 constraintAutoValues(t, vec);
172                 /* inverse transformation at the end */
173         }
174
175         if (t->con.mode & CON_AXIS0) {
176                 pvec[i++] = vec[0];
177         }
178         if (t->con.mode & CON_AXIS1) {
179                 pvec[i++] = vec[1];
180         }
181         if (t->con.mode & CON_AXIS2) {
182                 pvec[i++] = vec[2];
183         }
184
185         mul_m3_v3(t->con.mtx, vec);
186 }
187
188 static void viewAxisCorrectCenter(TransInfo *t, float t_con_center[3])
189 {
190         if(t->spacetype == SPACE_VIEW3D) {
191                 // View3D *v3d = t->sa->spacedata.first;
192                 const float min_dist= 1.0f; // v3d->near;
193                 float dir[3];
194                 float l;
195
196                 sub_v3_v3v3(dir, t_con_center, t->viewinv[3]);
197                 if(dot_v3v3(dir, t->viewinv[2]) < 0.0f) {
198                         negate_v3(dir);
199                 }
200                 project_v3_v3v3(dir, dir, t->viewinv[2]);
201
202                 l= len_v3(dir);
203
204                 if(l < min_dist) {
205                         float diff[3];
206                         normalize_v3_v3(diff, t->viewinv[2]);
207                         mul_v3_fl(diff, min_dist - l);
208
209                         sub_v3_v3(t_con_center, diff);
210                 }
211         }
212 }
213
214 static void axisProjection(TransInfo *t, float axis[3], float in[3], float out[3]) {
215         float norm[3], vec[3], factor, angle;
216         float t_con_center[3];
217
218         if(in[0]==0.0f && in[1]==0.0f && in[2]==0.0f)
219                 return;
220
221         copy_v3_v3(t_con_center, t->con.center);
222
223         /* checks for center being too close to the view center */
224         viewAxisCorrectCenter(t, t_con_center);
225         
226         angle = fabsf(angle_v3v3(axis, t->viewinv[2]));
227         if (angle > (float)M_PI / 2.0f) {
228                 angle = (float)M_PI - angle;
229         }
230         angle = RAD2DEGF(angle);
231
232         /* For when view is parallel to constraint... will cause NaNs otherwise
233            So we take vertical motion in 3D space and apply it to the
234            constraint axis. Nice for camera grab + MMB */
235         if(angle < 5.0f) {
236                 project_v3_v3v3(vec, in, t->viewinv[1]);
237                 factor = dot_v3v3(t->viewinv[1], vec) * 2.0f;
238                 /* since camera distance is quite relative, use quadratic relationship. holding shift can compensate */
239                 if(factor<0.0f) factor*= -factor;
240                 else factor*= factor;
241
242                 copy_v3_v3(out, axis);
243                 normalize_v3(out);
244                 mul_v3_fl(out, -factor);        /* -factor makes move down going backwards */
245         }
246         else {
247                 float v[3], i1[3], i2[3];
248                 float v2[3], v4[3];
249                 float norm_center[3];
250                 float plane[3];
251
252                 getViewVector(t, t_con_center, norm_center);
253                 cross_v3_v3v3(plane, norm_center, axis);
254
255                 project_v3_v3v3(vec, in, plane);
256                 sub_v3_v3v3(vec, in, vec);
257                 
258                 add_v3_v3v3(v, vec, t_con_center);
259                 getViewVector(t, v, norm);
260
261                 /* give arbitrary large value if projection is impossible */
262                 factor = dot_v3v3(axis, norm);
263                 if (1.0f - fabsf(factor) < 0.0002f) {
264                         copy_v3_v3(out, axis);
265                         if (factor > 0) {
266                                 mul_v3_fl(out, 1000000000.0f);
267                         } else {
268                                 mul_v3_fl(out, -1000000000.0f);
269                         }
270                 } else {
271                         add_v3_v3v3(v2, t_con_center, axis);
272                         add_v3_v3v3(v4, v, norm);
273                         
274                         isect_line_line_v3(t_con_center, v2, v, v4, i1, i2);
275                         
276                         sub_v3_v3v3(v, i2, v);
277         
278                         sub_v3_v3v3(out, i1, t_con_center);
279
280                         /* possible some values become nan when
281                          * viewpoint and object are both zero */
282                         if(!finite(out[0])) out[0]= 0.0f;
283                         if(!finite(out[1])) out[1]= 0.0f;
284                         if(!finite(out[2])) out[2]= 0.0f;
285                 }
286         }
287 }
288
289 static void planeProjection(TransInfo *t, float in[3], float out[3]) {
290         float vec[3], factor, norm[3];
291
292         add_v3_v3v3(vec, in, t->con.center);
293         getViewVector(t, vec, norm);
294
295         sub_v3_v3v3(vec, out, in);
296
297         factor = dot_v3v3(vec, norm);
298         if (fabs(factor) <= 0.001) {
299                 return; /* prevent divide by zero */
300         }
301         factor = dot_v3v3(vec, vec) / factor;
302
303         copy_v3_v3(vec, norm);
304         mul_v3_fl(vec, factor);
305
306         add_v3_v3v3(out, in, vec);
307 }
308
309 /*
310  * Generic callback for constant spacial constraints applied to linear motion
311  *
312  * The IN vector in projected into the constrained space and then further
313  * projected along the view vector.
314  * (in perspective mode, the view vector is relative to the position on screen)
315  *
316  */
317
318 static void applyAxisConstraintVec(TransInfo *t, TransData *td, float in[3], float out[3], float pvec[3])
319 {
320         copy_v3_v3(out, in);
321         if (!td && t->con.mode & CON_APPLY) {
322                 mul_m3_v3(t->con.pmtx, out);
323
324                 // With snap, a projection is alright, no need to correct for view alignment
325                 if (!(t->tsnap.mode != SCE_SNAP_MODE_INCREMENT && activeSnap(t))) {
326                         if (getConstraintSpaceDimension(t) == 2) {
327                                 if (out[0] != 0.0f || out[1] != 0.0f || out[2] != 0.0f) {
328                                         planeProjection(t, in, out);
329                                 }
330                         }
331                         else if (getConstraintSpaceDimension(t) == 1) {
332                                 float c[3];
333
334                                 if (t->con.mode & CON_AXIS0) {
335                                         copy_v3_v3(c, t->con.mtx[0]);
336                                 }
337                                 else if (t->con.mode & CON_AXIS1) {
338                                         copy_v3_v3(c, t->con.mtx[1]);
339                                 }
340                                 else if (t->con.mode & CON_AXIS2) {
341                                         copy_v3_v3(c, t->con.mtx[2]);
342                                 }
343                                 axisProjection(t, c, in, out);
344                         }
345                 }
346                 postConstraintChecks(t, out, pvec);
347         }
348 }
349
350 /*
351  * Generic callback for object based spatial constraints applied to linear motion
352  *
353  * At first, the following is applied to the first data in the array
354  * The IN vector in projected into the constrained space and then further
355  * projected along the view vector.
356  * (in perspective mode, the view vector is relative to the position on screen)
357  *
358  * Further down, that vector is mapped to each data's space.
359  */
360
361 static void applyObjectConstraintVec(TransInfo *t, TransData *td, float in[3], float out[3], float pvec[3])
362 {
363         copy_v3_v3(out, in);
364         if (t->con.mode & CON_APPLY) {
365                 if (!td) {
366                         mul_m3_v3(t->con.pmtx, out);
367                         if (getConstraintSpaceDimension(t) == 2) {
368                                 if (out[0] != 0.0f || out[1] != 0.0f || out[2] != 0.0f) {
369                                         planeProjection(t, in, out);
370                                 }
371                         }
372                         else if (getConstraintSpaceDimension(t) == 1) {
373                                 float c[3];
374
375                                 if (t->con.mode & CON_AXIS0) {
376                                         copy_v3_v3(c, t->con.mtx[0]);
377                                 }
378                                 else if (t->con.mode & CON_AXIS1) {
379                                         copy_v3_v3(c, t->con.mtx[1]);
380                                 }
381                                 else if (t->con.mode & CON_AXIS2) {
382                                         copy_v3_v3(c, t->con.mtx[2]);
383                                 }
384                                 axisProjection(t, c, in, out);
385                         }
386                         postConstraintChecks(t, out, pvec);
387                         copy_v3_v3(out, pvec);
388                 }
389                 else {
390                         int i=0;
391
392                         out[0] = out[1] = out[2] = 0.0f;
393                         if (t->con.mode & CON_AXIS0) {
394                                 out[0] = in[i++];
395                         }
396                         if (t->con.mode & CON_AXIS1) {
397                                 out[1] = in[i++];
398                         }
399                         if (t->con.mode & CON_AXIS2) {
400                                 out[2] = in[i++];
401                         }
402                         mul_m3_v3(td->axismtx, out);
403                 }
404         }
405 }
406
407 /*
408  * Generic callback for constant spacial constraints applied to resize motion
409  *
410  *
411  */
412
413 static void applyAxisConstraintSize(TransInfo *t, TransData *td, float smat[3][3])
414 {
415         if (!td && t->con.mode & CON_APPLY) {
416                 float tmat[3][3];
417
418                 if (!(t->con.mode & CON_AXIS0)) {
419                         smat[0][0] = 1.0f;
420                 }
421                 if (!(t->con.mode & CON_AXIS1)) {
422                         smat[1][1] = 1.0f;
423                 }
424                 if (!(t->con.mode & CON_AXIS2)) {
425                         smat[2][2] = 1.0f;
426                 }
427
428                 mul_m3_m3m3(tmat, smat, t->con.imtx);
429                 mul_m3_m3m3(smat, t->con.mtx, tmat);
430         }
431 }
432
433 /*
434  * Callback for object based spacial constraints applied to resize motion
435  *
436  *
437  */
438
439 static void applyObjectConstraintSize(TransInfo *t, TransData *td, float smat[3][3])
440 {
441         if (td && t->con.mode & CON_APPLY) {
442                 float tmat[3][3];
443                 float imat[3][3];
444
445                 invert_m3_m3(imat, td->axismtx);
446
447                 if (!(t->con.mode & CON_AXIS0)) {
448                         smat[0][0] = 1.0f;
449                 }
450                 if (!(t->con.mode & CON_AXIS1)) {
451                         smat[1][1] = 1.0f;
452                 }
453                 if (!(t->con.mode & CON_AXIS2)) {
454                         smat[2][2] = 1.0f;
455                 }
456
457                 mul_m3_m3m3(tmat, smat, imat);
458                 mul_m3_m3m3(smat, td->axismtx, tmat);
459         }
460 }
461
462 /*
463  * Generic callback for constant spacial constraints applied to rotations
464  *
465  * The rotation axis is copied into VEC.
466  *
467  * In the case of single axis constraints, the rotation axis is directly the one constrained to.
468  * For planar constraints (2 axis), the rotation axis is the normal of the plane.
469  *
470  * The following only applies when CON_NOFLIP is not set.
471  * The vector is then modified to always point away from the screen (in global space)
472  * This insures that the rotation is always logically following the mouse.
473  * (ie: not doing counterclockwise rotations when the mouse moves clockwise).
474  */
475
476 static void applyAxisConstraintRot(TransInfo *t, TransData *td, float vec[3], float *angle)
477 {
478         if (!td && t->con.mode & CON_APPLY) {
479                 int mode = t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
480
481                 switch(mode) {
482                 case CON_AXIS0:
483                 case (CON_AXIS1|CON_AXIS2):
484                         copy_v3_v3(vec, t->con.mtx[0]);
485                         break;
486                 case CON_AXIS1:
487                 case (CON_AXIS0|CON_AXIS2):
488                         copy_v3_v3(vec, t->con.mtx[1]);
489                         break;
490                 case CON_AXIS2:
491                 case (CON_AXIS0|CON_AXIS1):
492                         copy_v3_v3(vec, t->con.mtx[2]);
493                         break;
494                 }
495                 /* don't flip axis if asked to or if num input */
496                 if (angle && (mode & CON_NOFLIP) == 0 && hasNumInput(&t->num) == 0) {
497                         if (dot_v3v3(vec, t->viewinv[2]) > 0.0f) {
498                                 *angle = -(*angle);
499                         }
500                 }
501         }
502 }
503
504 /*
505  * Callback for object based spacial constraints applied to rotations
506  *
507  * The rotation axis is copied into VEC.
508  *
509  * In the case of single axis constraints, the rotation axis is directly the one constrained to.
510  * For planar constraints (2 axis), the rotation axis is the normal of the plane.
511  *
512  * The following only applies when CON_NOFLIP is not set.
513  * The vector is then modified to always point away from the screen (in global space)
514  * This insures that the rotation is always logically following the mouse.
515  * (ie: not doing counterclockwise rotations when the mouse moves clockwise).
516  */
517
518 static void applyObjectConstraintRot(TransInfo *t, TransData *td, float vec[3], float *angle)
519 {
520         if (t->con.mode & CON_APPLY) {
521                 int mode = t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
522
523                 /* on setup call, use first object */
524                 if (td == NULL) {
525                         td= t->data;
526                 }
527
528                 switch(mode) {
529                 case CON_AXIS0:
530                 case (CON_AXIS1|CON_AXIS2):
531                         copy_v3_v3(vec, td->axismtx[0]);
532                         break;
533                 case CON_AXIS1:
534                 case (CON_AXIS0|CON_AXIS2):
535                         copy_v3_v3(vec, td->axismtx[1]);
536                         break;
537                 case CON_AXIS2:
538                 case (CON_AXIS0|CON_AXIS1):
539                         copy_v3_v3(vec, td->axismtx[2]);
540                         break;
541                 }
542                 if (angle && (mode & CON_NOFLIP) == 0 && hasNumInput(&t->num) == 0) {
543                         if (dot_v3v3(vec, t->viewinv[2]) > 0.0f) {
544                                 *angle = -(*angle);
545                         }
546                 }
547         }
548 }
549
550 /*--------------------- INTERNAL SETUP CALLS ------------------*/
551
552 void setConstraint(TransInfo *t, float space[3][3], int mode, const char text[]) {
553         strncpy(t->con.text + 1, text, 48);
554         copy_m3_m3(t->con.mtx, space);
555         t->con.mode = mode;
556         getConstraintMatrix(t);
557
558         startConstraint(t);
559
560         t->con.drawExtra = NULL;
561         t->con.applyVec = applyAxisConstraintVec;
562         t->con.applySize = applyAxisConstraintSize;
563         t->con.applyRot = applyAxisConstraintRot;
564         t->redraw = 1;
565 }
566
567 void setLocalConstraint(TransInfo *t, int mode, const char text[]) {
568         if (t->flag & T_EDIT) {
569                 float obmat[3][3];
570                 copy_m3_m4(obmat, t->scene->obedit->obmat);
571                 normalize_m3(obmat);
572                 setConstraint(t, obmat, mode, text);
573         }
574         else {
575                 if (t->total == 1) {
576                         setConstraint(t, t->data->axismtx, mode, text);
577                 }
578                 else {
579                         strncpy(t->con.text + 1, text, 48);
580                         copy_m3_m3(t->con.mtx, t->data->axismtx);
581                         t->con.mode = mode;
582                         getConstraintMatrix(t);
583
584                         startConstraint(t);
585
586                         t->con.drawExtra = drawObjectConstraint;
587                         t->con.applyVec = applyObjectConstraintVec;
588                         t->con.applySize = applyObjectConstraintSize;
589                         t->con.applyRot = applyObjectConstraintRot;
590                         t->redraw = 1;
591                 }
592         }
593 }
594
595 /*
596         Set the constraint according to the user defined orientation
597
598         ftext is a format string passed to sprintf. It will add the name of
599         the orientation where %s is (logically).
600 */
601 void setUserConstraint(TransInfo *t, short orientation, int mode, const char ftext[]) {
602         char text[40];
603
604         switch(orientation) {
605         case V3D_MANIP_GLOBAL:
606                 {
607                         float mtx[3][3]= MAT3_UNITY;
608                         sprintf(text, ftext, "global");
609                         setConstraint(t, mtx, mode, text);
610                 }
611                 break;
612         case V3D_MANIP_LOCAL:
613                 sprintf(text, ftext, "local");
614                 setLocalConstraint(t, mode, text);
615                 break;
616         case V3D_MANIP_NORMAL:
617                 sprintf(text, ftext, "normal");
618                 setConstraint(t, t->spacemtx, mode, text);
619                 break;
620         case V3D_MANIP_VIEW:
621                 sprintf(text, ftext, "view");
622                 setConstraint(t, t->spacemtx, mode, text);
623                 break;
624         case V3D_MANIP_GIMBAL:
625                 sprintf(text, ftext, "gimbal");
626                 setConstraint(t, t->spacemtx, mode, text);
627                 break;
628         default: /* V3D_MANIP_CUSTOM */
629                 sprintf(text, ftext, t->spacename);
630                 setConstraint(t, t->spacemtx, mode, text);
631                 break;
632         }
633
634         t->con.orientation = orientation;
635
636         t->con.mode |= CON_USER;
637 }
638
639 /*----------------- DRAWING CONSTRAINTS -------------------*/
640
641 void drawConstraint(TransInfo *t)
642 {
643         TransCon *tc = &(t->con);
644
645         if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE))
646                 return;
647         if (!(tc->mode & CON_APPLY))
648                 return;
649         if (t->flag & T_USES_MANIPULATOR)
650                 return;
651         if (t->flag & T_NO_CONSTRAINT)
652                 return;
653
654         /* nasty exception for Z constraint in camera view */
655         // TRANSFORM_FIX_ME
656 //      if((t->flag & T_OBJECT) && G.vd->camera==OBACT && G.vd->persp==V3D_CAMOB)
657 //              return;
658
659         if (tc->drawExtra) {
660                 tc->drawExtra(t);
661         }
662         else {
663                 if (tc->mode & CON_SELECT) {
664                         float vec[3];
665                         char col2[3] = {255,255,255};
666                         int depth_test_enabled;
667
668                         convertViewVec(t, vec, (t->mval[0] - t->con.imval[0]), (t->mval[1] - t->con.imval[1]));
669                         add_v3_v3(vec, tc->center);
670
671                         drawLine(t, tc->center, tc->mtx[0], 'X', 0);
672                         drawLine(t, tc->center, tc->mtx[1], 'Y', 0);
673                         drawLine(t, tc->center, tc->mtx[2], 'Z', 0);
674
675                         glColor3ubv((GLubyte *)col2);
676
677                         depth_test_enabled = glIsEnabled(GL_DEPTH_TEST);
678                         if(depth_test_enabled)
679                                 glDisable(GL_DEPTH_TEST);
680
681                         setlinestyle(1);
682                         glBegin(GL_LINE_STRIP);
683                                 glVertex3fv(tc->center);
684                                 glVertex3fv(vec);
685                         glEnd();
686                         setlinestyle(0);
687
688                         if(depth_test_enabled)
689                                 glEnable(GL_DEPTH_TEST);
690                 }
691
692                 if (tc->mode & CON_AXIS0) {
693                         drawLine(t, tc->center, tc->mtx[0], 'X', DRAWLIGHT);
694                 }
695                 if (tc->mode & CON_AXIS1) {
696                         drawLine(t, tc->center, tc->mtx[1], 'Y', DRAWLIGHT);
697                 }
698                 if (tc->mode & CON_AXIS2) {
699                         drawLine(t, tc->center, tc->mtx[2], 'Z', DRAWLIGHT);
700                 }
701         }
702 }
703
704 /* called from drawview.c, as an extra per-window draw option */
705 void drawPropCircle(const struct bContext *C, TransInfo *t)
706 {
707         if (t->flag & T_PROP_EDIT) {
708                 RegionView3D *rv3d = CTX_wm_region_view3d(C);
709                 float tmat[4][4], imat[4][4];
710                 float center[3];
711
712                 UI_ThemeColor(TH_GRID);
713
714                 if(t->spacetype == SPACE_VIEW3D && rv3d != NULL)
715                 {
716                         copy_m4_m4(tmat, rv3d->viewmat);
717                         invert_m4_m4(imat, tmat);
718                 }
719                 else
720                 {
721                         unit_m4(tmat);
722                         unit_m4(imat);
723                 }
724
725                 glPushMatrix();
726
727                 copy_v3_v3(center, t->center);
728
729                 if((t->spacetype == SPACE_VIEW3D) && t->obedit)
730                 {
731                         mul_m4_v3(t->obedit->obmat, center); /* because t->center is in local space */
732                 }
733                 else if(t->spacetype == SPACE_IMAGE)
734                 {
735                         float aspx, aspy;
736
737                         ED_space_image_uv_aspect(t->sa->spacedata.first, &aspx, &aspy);
738                         glScalef(1.0f/aspx, 1.0f/aspy, 1.0);
739                 }
740
741                 set_inverted_drawing(1);
742                 drawcircball(GL_LINE_LOOP, center, t->prop_size, imat);
743                 set_inverted_drawing(0);
744
745                 glPopMatrix();
746         }
747 }
748
749 static void drawObjectConstraint(TransInfo *t) {
750         int i;
751         TransData * td = t->data;
752
753         /* Draw the first one lighter because that's the one who controls the others.
754            Meaning the transformation is projected on that one and just copied on the others
755            constraint space.
756            In a nutshell, the object with light axis is controlled by the user and the others follow.
757            Without drawing the first light, users have little clue what they are doing.
758          */
759         if (t->con.mode & CON_AXIS0) {
760                 drawLine(t, td->ob->obmat[3], td->axismtx[0], 'X', DRAWLIGHT);
761         }
762         if (t->con.mode & CON_AXIS1) {
763                 drawLine(t, td->ob->obmat[3], td->axismtx[1], 'Y', DRAWLIGHT);
764         }
765         if (t->con.mode & CON_AXIS2) {
766                 drawLine(t, td->ob->obmat[3], td->axismtx[2], 'Z', DRAWLIGHT);
767         }
768
769         td++;
770
771         for(i=1;i<t->total;i++,td++) {
772                 if (t->con.mode & CON_AXIS0) {
773                         drawLine(t, td->ob->obmat[3], td->axismtx[0], 'X', 0);
774                 }
775                 if (t->con.mode & CON_AXIS1) {
776                         drawLine(t, td->ob->obmat[3], td->axismtx[1], 'Y', 0);
777                 }
778                 if (t->con.mode & CON_AXIS2) {
779                         drawLine(t, td->ob->obmat[3], td->axismtx[2], 'Z', 0);
780                 }
781         }
782 }
783
784 /*--------------------- START / STOP CONSTRAINTS ---------------------- */
785
786 void startConstraint(TransInfo *t) {
787         t->con.mode |= CON_APPLY;
788         *t->con.text = ' ';
789         t->num.idx_max = MIN2(getConstraintSpaceDimension(t) - 1, t->idx_max);
790 }
791
792 void stopConstraint(TransInfo *t) {
793         t->con.mode &= ~(CON_APPLY|CON_SELECT);
794         *t->con.text = '\0';
795         t->num.idx_max = t->idx_max;
796 }
797
798 void getConstraintMatrix(TransInfo *t)
799 {
800         float mat[3][3];
801         invert_m3_m3(t->con.imtx, t->con.mtx);
802         unit_m3(t->con.pmtx);
803
804         if (!(t->con.mode & CON_AXIS0)) {
805                 t->con.pmtx[0][0]               =
806                         t->con.pmtx[0][1]       =
807                         t->con.pmtx[0][2]       = 0.0f;
808         }
809
810         if (!(t->con.mode & CON_AXIS1)) {
811                 t->con.pmtx[1][0]               =
812                         t->con.pmtx[1][1]       =
813                         t->con.pmtx[1][2]       = 0.0f;
814         }
815
816         if (!(t->con.mode & CON_AXIS2)) {
817                 t->con.pmtx[2][0]               =
818                         t->con.pmtx[2][1]       =
819                         t->con.pmtx[2][2]       = 0.0f;
820         }
821
822         mul_m3_m3m3(mat, t->con.pmtx, t->con.imtx);
823         mul_m3_m3m3(t->con.pmtx, t->con.mtx, mat);
824 }
825
826 /*------------------------- MMB Select -------------------------------*/
827
828 void initSelectConstraint(TransInfo *t, float mtx[3][3])
829 {
830         copy_m3_m3(t->con.mtx, mtx);
831         t->con.mode |= CON_APPLY;
832         t->con.mode |= CON_SELECT;
833
834         setNearestAxis(t);
835         t->con.drawExtra = NULL;
836         t->con.applyVec = applyAxisConstraintVec;
837         t->con.applySize = applyAxisConstraintSize;
838         t->con.applyRot = applyAxisConstraintRot;
839 }
840
841 void selectConstraint(TransInfo *t) {
842         if (t->con.mode & CON_SELECT) {
843                 setNearestAxis(t);
844                 startConstraint(t);
845         }
846 }
847
848 void postSelectConstraint(TransInfo *t)
849 {
850         if (!(t->con.mode & CON_SELECT))
851                 return;
852
853         t->con.mode &= ~CON_AXIS0;
854         t->con.mode &= ~CON_AXIS1;
855         t->con.mode &= ~CON_AXIS2;
856         t->con.mode &= ~CON_SELECT;
857
858         setNearestAxis(t);
859
860         startConstraint(t);
861         t->redraw = 1;
862 }
863
864 static void setNearestAxis2d(TransInfo *t)
865 {
866         /* no correction needed... just use whichever one is lower */
867         if ( abs(t->mval[0]-t->con.imval[0]) < abs(t->mval[1]-t->con.imval[1]) ) {
868                 t->con.mode |= CON_AXIS1;
869                 sprintf(t->con.text, " along Y axis");
870         }
871         else {
872                 t->con.mode |= CON_AXIS0;
873                 sprintf(t->con.text, " along X axis");
874         }
875 }
876
877 static void setNearestAxis3d(TransInfo *t)
878 {
879         float zfac;
880         float mvec[3], axis[3], proj[3];
881         float len[3];
882         int i, icoord[2];
883
884         /* calculate mouse movement */
885         mvec[0] = (float)(t->mval[0] - t->con.imval[0]);
886         mvec[1] = (float)(t->mval[1] - t->con.imval[1]);
887         mvec[2] = 0.0f;
888
889         /* we need to correct axis length for the current zoomlevel of view,
890            this to prevent projected values to be clipped behind the camera
891            and to overflow the short integers.
892            The formula used is a bit stupid, just a simplification of the substraction
893            of two 2D points 30 pixels apart (that's the last factor in the formula) after
894            projecting them with window_to_3d_delta and then get the length of that vector.
895         */
896         zfac= t->persmat[0][3]*t->center[0]+ t->persmat[1][3]*t->center[1]+ t->persmat[2][3]*t->center[2]+ t->persmat[3][3];
897         zfac = len_v3(t->persinv[0]) * 2.0f/t->ar->winx * zfac * 30.0f;
898
899         for (i = 0; i<3; i++) {
900                 copy_v3_v3(axis, t->con.mtx[i]);
901
902                 mul_v3_fl(axis, zfac);
903                 /* now we can project to get window coordinate */
904                 add_v3_v3(axis, t->con.center);
905                 projectIntView(t, axis, icoord);
906
907                 axis[0] = (float)(icoord[0] - t->center2d[0]);
908                 axis[1] = (float)(icoord[1] - t->center2d[1]);
909                 axis[2] = 0.0f;
910
911                 if (normalize_v3(axis) != 0.0f) {
912                         project_v3_v3v3(proj, mvec, axis);
913                         sub_v3_v3v3(axis, mvec, proj);
914                         len[i] = normalize_v3(axis);
915                 }
916                 else {
917                         len[i] = 10000000000.0f;
918                 }
919         }
920
921         if (len[0] <= len[1] && len[0] <= len[2]) {
922                 if (t->modifiers & MOD_CONSTRAINT_PLANE) {
923                         t->con.mode |= (CON_AXIS1|CON_AXIS2);
924                         sprintf(t->con.text, " locking %s X axis", t->spacename);
925                 }
926                 else {
927                         t->con.mode |= CON_AXIS0;
928                         sprintf(t->con.text, " along %s X axis", t->spacename);
929                 }
930         }
931         else if (len[1] <= len[0] && len[1] <= len[2]) {
932                 if (t->modifiers & MOD_CONSTRAINT_PLANE) {
933                         t->con.mode |= (CON_AXIS0|CON_AXIS2);
934                         sprintf(t->con.text, " locking %s Y axis", t->spacename);
935                 }
936                 else {
937                         t->con.mode |= CON_AXIS1;
938                         sprintf(t->con.text, " along %s Y axis", t->spacename);
939                 }
940         }
941         else if (len[2] <= len[1] && len[2] <= len[0]) {
942                 if (t->modifiers & MOD_CONSTRAINT_PLANE) {
943                         t->con.mode |= (CON_AXIS0|CON_AXIS1);
944                         sprintf(t->con.text, " locking %s Z axis", t->spacename);
945                 }
946                 else {
947                         t->con.mode |= CON_AXIS2;
948                         sprintf(t->con.text, " along %s Z axis", t->spacename);
949                 }
950         }
951 }
952
953 void setNearestAxis(TransInfo *t)
954 {
955         /* clear any prior constraint flags */
956         t->con.mode &= ~CON_AXIS0;
957         t->con.mode &= ~CON_AXIS1;
958         t->con.mode &= ~CON_AXIS2;
959
960         /* constraint setting - depends on spacetype */
961         if (t->spacetype == SPACE_VIEW3D) {
962                 /* 3d-view */
963                 setNearestAxis3d(t);
964         }
965         else {
966                 /* assume that this means a 2D-Editor */
967                 setNearestAxis2d(t);
968         }
969
970         getConstraintMatrix(t);
971 }
972
973 /*-------------- HELPER FUNCTIONS ----------------*/
974
975 char constraintModeToChar(TransInfo *t) {
976         if ((t->con.mode & CON_APPLY)==0) {
977                 return '\0';
978         }
979         switch (t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2)) {
980         case (CON_AXIS0):
981         case (CON_AXIS1|CON_AXIS2):
982                 return 'X';
983         case (CON_AXIS1):
984         case (CON_AXIS0|CON_AXIS2):
985                 return 'Y';
986         case (CON_AXIS2):
987         case (CON_AXIS0|CON_AXIS1):
988                 return 'Z';
989         default:
990                 return '\0';
991         }
992 }
993
994
995 int isLockConstraint(TransInfo *t) {
996         int mode = t->con.mode;
997
998         if ( (mode & (CON_AXIS0|CON_AXIS1)) == (CON_AXIS0|CON_AXIS1))
999                 return 1;
1000
1001         if ( (mode & (CON_AXIS1|CON_AXIS2)) == (CON_AXIS1|CON_AXIS2))
1002                 return 1;
1003
1004         if ( (mode & (CON_AXIS0|CON_AXIS2)) == (CON_AXIS0|CON_AXIS2))
1005                 return 1;
1006
1007         return 0;
1008 }
1009
1010 /*
1011  * Returns the dimension of the constraint space.
1012  *
1013  * For that reason, the flags always needs to be set to properly evaluate here,
1014  * even if they aren't actually used in the callback function. (Which could happen
1015  * for weird constraints not yet designed. Along a path for example.)
1016  */
1017
1018 int getConstraintSpaceDimension(TransInfo *t)
1019 {
1020         int n = 0;
1021
1022         if (t->con.mode & CON_AXIS0)
1023                 n++;
1024
1025         if (t->con.mode & CON_AXIS1)
1026                 n++;
1027
1028         if (t->con.mode & CON_AXIS2)
1029                 n++;
1030
1031         return n;
1032 /*
1033   Someone willing to do it criptically could do the following instead:
1034
1035   return t->con & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
1036
1037   Based on the assumptions that the axis flags are one after the other and start at 1
1038 */
1039 }