merging back to a version that works...
[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 #include "BLI_string.h"
63
64 //#include "blendef.h"
65 //
66 //#include "mydevice.h"
67
68 #include "UI_resources.h"
69
70
71 #include "transform.h"
72
73 static void drawObjectConstraint(TransInfo *t);
74
75 /* ************************** CONSTRAINTS ************************* */
76 static void constraintAutoValues(TransInfo *t, float vec[3])
77 {
78         int mode = t->con.mode;
79         if (mode & CON_APPLY)
80         {
81                 float nval = (t->flag & T_NULL_ONE)?1.0f:0.0f;
82
83                 if ((mode & CON_AXIS0) == 0) {
84                         vec[0] = nval;
85                 }
86                 if ((mode & CON_AXIS1) == 0) {
87                         vec[1] = nval;
88                 }
89                 if ((mode & CON_AXIS2) == 0) {
90                         vec[2] = nval;
91                 }
92         }
93 }
94
95 void constraintNumInput(TransInfo *t, float vec[3])
96 {
97         int mode = t->con.mode;
98         if (mode & CON_APPLY) {
99                 float nval = (t->flag & T_NULL_ONE)?1.0f:0.0f;
100
101                 if (getConstraintSpaceDimension(t) == 2) {
102                         int axis = mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
103                         if (axis == (CON_AXIS0|CON_AXIS1)) {
104                                 /* vec[0] = vec[0]; */ /* same */
105                                 /* vec[1] = vec[1]; */ /* same */
106                                 vec[2] = nval;
107                         }
108                         else if (axis == (CON_AXIS1|CON_AXIS2)) {
109                                 vec[2] = vec[1];
110                                 vec[1] = vec[0];
111                                 vec[0] = nval;
112                         }
113                         else if (axis == (CON_AXIS0|CON_AXIS2)) {
114                                 /* vec[0] = vec[0]; */  /* same */
115                                 vec[2] = vec[1];
116                                 vec[1] = nval;
117                         }
118                 }
119                 else if (getConstraintSpaceDimension(t) == 1) {
120                         if (mode & CON_AXIS0) {
121                                 /* vec[0] = vec[0]; */ /* same */
122                                 vec[1] = nval;
123                                 vec[2] = nval;
124                         }
125                         else if (mode & CON_AXIS1) {
126                                 vec[1] = vec[0];
127                                 vec[0] = nval;
128                                 vec[2] = nval;
129                         }
130                         else if (mode & CON_AXIS2) {
131                                 vec[2] = vec[0];
132                                 vec[0] = nval;
133                                 vec[1] = nval;
134                         }
135                 }
136         }
137 }
138
139 static void postConstraintChecks(TransInfo *t, float vec[3], float pvec[3])
140 {
141         int i = 0;
142
143         mul_m3_v3(t->con.imtx, vec);
144
145         snapGrid(t, vec);
146
147         if (t->num.flag & T_NULL_ONE) {
148                 if (!(t->con.mode & CON_AXIS0))
149                         vec[0] = 1.0f;
150
151                 if (!(t->con.mode & CON_AXIS1))
152                         vec[1] = 1.0f;
153
154                 if (!(t->con.mode & CON_AXIS2))
155                         vec[2] = 1.0f;
156         }
157
158         if (hasNumInput(&t->num)) {
159                 applyNumInput(&t->num, vec);
160                 removeAspectRatio(t, vec);
161                 constraintNumInput(t, vec);
162         }
163
164         /* autovalues is operator param, use that directly but not if snapping is forced */
165         if (t->flag & T_AUTOVALUES && (t->tsnap.status & SNAP_FORCED) == 0) {
166                 mul_v3_m3v3(vec, t->con.imtx, t->auto_values);
167                 constraintAutoValues(t, vec);
168                 /* inverse transformation at the end */
169         }
170
171         if (t->con.mode & CON_AXIS0) {
172                 pvec[i++] = vec[0];
173         }
174         if (t->con.mode & CON_AXIS1) {
175                 pvec[i++] = vec[1];
176         }
177         if (t->con.mode & CON_AXIS2) {
178                 pvec[i++] = vec[2];
179         }
180
181         mul_m3_v3(t->con.mtx, vec);
182 }
183
184 static void viewAxisCorrectCenter(TransInfo *t, float t_con_center[3])
185 {
186         if (t->spacetype == SPACE_VIEW3D) {
187                 // View3D *v3d = t->sa->spacedata.first;
188                 const float min_dist= 1.0f; // v3d->near;
189                 float dir[3];
190                 float l;
191
192                 sub_v3_v3v3(dir, t_con_center, t->viewinv[3]);
193                 if (dot_v3v3(dir, t->viewinv[2]) < 0.0f) {
194                         negate_v3(dir);
195                 }
196                 project_v3_v3v3(dir, dir, t->viewinv[2]);
197
198                 l= len_v3(dir);
199
200                 if (l < min_dist) {
201                         float diff[3];
202                         normalize_v3_v3(diff, t->viewinv[2]);
203                         mul_v3_fl(diff, min_dist - l);
204
205                         sub_v3_v3(t_con_center, diff);
206                 }
207         }
208 }
209
210 static void axisProjection(TransInfo *t, float axis[3], float in[3], float out[3])
211 {
212         float norm[3], vec[3], factor, angle;
213         float t_con_center[3];
214
215         if (in[0]==0.0f && in[1]==0.0f && in[2]==0.0f)
216                 return;
217
218         copy_v3_v3(t_con_center, t->con.center);
219
220         /* checks for center being too close to the view center */
221         viewAxisCorrectCenter(t, t_con_center);
222         
223         angle = fabsf(angle_v3v3(axis, t->viewinv[2]));
224         if (angle > (float)M_PI / 2.0f) {
225                 angle = (float)M_PI - angle;
226         }
227         angle = RAD2DEGF(angle);
228
229         /* For when view is parallel to constraint... will cause NaNs otherwise
230          * So we take vertical motion in 3D space and apply it to the
231          * constraint axis. Nice for camera grab + MMB */
232         if (angle < 5.0f) {
233                 project_v3_v3v3(vec, in, t->viewinv[1]);
234                 factor = dot_v3v3(t->viewinv[1], vec) * 2.0f;
235                 /* since camera distance is quite relative, use quadratic relationship. holding shift can compensate */
236                 if (factor<0.0f) factor*= -factor;
237                 else factor*= factor;
238
239                 copy_v3_v3(out, axis);
240                 normalize_v3(out);
241                 mul_v3_fl(out, -factor);        /* -factor makes move down going backwards */
242         }
243         else {
244                 float v[3], i1[3], i2[3];
245                 float v2[3], v4[3];
246                 float norm_center[3];
247                 float plane[3];
248
249                 getViewVector(t, t_con_center, norm_center);
250                 cross_v3_v3v3(plane, norm_center, axis);
251
252                 project_v3_v3v3(vec, in, plane);
253                 sub_v3_v3v3(vec, in, vec);
254                 
255                 add_v3_v3v3(v, vec, t_con_center);
256                 getViewVector(t, v, norm);
257
258                 /* give arbitrary large value if projection is impossible */
259                 factor = dot_v3v3(axis, norm);
260                 if (1.0f - fabsf(factor) < 0.0002f) {
261                         copy_v3_v3(out, axis);
262                         if (factor > 0) {
263                                 mul_v3_fl(out, 1000000000.0f);
264                         }
265                         else {
266                                 mul_v3_fl(out, -1000000000.0f);
267                         }
268                 }
269                 else {
270                         add_v3_v3v3(v2, t_con_center, axis);
271                         add_v3_v3v3(v4, v, norm);
272                         
273                         isect_line_line_v3(t_con_center, v2, v, v4, i1, i2);
274                         
275                         sub_v3_v3v3(v, i2, v);
276         
277                         sub_v3_v3v3(out, i1, t_con_center);
278
279                         /* possible some values become nan when
280                          * viewpoint and object are both zero */
281                         if (!finite(out[0])) out[0]= 0.0f;
282                         if (!finite(out[1])) out[1]= 0.0f;
283                         if (!finite(out[2])) out[2]= 0.0f;
284                 }
285         }
286 }
287
288 static void planeProjection(TransInfo *t, float in[3], float out[3])
289 {
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 {
554         BLI_strncpy(t->con.text + 1, text, sizeof(t->con.text) - 1);
555         copy_m3_m3(t->con.mtx, space);
556         t->con.mode = mode;
557         getConstraintMatrix(t);
558
559         startConstraint(t);
560
561         t->con.drawExtra = NULL;
562         t->con.applyVec = applyAxisConstraintVec;
563         t->con.applySize = applyAxisConstraintSize;
564         t->con.applyRot = applyAxisConstraintRot;
565         t->redraw = 1;
566 }
567
568 void setLocalConstraint(TransInfo *t, int mode, const char text[])
569 {
570         if (t->flag & T_EDIT) {
571                 float obmat[3][3];
572                 copy_m3_m4(obmat, t->scene->obedit->obmat);
573                 normalize_m3(obmat);
574                 setConstraint(t, obmat, mode, text);
575         }
576         else {
577                 if (t->total == 1) {
578                         setConstraint(t, t->data->axismtx, mode, text);
579                 }
580                 else {
581                         BLI_strncpy(t->con.text + 1, text, sizeof(t->con.text) - 1);
582                         copy_m3_m3(t->con.mtx, t->data->axismtx);
583                         t->con.mode = mode;
584                         getConstraintMatrix(t);
585
586                         startConstraint(t);
587
588                         t->con.drawExtra = drawObjectConstraint;
589                         t->con.applyVec = applyObjectConstraintVec;
590                         t->con.applySize = applyObjectConstraintSize;
591                         t->con.applyRot = applyObjectConstraintRot;
592                         t->redraw = 1;
593                 }
594         }
595 }
596
597 /*
598  * Set the constraint according to the user defined orientation
599  *
600  * ftext is a format string passed to BLI_snprintf. It will add the name of
601  * the orientation where %s is (logically).
602  */
603 void setUserConstraint(TransInfo *t, short orientation, int mode, const char ftext[])
604 {
605         char text[40];
606
607         switch(orientation) {
608         case V3D_MANIP_GLOBAL:
609                 {
610                         float mtx[3][3]= MAT3_UNITY;
611                         BLI_snprintf(text, sizeof(text), ftext, "global");
612                         setConstraint(t, mtx, mode, text);
613                 }
614                 break;
615         case V3D_MANIP_LOCAL:
616                 BLI_snprintf(text, sizeof(text), ftext, "local");
617                 setLocalConstraint(t, mode, text);
618                 break;
619         case V3D_MANIP_NORMAL:
620                 BLI_snprintf(text, sizeof(text), ftext, "normal");
621                 setConstraint(t, t->spacemtx, mode, text);
622                 break;
623         case V3D_MANIP_VIEW:
624                 BLI_snprintf(text, sizeof(text), ftext, "view");
625                 setConstraint(t, t->spacemtx, mode, text);
626                 break;
627         case V3D_MANIP_GIMBAL:
628                 BLI_snprintf(text, sizeof(text), ftext, "gimbal");
629                 setConstraint(t, t->spacemtx, mode, text);
630                 break;
631         default: /* V3D_MANIP_CUSTOM */
632                 BLI_snprintf(text, sizeof(text), ftext, t->spacename);
633                 setConstraint(t, t->spacemtx, mode, text);
634                 break;
635         }
636
637         t->con.orientation = orientation;
638
639         t->con.mode |= CON_USER;
640 }
641
642 /*----------------- DRAWING CONSTRAINTS -------------------*/
643
644 void drawConstraint(TransInfo *t)
645 {
646         TransCon *tc = &(t->con);
647
648         if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE))
649                 return;
650         if (!(tc->mode & CON_APPLY))
651                 return;
652         if (t->flag & T_USES_MANIPULATOR)
653                 return;
654         if (t->flag & T_NO_CONSTRAINT)
655                 return;
656
657         /* nasty exception for Z constraint in camera view */
658         // TRANSFORM_FIX_ME
659 //      if ((t->flag & T_OBJECT) && G.vd->camera==OBACT && G.vd->persp==V3D_CAMOB)
660 //              return;
661
662         if (tc->drawExtra) {
663                 tc->drawExtra(t);
664         }
665         else {
666                 if (tc->mode & CON_SELECT) {
667                         float vec[3];
668                         char col2[3] = {255,255,255};
669                         int depth_test_enabled;
670
671                         convertViewVec(t, vec, (t->mval[0] - t->con.imval[0]), (t->mval[1] - t->con.imval[1]));
672                         add_v3_v3(vec, tc->center);
673
674                         drawLine(t, tc->center, tc->mtx[0], 'X', 0);
675                         drawLine(t, tc->center, tc->mtx[1], 'Y', 0);
676                         drawLine(t, tc->center, tc->mtx[2], 'Z', 0);
677
678                         glColor3ubv((GLubyte *)col2);
679
680                         depth_test_enabled = glIsEnabled(GL_DEPTH_TEST);
681                         if (depth_test_enabled)
682                                 glDisable(GL_DEPTH_TEST);
683
684                         setlinestyle(1);
685                         glBegin(GL_LINE_STRIP);
686                                 glVertex3fv(tc->center);
687                                 glVertex3fv(vec);
688                         glEnd();
689                         setlinestyle(0);
690
691                         if (depth_test_enabled)
692                                 glEnable(GL_DEPTH_TEST);
693                 }
694
695                 if (tc->mode & CON_AXIS0) {
696                         drawLine(t, tc->center, tc->mtx[0], 'X', DRAWLIGHT);
697                 }
698                 if (tc->mode & CON_AXIS1) {
699                         drawLine(t, tc->center, tc->mtx[1], 'Y', DRAWLIGHT);
700                 }
701                 if (tc->mode & CON_AXIS2) {
702                         drawLine(t, tc->center, tc->mtx[2], 'Z', DRAWLIGHT);
703                 }
704         }
705 }
706
707 /* called from drawview.c, as an extra per-window draw option */
708 void drawPropCircle(const struct bContext *C, TransInfo *t)
709 {
710         if (t->flag & T_PROP_EDIT) {
711                 RegionView3D *rv3d = CTX_wm_region_view3d(C);
712                 float tmat[4][4], imat[4][4];
713                 float center[3];
714
715                 UI_ThemeColor(TH_GRID);
716
717                 if (t->spacetype == SPACE_VIEW3D && rv3d != NULL) {
718                         copy_m4_m4(tmat, rv3d->viewmat);
719                         invert_m4_m4(imat, tmat);
720                 }
721                 else {
722                         unit_m4(tmat);
723                         unit_m4(imat);
724                 }
725
726                 glPushMatrix();
727
728                 copy_v3_v3(center, t->center);
729
730                 if ((t->spacetype == SPACE_VIEW3D) && t->obedit) {
731                         mul_m4_v3(t->obedit->obmat, center); /* because t->center is in local space */
732                 }
733                 else if (t->spacetype == SPACE_IMAGE) {
734                         float aspx, aspy;
735
736                         ED_space_image_uv_aspect(t->sa->spacedata.first, &aspx, &aspy);
737                         glScalef(1.0f/aspx, 1.0f/aspy, 1.0);
738                 }
739
740                 set_inverted_drawing(1);
741                 drawcircball(GL_LINE_LOOP, center, t->prop_size, imat);
742                 set_inverted_drawing(0);
743
744                 glPopMatrix();
745         }
746 }
747
748 static void drawObjectConstraint(TransInfo *t)
749 {
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 {
788         t->con.mode |= CON_APPLY;
789         *t->con.text = ' ';
790         t->num.idx_max = MIN2(getConstraintSpaceDimension(t) - 1, t->idx_max);
791 }
792
793 void stopConstraint(TransInfo *t)
794 {
795         t->con.mode &= ~(CON_APPLY|CON_SELECT);
796         *t->con.text = '\0';
797         t->num.idx_max = t->idx_max;
798 }
799
800 void getConstraintMatrix(TransInfo *t)
801 {
802         float mat[3][3];
803         invert_m3_m3(t->con.imtx, t->con.mtx);
804         unit_m3(t->con.pmtx);
805
806         if (!(t->con.mode & CON_AXIS0)) {
807                 t->con.pmtx[0][0]               =
808                         t->con.pmtx[0][1]       =
809                         t->con.pmtx[0][2]       = 0.0f;
810         }
811
812         if (!(t->con.mode & CON_AXIS1)) {
813                 t->con.pmtx[1][0]               =
814                         t->con.pmtx[1][1]       =
815                         t->con.pmtx[1][2]       = 0.0f;
816         }
817
818         if (!(t->con.mode & CON_AXIS2)) {
819                 t->con.pmtx[2][0]               =
820                         t->con.pmtx[2][1]       =
821                         t->con.pmtx[2][2]       = 0.0f;
822         }
823
824         mul_m3_m3m3(mat, t->con.pmtx, t->con.imtx);
825         mul_m3_m3m3(t->con.pmtx, t->con.mtx, mat);
826 }
827
828 /*------------------------- MMB Select -------------------------------*/
829
830 void initSelectConstraint(TransInfo *t, float mtx[3][3])
831 {
832         copy_m3_m3(t->con.mtx, mtx);
833         t->con.mode |= CON_APPLY;
834         t->con.mode |= CON_SELECT;
835
836         setNearestAxis(t);
837         t->con.drawExtra = NULL;
838         t->con.applyVec = applyAxisConstraintVec;
839         t->con.applySize = applyAxisConstraintSize;
840         t->con.applyRot = applyAxisConstraintRot;
841 }
842
843 void selectConstraint(TransInfo *t)
844 {
845         if (t->con.mode & CON_SELECT) {
846                 setNearestAxis(t);
847                 startConstraint(t);
848         }
849 }
850
851 void postSelectConstraint(TransInfo *t)
852 {
853         if (!(t->con.mode & CON_SELECT))
854                 return;
855
856         t->con.mode &= ~CON_AXIS0;
857         t->con.mode &= ~CON_AXIS1;
858         t->con.mode &= ~CON_AXIS2;
859         t->con.mode &= ~CON_SELECT;
860
861         setNearestAxis(t);
862
863         startConstraint(t);
864         t->redraw = 1;
865 }
866
867 static void setNearestAxis2d(TransInfo *t)
868 {
869         /* no correction needed... just use whichever one is lower */
870         if ( abs(t->mval[0]-t->con.imval[0]) < abs(t->mval[1]-t->con.imval[1]) ) {
871                 t->con.mode |= CON_AXIS1;
872                 BLI_snprintf(t->con.text, sizeof(t->con.text), " along Y axis");
873         }
874         else {
875                 t->con.mode |= CON_AXIS0;
876                 BLI_snprintf(t->con.text, sizeof(t->con.text), " along X axis");
877         }
878 }
879
880 static void setNearestAxis3d(TransInfo *t)
881 {
882         float zfac;
883         float mvec[3], axis[3], proj[3];
884         float len[3];
885         int i, icoord[2];
886
887         /* calculate mouse movement */
888         mvec[0] = (float)(t->mval[0] - t->con.imval[0]);
889         mvec[1] = (float)(t->mval[1] - t->con.imval[1]);
890         mvec[2] = 0.0f;
891
892         /* we need to correct axis length for the current zoomlevel of view,
893          * this to prevent projected values to be clipped behind the camera
894          * and to overflow the short integers.
895          * The formula used is a bit stupid, just a simplification of the subtraction
896          * of two 2D points 30 pixels apart (that's the last factor in the formula) after
897          * projecting them with window_to_3d_delta and then get the length of that vector.
898          */
899         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];
900         zfac = len_v3(t->persinv[0]) * 2.0f/t->ar->winx * zfac * 30.0f;
901
902         for (i = 0; i<3; i++) {
903                 copy_v3_v3(axis, t->con.mtx[i]);
904
905                 mul_v3_fl(axis, zfac);
906                 /* now we can project to get window coordinate */
907                 add_v3_v3(axis, t->con.center);
908                 projectIntView(t, axis, icoord);
909
910                 axis[0] = (float)(icoord[0] - t->center2d[0]);
911                 axis[1] = (float)(icoord[1] - t->center2d[1]);
912                 axis[2] = 0.0f;
913
914                 if (normalize_v3(axis) != 0.0f) {
915                         project_v3_v3v3(proj, mvec, axis);
916                         sub_v3_v3v3(axis, mvec, proj);
917                         len[i] = normalize_v3(axis);
918                 }
919                 else {
920                         len[i] = 10000000000.0f;
921                 }
922         }
923
924         if (len[0] <= len[1] && len[0] <= len[2]) {
925                 if (t->modifiers & MOD_CONSTRAINT_PLANE) {
926                         t->con.mode |= (CON_AXIS1|CON_AXIS2);
927                         BLI_snprintf(t->con.text, sizeof(t->con.text), " locking %s X axis", t->spacename);
928                 }
929                 else {
930                         t->con.mode |= CON_AXIS0;
931                         BLI_snprintf(t->con.text, sizeof(t->con.text), " along %s X axis", t->spacename);
932                 }
933         }
934         else if (len[1] <= len[0] && len[1] <= len[2]) {
935                 if (t->modifiers & MOD_CONSTRAINT_PLANE) {
936                         t->con.mode |= (CON_AXIS0|CON_AXIS2);
937                         BLI_snprintf(t->con.text, sizeof(t->con.text), " locking %s Y axis", t->spacename);
938                 }
939                 else {
940                         t->con.mode |= CON_AXIS1;
941                         BLI_snprintf(t->con.text, sizeof(t->con.text), " along %s Y axis", t->spacename);
942                 }
943         }
944         else if (len[2] <= len[1] && len[2] <= len[0]) {
945                 if (t->modifiers & MOD_CONSTRAINT_PLANE) {
946                         t->con.mode |= (CON_AXIS0|CON_AXIS1);
947                         BLI_snprintf(t->con.text, sizeof(t->con.text), " locking %s Z axis", t->spacename);
948                 }
949                 else {
950                         t->con.mode |= CON_AXIS2;
951                         BLI_snprintf(t->con.text, sizeof(t->con.text), " along %s Z axis", t->spacename);
952                 }
953         }
954 }
955
956 void setNearestAxis(TransInfo *t)
957 {
958         /* clear any prior constraint flags */
959         t->con.mode &= ~CON_AXIS0;
960         t->con.mode &= ~CON_AXIS1;
961         t->con.mode &= ~CON_AXIS2;
962
963         /* constraint setting - depends on spacetype */
964         if (t->spacetype == SPACE_VIEW3D) {
965                 /* 3d-view */
966                 setNearestAxis3d(t);
967         }
968         else {
969                 /* assume that this means a 2D-Editor */
970                 setNearestAxis2d(t);
971         }
972
973         getConstraintMatrix(t);
974 }
975
976 /*-------------- HELPER FUNCTIONS ----------------*/
977
978 char constraintModeToChar(TransInfo *t)
979 {
980         if ((t->con.mode & CON_APPLY)==0) {
981                 return '\0';
982         }
983         switch (t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2)) {
984         case (CON_AXIS0):
985         case (CON_AXIS1|CON_AXIS2):
986                 return 'X';
987         case (CON_AXIS1):
988         case (CON_AXIS0|CON_AXIS2):
989                 return 'Y';
990         case (CON_AXIS2):
991         case (CON_AXIS0|CON_AXIS1):
992                 return 'Z';
993         default:
994                 return '\0';
995         }
996 }
997
998
999 int isLockConstraint(TransInfo *t)
1000 {
1001         int mode = t->con.mode;
1002
1003         if ( (mode & (CON_AXIS0|CON_AXIS1)) == (CON_AXIS0|CON_AXIS1))
1004                 return 1;
1005
1006         if ( (mode & (CON_AXIS1|CON_AXIS2)) == (CON_AXIS1|CON_AXIS2))
1007                 return 1;
1008
1009         if ( (mode & (CON_AXIS0|CON_AXIS2)) == (CON_AXIS0|CON_AXIS2))
1010                 return 1;
1011
1012         return 0;
1013 }
1014
1015 /*
1016  * Returns the dimension of the constraint space.
1017  *
1018  * For that reason, the flags always needs to be set to properly evaluate here,
1019  * even if they aren't actually used in the callback function. (Which could happen
1020  * for weird constraints not yet designed. Along a path for example.)
1021  */
1022
1023 int getConstraintSpaceDimension(TransInfo *t)
1024 {
1025         int n = 0;
1026
1027         if (t->con.mode & CON_AXIS0)
1028                 n++;
1029
1030         if (t->con.mode & CON_AXIS1)
1031                 n++;
1032
1033         if (t->con.mode & CON_AXIS2)
1034                 n++;
1035
1036         return n;
1037 /*
1038  * Someone willing to do it cryptically could do the following instead:
1039  *
1040  * return t->con & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
1041  *
1042  * Based on the assumptions that the axis flags are one after the other and start at 1
1043  */
1044 }