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