converted more mixed tab/space indentations to tabs. only whitespace changes.
[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 /** \file blender/editors/transform/transform_constraints.c
31  *  \ingroup edtransform
32  */
33
34
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <math.h>
39
40 #ifndef WIN32
41 #include <unistd.h>
42 #else
43 #include <io.h>
44 #endif
45
46
47 #include "DNA_object_types.h"
48 #include "DNA_scene_types.h"
49 #include "DNA_screen_types.h"
50 #include "DNA_space_types.h"
51 #include "DNA_view3d_types.h"
52
53 #include "BIF_gl.h"
54 #include "BIF_glutil.h"
55
56 #include "BKE_context.h"
57
58
59 #include "ED_image.h"
60 #include "ED_view3d.h"
61
62 #include "BLI_math.h"
63 #include "BLI_utildefines.h"
64
65 //#include "blendef.h"
66 //
67 //#include "mydevice.h"
68
69 #include "UI_resources.h"
70
71
72 #include "transform.h"
73
74 static void drawObjectConstraint(TransInfo *t);
75
76 /* ************************** CONSTRAINTS ************************* */
77 static void constraintAutoValues(TransInfo *t, float vec[3])
78 {
79         int mode = t->con.mode;
80         if (mode & CON_APPLY)
81         {
82                 float nval = (t->flag & T_NULL_ONE)?1.0f:0.0f;
83
84                 if ((mode & CON_AXIS0) == 0)
85                 {
86                         vec[0] = nval;
87                 }
88                 if ((mode & CON_AXIS1) == 0)
89                 {
90                         vec[1] = nval;
91                 }
92                 if ((mode & CON_AXIS2) == 0)
93                 {
94                         vec[2] = nval;
95                 }
96         }
97 }
98
99 void constraintNumInput(TransInfo *t, float vec[3])
100 {
101         int mode = t->con.mode;
102         if (mode & CON_APPLY) {
103                 float nval = (t->flag & T_NULL_ONE)?1.0f:0.0f;
104
105                 if (getConstraintSpaceDimension(t) == 2) {
106                         int axis = mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
107                         if (axis == (CON_AXIS0|CON_AXIS1)) {
108                                 vec[0] = vec[0];
109                                 vec[1] = vec[1];
110                                 vec[2] = nval;
111                         }
112                         else if (axis == (CON_AXIS1|CON_AXIS2)) {
113                                 vec[2] = vec[1];
114                                 vec[1] = vec[0];
115                                 vec[0] = nval;
116                         }
117                         else if (axis == (CON_AXIS0|CON_AXIS2)) {
118                                 vec[0] = vec[0];
119                                 vec[2] = vec[1];
120                                 vec[1] = nval;
121                         }
122                 }
123                 else if (getConstraintSpaceDimension(t) == 1) {
124                         if (mode & CON_AXIS0) {
125                                 vec[0] = vec[0];
126                                 vec[1] = nval;
127                                 vec[2] = nval;
128                         }
129                         else if (mode & CON_AXIS1) {
130                                 vec[1] = vec[0];
131                                 vec[0] = nval;
132                                 vec[2] = nval;
133                         }
134                         else if (mode & CON_AXIS2) {
135                                 vec[2] = vec[0];
136                                 vec[0] = nval;
137                                 vec[1] = nval;
138                         }
139                 }
140         }
141 }
142
143 static void postConstraintChecks(TransInfo *t, float vec[3], float pvec[3]) {
144         int i = 0;
145
146         mul_m3_v3(t->con.imtx, vec);
147
148         snapGrid(t, vec);
149
150         if (t->num.flag & T_NULL_ONE) {
151                 if (!(t->con.mode & CON_AXIS0))
152                         vec[0] = 1.0f;
153
154                 if (!(t->con.mode & CON_AXIS1))
155                         vec[1] = 1.0f;
156
157                 if (!(t->con.mode & CON_AXIS2))
158                         vec[2] = 1.0f;
159         }
160
161         if (hasNumInput(&t->num)) {
162                 applyNumInput(&t->num, vec);
163                 removeAspectRatio(t, vec);
164                 constraintNumInput(t, vec);
165         }
166
167         /* autovalues is operator param, use that directly but not if snapping is forced */
168         if (t->flag & T_AUTOVALUES && (t->tsnap.status & SNAP_FORCED) == 0)
169         {
170                 mul_v3_m3v3(vec, t->con.imtx, t->auto_values);
171                 constraintAutoValues(t, vec);
172                 /* inverse transformation at the end */
173         }
174
175         if (t->con.mode & CON_AXIS0) {
176                 pvec[i++] = vec[0];
177         }
178         if (t->con.mode & CON_AXIS1) {
179                 pvec[i++] = vec[1];
180         }
181         if (t->con.mode & CON_AXIS2) {
182                 pvec[i++] = vec[2];
183         }
184
185         mul_m3_v3(t->con.mtx, vec);
186 }
187
188 static void axisProjection(TransInfo *t, float axis[3], float in[3], float out[3]) {
189         float norm[3], vec[3], factor, angle;
190
191         if(in[0]==0.0f && in[1]==0.0f && in[2]==0.0f)
192                 return;
193
194         angle = fabsf(angle_v3v3(axis, t->viewinv[2]));
195         if (angle > (float)M_PI / 2.0f) {
196                 angle = (float)M_PI - angle;
197         }
198         angle = RAD2DEGF(angle);
199
200         /* For when view is parallel to constraint... will cause NaNs otherwise
201            So we take vertical motion in 3D space and apply it to the
202            constraint axis. Nice for camera grab + MMB */
203         if(angle < 5.0f) {
204                 project_v3_v3v3(vec, in, t->viewinv[1]);
205                 factor = dot_v3v3(t->viewinv[1], vec) * 2.0f;
206                 /* since camera distance is quite relative, use quadratic relationship. holding shift can compensate */
207                 if(factor<0.0f) factor*= -factor;
208                 else factor*= factor;
209
210                 VECCOPY(out, axis);
211                 normalize_v3(out);
212                 mul_v3_fl(out, -factor);        /* -factor makes move down going backwards */
213         }
214         else {
215                 float v[3], i1[3], i2[3];
216                 float v2[3], v4[3];
217                 float norm_center[3];
218                 float plane[3];
219
220                 getViewVector(t, t->con.center, norm_center);
221                 cross_v3_v3v3(plane, norm_center, axis);
222
223                 project_v3_v3v3(vec, in, plane);
224                 sub_v3_v3v3(vec, in, vec);
225                 
226                 add_v3_v3v3(v, vec, t->con.center);
227                 getViewVector(t, v, norm);
228
229                 /* give arbitrary large value if projection is impossible */
230                 factor = dot_v3v3(axis, norm);
231                 if (1.0f - fabsf(factor) < 0.0002f) {
232                         VECCOPY(out, axis);
233                         if (factor > 0) {
234                                 mul_v3_fl(out, 1000000000.0f);
235                         } else {
236                                 mul_v3_fl(out, -1000000000.0f);
237                         }
238                 } else {
239                         add_v3_v3v3(v2, t->con.center, axis);
240                         add_v3_v3v3(v4, v, norm);
241                         
242                         isect_line_line_v3(t->con.center, v2, v, v4, i1, i2);
243                         
244                         sub_v3_v3v3(v, i2, v);
245         
246                         sub_v3_v3v3(out, i1,  t->con.center);
247
248                         /* possible some values become nan when
249                          * viewpoint and object are both zero */
250                         if(!finite(out[0])) out[0]= 0.0f;
251                         if(!finite(out[1])) out[1]= 0.0f;
252                         if(!finite(out[2])) out[2]= 0.0f;
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                 normalize_m3(obmat);
540                 setConstraint(t, obmat, mode, text);
541         }
542         else {
543                 if (t->total == 1) {
544                         setConstraint(t, t->data->axismtx, mode, text);
545                 }
546                 else {
547                         strncpy(t->con.text + 1, text, 48);
548                         copy_m3_m3(t->con.mtx, t->data->axismtx);
549                         t->con.mode = mode;
550                         getConstraintMatrix(t);
551
552                         startConstraint(t);
553
554                         t->con.drawExtra = drawObjectConstraint;
555                         t->con.applyVec = applyObjectConstraintVec;
556                         t->con.applySize = applyObjectConstraintSize;
557                         t->con.applyRot = applyObjectConstraintRot;
558                         t->redraw = 1;
559                 }
560         }
561 }
562
563 /*
564         Set the constraint according to the user defined orientation
565
566         ftext is a format string passed to sprintf. It will add the name of
567         the orientation where %s is (logically).
568 */
569 void setUserConstraint(TransInfo *t, short orientation, int mode, const char ftext[]) {
570         char text[40];
571
572         switch(orientation) {
573         case V3D_MANIP_GLOBAL:
574                 {
575                         float mtx[3][3]= MAT3_UNITY;
576                         sprintf(text, ftext, "global");
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(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                         int depth_test_enabled;
635
636                         convertViewVec(t, vec, (short)(t->mval[0] - t->con.imval[0]), (short)(t->mval[1] - t->con.imval[1]));
637                         add_v3_v3(vec, tc->center);
638
639                         drawLine(t, tc->center, tc->mtx[0], 'X', 0);
640                         drawLine(t, tc->center, tc->mtx[1], 'Y', 0);
641                         drawLine(t, tc->center, tc->mtx[2], 'Z', 0);
642
643                         glColor3ubv((GLubyte *)col2);
644
645                         depth_test_enabled = glIsEnabled(GL_DEPTH_TEST);
646                         if(depth_test_enabled)
647                                 glDisable(GL_DEPTH_TEST);
648
649                         setlinestyle(1);
650                         glBegin(GL_LINE_STRIP);
651                                 glVertex3fv(tc->center);
652                                 glVertex3fv(vec);
653                         glEnd();
654                         setlinestyle(0);
655
656                         if(depth_test_enabled)
657                                 glEnable(GL_DEPTH_TEST);
658                 }
659
660                 if (tc->mode & CON_AXIS0) {
661                         drawLine(t, tc->center, tc->mtx[0], 'X', DRAWLIGHT);
662                 }
663                 if (tc->mode & CON_AXIS1) {
664                         drawLine(t, tc->center, tc->mtx[1], 'Y', DRAWLIGHT);
665                 }
666                 if (tc->mode & CON_AXIS2) {
667                         drawLine(t, tc->center, tc->mtx[2], 'Z', DRAWLIGHT);
668                 }
669         }
670 }
671
672 /* called from drawview.c, as an extra per-window draw option */
673 void drawPropCircle(const struct bContext *C, TransInfo *t)
674 {
675         if (t->flag & T_PROP_EDIT) {
676                 RegionView3D *rv3d = CTX_wm_region_view3d(C);
677                 float tmat[4][4], imat[4][4];
678                 float center[3];
679
680                 UI_ThemeColor(TH_GRID);
681
682                 if(t->spacetype == SPACE_VIEW3D && rv3d != NULL)
683                 {
684                         copy_m4_m4(tmat, rv3d->viewmat);
685                         invert_m4_m4(imat, tmat);
686                 }
687                 else
688                 {
689                         unit_m4(tmat);
690                         unit_m4(imat);
691                 }
692
693                 glPushMatrix();
694
695                 VECCOPY(center, t->center);
696
697                 if((t->spacetype == SPACE_VIEW3D) && t->obedit)
698                 {
699                         mul_m4_v3(t->obedit->obmat, center); /* because t->center is in local space */
700                 }
701                 else if(t->spacetype == SPACE_IMAGE)
702                 {
703                         float aspx, aspy;
704
705                         ED_space_image_uv_aspect(t->sa->spacedata.first, &aspx, &aspy);
706                         glScalef(1.0f/aspx, 1.0f/aspy, 1.0);
707                 }
708
709                 set_inverted_drawing(1);
710                 drawcircball(GL_LINE_LOOP, center, t->prop_size, imat);
711                 set_inverted_drawing(0);
712
713                 glPopMatrix();
714         }
715 }
716
717 static void drawObjectConstraint(TransInfo *t) {
718         int i;
719         TransData * td = t->data;
720
721         /* Draw the first one lighter because that's the one who controls the others.
722            Meaning the transformation is projected on that one and just copied on the others
723            constraint space.
724            In a nutshell, the object with light axis is controlled by the user and the others follow.
725            Without drawing the first light, users have little clue what they are doing.
726          */
727         if (t->con.mode & CON_AXIS0) {
728                 drawLine(t, td->ob->obmat[3], td->axismtx[0], 'X', DRAWLIGHT);
729         }
730         if (t->con.mode & CON_AXIS1) {
731                 drawLine(t, td->ob->obmat[3], td->axismtx[1], 'Y', DRAWLIGHT);
732         }
733         if (t->con.mode & CON_AXIS2) {
734                 drawLine(t, td->ob->obmat[3], td->axismtx[2], 'Z', DRAWLIGHT);
735         }
736
737         td++;
738
739         for(i=1;i<t->total;i++,td++) {
740                 if (t->con.mode & CON_AXIS0) {
741                         drawLine(t, td->ob->obmat[3], td->axismtx[0], 'X', 0);
742                 }
743                 if (t->con.mode & CON_AXIS1) {
744                         drawLine(t, td->ob->obmat[3], td->axismtx[1], 'Y', 0);
745                 }
746                 if (t->con.mode & CON_AXIS2) {
747                         drawLine(t, td->ob->obmat[3], td->axismtx[2], 'Z', 0);
748                 }
749         }
750 }
751
752 /*--------------------- START / STOP CONSTRAINTS ---------------------- */
753
754 void startConstraint(TransInfo *t) {
755         t->con.mode |= CON_APPLY;
756         *t->con.text = ' ';
757         t->num.idx_max = MIN2(getConstraintSpaceDimension(t) - 1, t->idx_max);
758 }
759
760 void stopConstraint(TransInfo *t) {
761         t->con.mode &= ~(CON_APPLY|CON_SELECT);
762         *t->con.text = '\0';
763         t->num.idx_max = t->idx_max;
764 }
765
766 void getConstraintMatrix(TransInfo *t)
767 {
768         float mat[3][3];
769         invert_m3_m3(t->con.imtx, t->con.mtx);
770         unit_m3(t->con.pmtx);
771
772         if (!(t->con.mode & CON_AXIS0)) {
773                 t->con.pmtx[0][0]               =
774                         t->con.pmtx[0][1]       =
775                         t->con.pmtx[0][2]       = 0.0f;
776         }
777
778         if (!(t->con.mode & CON_AXIS1)) {
779                 t->con.pmtx[1][0]               =
780                         t->con.pmtx[1][1]       =
781                         t->con.pmtx[1][2]       = 0.0f;
782         }
783
784         if (!(t->con.mode & CON_AXIS2)) {
785                 t->con.pmtx[2][0]               =
786                         t->con.pmtx[2][1]       =
787                         t->con.pmtx[2][2]       = 0.0f;
788         }
789
790         mul_m3_m3m3(mat, t->con.pmtx, t->con.imtx);
791         mul_m3_m3m3(t->con.pmtx, t->con.mtx, mat);
792 }
793
794 /*------------------------- MMB Select -------------------------------*/
795
796 void initSelectConstraint(TransInfo *t, float mtx[3][3])
797 {
798         copy_m3_m3(t->con.mtx, mtx);
799         t->con.mode |= CON_APPLY;
800         t->con.mode |= CON_SELECT;
801
802         setNearestAxis(t);
803         t->con.drawExtra = NULL;
804         t->con.applyVec = applyAxisConstraintVec;
805         t->con.applySize = applyAxisConstraintSize;
806         t->con.applyRot = applyAxisConstraintRot;
807 }
808
809 void selectConstraint(TransInfo *t) {
810         if (t->con.mode & CON_SELECT) {
811                 setNearestAxis(t);
812                 startConstraint(t);
813         }
814 }
815
816 void postSelectConstraint(TransInfo *t)
817 {
818         if (!(t->con.mode & CON_SELECT))
819                 return;
820
821         t->con.mode &= ~CON_AXIS0;
822         t->con.mode &= ~CON_AXIS1;
823         t->con.mode &= ~CON_AXIS2;
824         t->con.mode &= ~CON_SELECT;
825
826         setNearestAxis(t);
827
828         startConstraint(t);
829         t->redraw = 1;
830 }
831
832 static void setNearestAxis2d(TransInfo *t)
833 {
834         /* no correction needed... just use whichever one is lower */
835         if ( abs(t->mval[0]-t->con.imval[0]) < abs(t->mval[1]-t->con.imval[1]) ) {
836                 t->con.mode |= CON_AXIS1;
837                 sprintf(t->con.text, " along Y axis");
838         }
839         else {
840                 t->con.mode |= CON_AXIS0;
841                 sprintf(t->con.text, " along X axis");
842         }
843 }
844
845 static void setNearestAxis3d(TransInfo *t)
846 {
847         float zfac;
848         float mvec[3], axis[3], proj[3];
849         float len[3];
850         int i, icoord[2];
851
852         /* calculate mouse movement */
853         mvec[0] = (float)(t->mval[0] - t->con.imval[0]);
854         mvec[1] = (float)(t->mval[1] - t->con.imval[1]);
855         mvec[2] = 0.0f;
856
857         /* we need to correct axis length for the current zoomlevel of view,
858            this to prevent projected values to be clipped behind the camera
859            and to overflow the short integers.
860            The formula used is a bit stupid, just a simplification of the substraction
861            of two 2D points 30 pixels apart (that's the last factor in the formula) after
862            projecting them with window_to_3d_delta and then get the length of that vector.
863         */
864         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];
865         zfac = len_v3(t->persinv[0]) * 2.0f/t->ar->winx * zfac * 30.0f;
866
867         for (i = 0; i<3; i++) {
868                 VECCOPY(axis, t->con.mtx[i]);
869
870                 mul_v3_fl(axis, zfac);
871                 /* now we can project to get window coordinate */
872                 add_v3_v3(axis, t->con.center);
873                 projectIntView(t, axis, icoord);
874
875                 axis[0] = (float)(icoord[0] - t->center2d[0]);
876                 axis[1] = (float)(icoord[1] - t->center2d[1]);
877                 axis[2] = 0.0f;
878
879                 if (normalize_v3(axis) != 0.0f) {
880                         project_v3_v3v3(proj, mvec, axis);
881                         sub_v3_v3v3(axis, mvec, proj);
882                         len[i] = normalize_v3(axis);
883                 }
884                 else {
885                         len[i] = 10000000000.0f;
886                 }
887         }
888
889         if (len[0] <= len[1] && len[0] <= len[2]) {
890                 if (t->modifiers & MOD_CONSTRAINT_PLANE) {
891                         t->con.mode |= (CON_AXIS1|CON_AXIS2);
892                         sprintf(t->con.text, " locking %s X axis", t->spacename);
893                 }
894                 else {
895                         t->con.mode |= CON_AXIS0;
896                         sprintf(t->con.text, " along %s X axis", t->spacename);
897                 }
898         }
899         else if (len[1] <= len[0] && len[1] <= len[2]) {
900                 if (t->modifiers & MOD_CONSTRAINT_PLANE) {
901                         t->con.mode |= (CON_AXIS0|CON_AXIS2);
902                         sprintf(t->con.text, " locking %s Y axis", t->spacename);
903                 }
904                 else {
905                         t->con.mode |= CON_AXIS1;
906                         sprintf(t->con.text, " along %s Y axis", t->spacename);
907                 }
908         }
909         else if (len[2] <= len[1] && len[2] <= len[0]) {
910                 if (t->modifiers & MOD_CONSTRAINT_PLANE) {
911                         t->con.mode |= (CON_AXIS0|CON_AXIS1);
912                         sprintf(t->con.text, " locking %s Z axis", t->spacename);
913                 }
914                 else {
915                         t->con.mode |= CON_AXIS2;
916                         sprintf(t->con.text, " along %s Z axis", t->spacename);
917                 }
918         }
919 }
920
921 void setNearestAxis(TransInfo *t)
922 {
923         /* clear any prior constraint flags */
924         t->con.mode &= ~CON_AXIS0;
925         t->con.mode &= ~CON_AXIS1;
926         t->con.mode &= ~CON_AXIS2;
927
928         /* constraint setting - depends on spacetype */
929         if (t->spacetype == SPACE_VIEW3D) {
930                 /* 3d-view */
931                 setNearestAxis3d(t);
932         }
933         else {
934                 /* assume that this means a 2D-Editor */
935                 setNearestAxis2d(t);
936         }
937
938         getConstraintMatrix(t);
939 }
940
941 /*-------------- HELPER FUNCTIONS ----------------*/
942
943 char constraintModeToChar(TransInfo *t) {
944         if ((t->con.mode & CON_APPLY)==0) {
945                 return '\0';
946         }
947         switch (t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2)) {
948         case (CON_AXIS0):
949         case (CON_AXIS1|CON_AXIS2):
950                 return 'X';
951         case (CON_AXIS1):
952         case (CON_AXIS0|CON_AXIS2):
953                 return 'Y';
954         case (CON_AXIS2):
955         case (CON_AXIS0|CON_AXIS1):
956                 return 'Z';
957         default:
958                 return '\0';
959         }
960 }
961
962
963 int isLockConstraint(TransInfo *t) {
964         int mode = t->con.mode;
965
966         if ( (mode & (CON_AXIS0|CON_AXIS1)) == (CON_AXIS0|CON_AXIS1))
967                 return 1;
968
969         if ( (mode & (CON_AXIS1|CON_AXIS2)) == (CON_AXIS1|CON_AXIS2))
970                 return 1;
971
972         if ( (mode & (CON_AXIS0|CON_AXIS2)) == (CON_AXIS0|CON_AXIS2))
973                 return 1;
974
975         return 0;
976 }
977
978 /*
979  * Returns the dimension of the constraint space.
980  *
981  * For that reason, the flags always needs to be set to properly evaluate here,
982  * even if they aren't actually used in the callback function. (Which could happen
983  * for weird constraints not yet designed. Along a path for example.)
984  */
985
986 int getConstraintSpaceDimension(TransInfo *t)
987 {
988         int n = 0;
989
990         if (t->con.mode & CON_AXIS0)
991                 n++;
992
993         if (t->con.mode & CON_AXIS1)
994                 n++;
995
996         if (t->con.mode & CON_AXIS2)
997                 n++;
998
999         return n;
1000 /*
1001   Someone willing to do it criptically could do the following instead:
1002
1003   return t->con & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
1004
1005   Based on the assumptions that the axis flags are one after the other and start at 1
1006 */
1007 }