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