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