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