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