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