86e10793f06ff7d8135cd5a2c340d145ce55dbf0
[blender.git] / source / blender / src / transform_constraints.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL 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. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  */
32
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 #ifndef WIN32
42 #include <unistd.h>
43 #else
44 #include <io.h>
45 #endif
46
47 #include "MEM_guardedalloc.h"
48
49 #include "DNA_action_types.h"
50 #include "DNA_armature_types.h"
51 #include "DNA_camera_types.h"
52 #include "DNA_curve_types.h"
53 #include "DNA_effect_types.h"
54 #include "DNA_image_types.h"
55 #include "DNA_ipo_types.h"
56 #include "DNA_key_types.h"
57 #include "DNA_lamp_types.h"
58 #include "DNA_lattice_types.h"
59 #include "DNA_mesh_types.h"
60 #include "DNA_meshdata_types.h"
61 #include "DNA_meta_types.h"
62 #include "DNA_object_types.h"
63 #include "DNA_scene_types.h"
64 #include "DNA_screen_types.h"
65 #include "DNA_view3d_types.h"
66
67 #include "BIF_screen.h"
68 #include "BIF_resources.h"
69 #include "BIF_mywindow.h"
70 #include "BIF_gl.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], n[3], n2[3], vec[3], factor;
168
169         /* For when view is parallel to constraint... will cause NaNs otherwise
170            So we take vertical motion in 3D space and apply it to the
171            constraint axis. Nice for camera grab + MMB */
172         if(1.0f - Inpf(axis, t->viewinv[2]) < 0.000001f) {
173                 Projf(vec, in, t->viewinv[1]);
174                 factor = Inpf(t->viewinv[1], vec) * 2.0f;
175                 /* since camera distance is quite relative, use quadratic relationship. holding shift can compensate */
176                 if(factor<0.0f) factor*= -factor;
177                 else factor*= factor;
178                 
179                 VECCOPY(out, axis);
180                 Normalise(out);
181                 VecMulf(out, -factor);  /* -factor makes move down going backwards */
182         }
183         else {
184                 // prevent division by zero, happens on constrainting without initial delta transform */
185                 if(in[0]!=0.0f || in[1]!=0.0f || in[2]!=0.0f) {
186                         /* project axis on viewplane            */
187                         Projf(vec, axis, t->viewinv[2]);
188                         VecSubf(vec, axis, vec);
189                         /* project input on the new axis        */
190                         Projf(vec, in, vec);
191                         /* get view vector on that point (account for perspective)      */
192                         VecAddf(vec, vec, t->con.center);
193                         getViewVector(vec, norm);
194
195                         /* cross product twice to get a full space definition */
196                         Crossf(n, axis, norm);
197                         Crossf(n2, norm, n);
198
199                         /* Project input on plane perpendicular to the axis (as drawn on screen) */
200                         Projf(vec, in, n2);
201
202                         /* Adjust output */
203                         factor = Inpf(axis, vec);
204                         if (factor == 0.0f) return; /* prevent divide by zero */
205                         factor = Inpf(vec, vec) / factor;
206
207                         VecMulf(axis, factor);
208                         VECCOPY(out, axis);
209                 }
210         }
211 }
212
213 static void planeProjection(TransInfo *t, float in[3], float out[3]) {
214         float vec[3], factor, norm[3];
215
216         VecAddf(vec, in, t->con.center);
217         getViewVector(vec, norm);
218
219         VecSubf(vec, out, in);
220
221         factor = Inpf(vec, norm);
222         if (factor == 0.0f) return; /* prevent divide by zero */
223         factor = Inpf(vec, vec) / factor;
224
225         VECCOPY(vec, norm);
226         VecMulf(vec, factor);
227
228         VecAddf(out, in, vec);
229 }
230
231 /*
232  * Generic callback for constant spacial constraints applied to linear motion
233  * 
234  * The IN vector in projected into the constrained space and then further
235  * projected along the view vector.
236  * (in perspective mode, the view vector is relative to the position on screen)
237  *
238  */
239
240 static void applyAxisConstraintVec(TransInfo *t, TransData *td, float in[3], float out[3], float pvec[3])
241 {
242         VECCOPY(out, in);
243         if (!td && t->con.mode & CON_APPLY) {
244                 Mat3MulVecfl(t->con.pmtx, out);
245                 if (getConstraintSpaceDimension(t) == 2) {
246                         if (out[0] != 0.0f || out[1] != 0.0f || out[2] != 0.0f) {
247                                 planeProjection(t, in, out);
248                         }
249                 }
250                 else if (getConstraintSpaceDimension(t) == 1) {
251                         float c[3];
252
253                         if (t->con.mode & CON_AXIS0) {
254                                 VECCOPY(c, t->con.mtx[0]);
255                         }
256                         else if (t->con.mode & CON_AXIS1) {
257                                 VECCOPY(c, t->con.mtx[1]);
258                         }
259                         else if (t->con.mode & CON_AXIS2) {
260                                 VECCOPY(c, t->con.mtx[2]);
261                         }
262                         axisProjection(t, c, in, out);
263                 }
264                 postConstraintChecks(t, out, pvec);
265         }
266 }
267
268 /*
269  * Generic callback for object based spacial constraints applied to linear motion
270  * 
271  * At first, the following is applied to the first data in the array
272  * The IN vector in projected into the constrained space and then further
273  * projected along the view vector.
274  * (in perspective mode, the view vector is relative to the position on screen)
275  *
276  * Further down, that vector is mapped to each data's space.
277  */
278
279 static void applyObjectConstraintVec(TransInfo *t, TransData *td, float in[3], float out[3], float pvec[3])
280 {
281         VECCOPY(out, in);
282         if (t->con.mode & CON_APPLY) {
283                 if (!td) {
284                         Mat3MulVecfl(t->con.pmtx, out);
285                         if (getConstraintSpaceDimension(t) == 2) {
286                                 if (out[0] != 0.0f || out[1] != 0.0f || out[2] != 0.0f) {
287                                         planeProjection(t, in, out);
288                                 }
289                         }
290                         else if (getConstraintSpaceDimension(t) == 1) {
291                                 float c[3];
292
293                                 if (t->con.mode & CON_AXIS0) {
294                                         VECCOPY(c, t->con.mtx[0]);
295                                 }
296                                 else if (t->con.mode & CON_AXIS1) {
297                                         VECCOPY(c, t->con.mtx[1]);
298                                 }
299                                 else if (t->con.mode & CON_AXIS2) {
300                                         VECCOPY(c, t->con.mtx[2]);
301                                 }
302                                 axisProjection(t, c, in, out);
303                         }
304                         postConstraintChecks(t, out, pvec);
305                         VECCOPY(out, pvec);
306                 }
307                 else {
308                         int i=0;
309
310                         out[0] = out[1] = out[2] = 0.0f;
311                         if (t->con.mode & CON_AXIS0) {
312                                 out[0] = in[i++];
313                         }
314                         if (t->con.mode & CON_AXIS1) {
315                                 out[1] = in[i++];
316                         }
317                         if (t->con.mode & CON_AXIS2) {
318                                 out[2] = in[i++];
319                         }
320                         Mat3MulVecfl(td->axismtx, out);
321                 }
322         }
323 }
324
325 /*
326  * Generic callback for constant spacial constraints applied to resize motion
327  * 
328  *
329  */
330
331 static void applyAxisConstraintSize(TransInfo *t, TransData *td, float smat[3][3])
332 {
333         if (!td && t->con.mode & CON_APPLY) {
334                 float tmat[3][3];
335
336                 if (!(t->con.mode & CON_AXIS0)) {
337                         smat[0][0] = 1.0f;
338                 }
339                 if (!(t->con.mode & CON_AXIS1)) {
340                         smat[1][1] = 1.0f;
341                 }
342                 if (!(t->con.mode & CON_AXIS2)) {
343                         smat[2][2] = 1.0f;
344                 }
345
346                 Mat3MulMat3(tmat, smat, t->con.imtx);
347                 Mat3MulMat3(smat, t->con.mtx, tmat);
348         }
349 }
350
351 /*
352  * Callback for object based spacial constraints applied to resize motion
353  * 
354  *
355  */
356
357 static void applyObjectConstraintSize(TransInfo *t, TransData *td, float smat[3][3])
358 {
359         if (td && t->con.mode & CON_APPLY) {
360                 float tmat[3][3];
361                 float imat[3][3];
362
363                 Mat3Inv(imat, td->axismtx);
364
365                 if (!(t->con.mode & CON_AXIS0)) {
366                         smat[0][0] = 1.0f;
367                 }
368                 if (!(t->con.mode & CON_AXIS1)) {
369                         smat[1][1] = 1.0f;
370                 }
371                 if (!(t->con.mode & CON_AXIS2)) {
372                         smat[2][2] = 1.0f;
373                 }
374
375                 Mat3MulMat3(tmat, smat, imat);
376                 Mat3MulMat3(smat, td->axismtx, tmat);
377         }
378 }
379
380 /*
381  * Generic callback for constant spacial constraints applied to rotations
382  * 
383  * The rotation axis is copied into VEC.
384  *
385  * In the case of single axis constraints, the rotation axis is directly the one constrained to.
386  * For planar constraints (2 axis), the rotation axis is the normal of the plane.
387  *
388  * The following only applies when CON_NOFLIP is not set.
389  * The vector is then modified to always point away from the screen (in global space)
390  * This insures that the rotation is always logically following the mouse.
391  * (ie: not doing counterclockwise rotations when the mouse moves clockwise).
392  */
393
394 static void applyAxisConstraintRot(TransInfo *t, TransData *td, float vec[3])
395 {
396         if (!td && t->con.mode & CON_APPLY) {
397                 int mode = t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
398
399                 switch(mode) {
400                 case CON_AXIS0:
401                 case (CON_AXIS1|CON_AXIS2):
402                         VECCOPY(vec, t->con.mtx[0]);
403                         break;
404                 case CON_AXIS1:
405                 case (CON_AXIS0|CON_AXIS2):
406                         VECCOPY(vec, t->con.mtx[1]);
407                         break;
408                 case CON_AXIS2:
409                 case (CON_AXIS0|CON_AXIS1):
410                         VECCOPY(vec, t->con.mtx[2]);
411                         break;
412                 }
413                 if (!(mode & CON_NOFLIP)) {
414                         if (Inpf(vec, t->viewinv[2]) > 0.0f) {
415                                 VecMulf(vec, -1.0f);
416                         }
417                 }
418         }
419 }
420
421 /*
422  * Callback for object based spacial constraints applied to rotations
423  * 
424  * The rotation axis is copied into VEC.
425  *
426  * In the case of single axis constraints, the rotation axis is directly the one constrained to.
427  * For planar constraints (2 axis), the rotation axis is the normal of the plane.
428  *
429  * The following only applies when CON_NOFLIP is not set.
430  * The vector is then modified to always point away from the screen (in global space)
431  * This insures that the rotation is always logically following the mouse.
432  * (ie: not doing counterclockwise rotations when the mouse moves clockwise).
433  */
434
435 static void applyObjectConstraintRot(TransInfo *t, TransData *td, float vec[3])
436 {
437         if (td && t->con.mode & CON_APPLY) {
438                 int mode = t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
439
440                 switch(mode) {
441                 case CON_AXIS0:
442                 case (CON_AXIS1|CON_AXIS2):
443                         VECCOPY(vec, td->axismtx[0]);
444                         break;
445                 case CON_AXIS1:
446                 case (CON_AXIS0|CON_AXIS2):
447                         VECCOPY(vec, td->axismtx[1]);
448                         break;
449                 case CON_AXIS2:
450                 case (CON_AXIS0|CON_AXIS1):
451                         VECCOPY(vec, td->axismtx[2]);
452                         break;
453                 }
454                 if (!(mode & CON_NOFLIP)) {
455                         if (Inpf(vec, t->viewinv[2]) > 0.0f) {
456                                 VecMulf(vec, -1.0f);
457                         }
458                 }
459         }
460 }
461
462 /*--------------------- INTERNAL SETUP CALLS ------------------*/
463
464 void setConstraint(TransInfo *t, float space[3][3], int mode, const char text[]) {
465         strncpy(t->con.text + 1, text, 48);
466         Mat3CpyMat3(t->con.mtx, space);
467         t->con.mode = mode;
468         getConstraintMatrix(t);
469
470         startConstraint(t);
471
472         t->con.drawExtra = NULL;
473         t->con.applyVec = applyAxisConstraintVec;
474         t->con.applySize = applyAxisConstraintSize;
475         t->con.applyRot = applyAxisConstraintRot;
476         t->redraw = 1;
477 }
478
479 void setLocalConstraint(TransInfo *t, int mode, const char text[]) {
480         if (t->flag & T_EDIT) {
481                 float obmat[3][3];
482                 Mat3CpyMat4(obmat, G.obedit->obmat);
483                 setConstraint(t, obmat, mode|CON_LOCAL, text);
484         }
485         else {
486                 if (t->total == 1) {
487                         setConstraint(t, t->data->axismtx, mode|CON_LOCAL, text);
488                 }
489                 else {
490                         strncpy(t->con.text + 1, text, 48);
491                         Mat3CpyMat3(t->con.mtx, t->data->axismtx);
492                         t->con.mode = mode|CON_LOCAL;
493                         getConstraintMatrix(t);
494
495                         startConstraint(t);
496
497                         t->con.drawExtra = drawObjectConstraint;
498                         t->con.applyVec = applyObjectConstraintVec;
499                         t->con.applySize = applyObjectConstraintSize;
500                         t->con.applyRot = applyObjectConstraintRot;
501                         t->redraw = 1;
502                 }
503         }
504 }
505
506 /*
507         Set the constraint according to the user defined orientation
508
509         ftext is a format string passed to sprintf. It will add the name of
510         the orientation where %s is (logically).
511 */
512 void setUserConstraint(TransInfo *t, int mode, const char ftext[]) {
513         float mtx[3][3];
514         char text[40];
515
516         switch(G.vd->twmode) {
517         case V3D_MANIP_GLOBAL:
518                 sprintf(text, ftext, "global");
519                 Mat3One(mtx);
520                 setConstraint(t, mtx, mode, text);
521                 break;
522         case V3D_MANIP_LOCAL:
523                 sprintf(text, ftext, "local");
524                 setLocalConstraint(t, mode, text);
525                 break;
526         case V3D_MANIP_NORMAL:
527                 sprintf(text, ftext, "normal");
528                 setConstraint(t, t->spacemtx, mode, text);
529                 break;
530         case V3D_MANIP_VIEW:
531                 sprintf(text, ftext, "view");
532                 setConstraint(t, t->spacemtx, mode, text);
533                 break;
534         }
535
536         t->con.mode |= CON_USER;
537 }
538
539 /*--------------------- EXTERNAL SETUP CALLS ------------------*/
540
541 void BIF_setLocalLockConstraint(char axis, char *text) {
542         TransInfo *t = BIF_GetTransInfo();
543
544         switch (axis) {
545         case 'x':
546                 setLocalConstraint(t, (CON_AXIS1|CON_AXIS2), text);
547                 break;
548         case 'y':
549                 setLocalConstraint(t, (CON_AXIS0|CON_AXIS2), text);
550                 break;
551         case 'z':
552                 setLocalConstraint(t, (CON_AXIS0|CON_AXIS1), text);
553                 break;
554         }
555 }
556
557 void BIF_setLocalAxisConstraint(char axis, char *text) {
558         TransInfo *t = BIF_GetTransInfo();
559
560         switch (axis) {
561         case 'X':
562                 setLocalConstraint(t, CON_AXIS0, text);
563                 break;
564         case 'Y':
565                 setLocalConstraint(t, CON_AXIS1, text);
566                 break;
567         case 'Z':
568                 setLocalConstraint(t, CON_AXIS2, text);
569                 break;
570         }
571 }
572
573 /* text is optional, for header print */
574 void BIF_setSingleAxisConstraint(float vec[3], char *text) {
575         TransInfo *t = BIF_GetTransInfo();
576         float space[3][3], v[3];
577         
578         VECCOPY(space[0], vec);
579
580         v[0] = vec[2];
581         v[1] = vec[0];
582         v[2] = vec[1];
583
584         Crossf(space[1], vec, v);
585         Crossf(space[2], vec, space[1]);
586         Mat3Ortho(space);
587
588         Mat3CpyMat3(t->con.mtx, space);
589         t->con.mode = (CON_AXIS0|CON_APPLY);
590         getConstraintMatrix(t);
591
592         /* start copying with an offset of 1, to reserve a spot for the SPACE char */
593         if(text) strncpy(t->con.text+1, text, 48);      // 50 in struct
594
595         
596         t->con.drawExtra = NULL;
597         t->con.applyVec = applyAxisConstraintVec;
598         t->con.applySize = applyAxisConstraintSize;
599         t->con.applyRot = applyAxisConstraintRot;
600         t->redraw = 1;
601 }
602
603 void BIF_setDualAxisConstraint(float vec1[3], float vec2[3], char *text) {
604         TransInfo *t = BIF_GetTransInfo();
605         float space[3][3];
606         
607         VECCOPY(space[0], vec1);
608         VECCOPY(space[1], vec2);
609         Crossf(space[2], space[0], space[1]);
610         Mat3Ortho(space);
611         
612         Mat3CpyMat3(t->con.mtx, space);
613         t->con.mode = (CON_AXIS0|CON_AXIS1|CON_APPLY);
614         getConstraintMatrix(t);
615         
616         /* start copying with an offset of 1, to reserve a spot for the SPACE char */
617         if(text) strncpy(t->con.text+1, text, 48);      // 50 in struct
618
619         t->con.drawExtra = NULL;
620         t->con.applyVec = applyAxisConstraintVec;
621         t->con.applySize = applyAxisConstraintSize;
622         t->con.applyRot = applyAxisConstraintRot;
623         t->redraw = 1;
624 }
625
626 /*----------------- DRAWING CONSTRAINTS -------------------*/
627
628 void BIF_drawConstraint(void)
629 {
630         TransInfo *t = BIF_GetTransInfo();
631         TransCon *tc = &(t->con);
632
633         if (!(tc->mode & CON_APPLY))
634                 return;
635         if (t->flag & T_USES_MANIPULATOR)
636                 return;
637         
638         /* nasty exception for Z constraint in camera view */
639         if( (t->flag & T_OBJECT) && G.vd->camera==OBACT && G.vd->persp>1) 
640                 return;
641
642         if (tc->drawExtra) {
643                 tc->drawExtra(t);
644         }
645         else {
646                 if (tc->mode & CON_SELECT) {
647                         float vec[3];
648                         short mval[2];
649                         char col2[3] = {255,255,255};
650                         getmouseco_areawin(mval);
651                         window_to_3d(vec, (short)(mval[0] - t->con.imval[0]), (short)(mval[1] - t->con.imval[1]));
652                         VecAddf(vec, vec, tc->center);
653
654                         drawLine(tc->center, tc->mtx[0], 'x', 0);
655                         drawLine(tc->center, tc->mtx[1], 'y', 0);
656                         drawLine(tc->center, tc->mtx[2], 'z', 0);
657
658                         glColor3ubv(col2);
659                         
660                         glDisable(GL_DEPTH_TEST);
661                         setlinestyle(1);
662                         glBegin(GL_LINE_STRIP); 
663                                 glVertex3fv(tc->center); 
664                                 glVertex3fv(vec); 
665                         glEnd();
666                         setlinestyle(0);
667                         if(G.vd->zbuf) glEnable(GL_DEPTH_TEST); 
668                 }
669
670                 if (tc->mode & CON_AXIS0) {
671                         drawLine(tc->center, tc->mtx[0], 'x', DRAWLIGHT);
672                 }
673                 if (tc->mode & CON_AXIS1) {
674                         drawLine(tc->center, tc->mtx[1], 'y', DRAWLIGHT);
675                 }
676                 if (tc->mode & CON_AXIS2) {
677                         drawLine(tc->center, tc->mtx[2], 'z', DRAWLIGHT);
678                 }
679         }
680 }
681
682 /* called from drawview.c, as an extra per-window draw option */
683 void BIF_drawPropCircle()
684 {
685         TransInfo *t = BIF_GetTransInfo();
686
687         if (t->flag & T_PROP_EDIT) {
688                 float tmat[4][4], imat[4][4];
689
690                 BIF_ThemeColor(TH_GRID);
691                 
692                 /* if editmode we need to go into object space */
693                 if(G.obedit) mymultmatrix(G.obedit->obmat);
694                 
695                 mygetmatrix(tmat);
696                 Mat4Invert(imat, tmat);
697                 
698                 drawcircball(GL_LINE_LOOP, t->center, t->propsize, imat);
699                 
700                 /* if editmode we restore */
701                 if(G.obedit) myloadmatrix(G.vd->viewmat);
702         }
703 }
704
705
706 static void drawObjectConstraint(TransInfo *t) {
707         int i;
708         TransData * td = t->data;
709
710         /* Draw the first one lighter because that's the one who controls the others.
711            Meaning the transformation is projected on that one and just copied on the others
712            constraint space.
713            In a nutshell, the object with light axis is controlled by the user and the others follow.
714            Without drawing the first light, users have little clue what they are doing.
715          */
716         if (t->con.mode & CON_AXIS0) {
717                 drawLine(td->ob->obmat[3], td->axismtx[0], 'x', DRAWLIGHT);
718         }
719         if (t->con.mode & CON_AXIS1) {
720                 drawLine(td->ob->obmat[3], td->axismtx[1], 'y', DRAWLIGHT);
721         }
722         if (t->con.mode & CON_AXIS2) {
723                 drawLine(td->ob->obmat[3], td->axismtx[2], 'z', DRAWLIGHT);
724         }
725         
726         td++;
727
728         for(i=1;i<t->total;i++,td++) {
729                 if (t->con.mode & CON_AXIS0) {
730                         drawLine(td->ob->obmat[3], td->axismtx[0], 'x', 0);
731                 }
732                 if (t->con.mode & CON_AXIS1) {
733                         drawLine(td->ob->obmat[3], td->axismtx[1], 'y', 0);
734                 }
735                 if (t->con.mode & CON_AXIS2) {
736                         drawLine(td->ob->obmat[3], td->axismtx[2], 'z', 0);
737                 }
738         }
739 }
740
741 /*--------------------- START / STOP CONSTRAINTS ---------------------- */
742
743 void startConstraint(TransInfo *t) {
744         t->con.mode |= CON_APPLY;
745         *t->con.text = ' ';
746         t->num.idx_max = MIN2(getConstraintSpaceDimension(t) - 1, t->idx_max);
747 }
748
749 void stopConstraint(TransInfo *t) {
750         t->con.mode &= ~(CON_APPLY|CON_SELECT);
751         *t->con.text = '\0';
752         t->num.idx_max = t->idx_max;
753 }
754
755 void getConstraintMatrix(TransInfo *t)
756 {
757         float mat[3][3];
758         Mat3Inv(t->con.imtx, t->con.mtx);
759         Mat3One(t->con.pmtx);
760
761         if (!(t->con.mode & CON_AXIS0)) {
762                 t->con.pmtx[0][0]               =
763                         t->con.pmtx[0][1]       =
764                         t->con.pmtx[0][2]       = 0.0f;
765         }
766
767         if (!(t->con.mode & CON_AXIS1)) {
768                 t->con.pmtx[1][0]               =
769                         t->con.pmtx[1][1]       =
770                         t->con.pmtx[1][2]       = 0.0f;
771         }
772
773         if (!(t->con.mode & CON_AXIS2)) {
774                 t->con.pmtx[2][0]               =
775                         t->con.pmtx[2][1]       =
776                         t->con.pmtx[2][2]       = 0.0f;
777         }
778
779         Mat3MulMat3(mat, t->con.pmtx, t->con.imtx);
780         Mat3MulMat3(t->con.pmtx, t->con.mtx, mat);
781 }
782
783 /*------------------------- MMB Select -------------------------------*/
784
785 void initSelectConstraint(TransInfo *t, float mtx[3][3])
786 {
787         Mat3CpyMat3(t->con.mtx, mtx);
788         t->con.mode |= CON_APPLY;
789         t->con.mode |= CON_SELECT;
790         t->con.mode &= ~CON_LOCAL;
791
792         setNearestAxis(t);
793         t->con.drawExtra = NULL;
794         t->con.applyVec = applyAxisConstraintVec;
795         t->con.applySize = applyAxisConstraintSize;
796         t->con.applyRot = applyAxisConstraintRot;
797 }
798
799 void selectConstraint(TransInfo *t) {
800         if (t->con.mode & CON_SELECT) {
801                 setNearestAxis(t);
802                 startConstraint(t);
803         }
804 }
805
806 void postSelectConstraint(TransInfo *t)
807 {
808         if (!(t->con.mode & CON_SELECT))
809                 return;
810
811         t->con.mode &= ~CON_AXIS0;
812         t->con.mode &= ~CON_AXIS1;
813         t->con.mode &= ~CON_AXIS2;
814         t->con.mode &= ~CON_SELECT;
815
816         setNearestAxis(t);
817
818         startConstraint(t);
819         t->redraw = 1;
820 }
821
822 void setNearestAxis(TransInfo *t)
823 {
824         float zfac;
825         float mvec[3], axis[3], proj[3];
826         float len[3];
827         int i, icoord[2];
828         short coord[2];
829
830         t->con.mode &= ~CON_AXIS0;
831         t->con.mode &= ~CON_AXIS1;
832         t->con.mode &= ~CON_AXIS2;
833
834         getmouseco_areawin(coord);
835         mvec[0] = (float)(coord[0] - t->con.imval[0]);
836         mvec[1] = (float)(coord[1] - t->con.imval[1]);
837         mvec[2] = 0.0f;
838
839                 
840         /* we need to correct axis length for the current zoomlevel of view,
841            this to prevent projected values to be clipped behind the camera
842            and to overflow the short integers.
843            The formula used is a bit stupid, just a simplification of the substraction
844            of two 2D points 30 pixels apart (that's the last factor in the formula) after
845            projecting them with window_to_3d and then get the length of that vector.
846         */
847         zfac= G.vd->persmat[0][3]*t->center[0]+ G.vd->persmat[1][3]*t->center[1]+ G.vd->persmat[2][3]*t->center[2]+ G.vd->persmat[3][3];
848         zfac = VecLength(G.vd->persinv[0]) * 2.0f/curarea->winx * zfac * 30.0f;
849
850         for (i = 0; i<3; i++) {
851                 VECCOPY(axis, t->con.mtx[i]);
852                 
853                 VecMulf(axis, zfac);
854                 /* now we can project to get window coordinate */
855                 VecAddf(axis, axis, t->con.center);
856                 project_int(axis, icoord);
857                 
858                 axis[0] = (float)(icoord[0] - t->center2d[0]);
859                 axis[1] = (float)(icoord[1] - t->center2d[1]);
860                 axis[2] = 0.0f;
861
862                 if (Normalise(axis) != 0.0f) {
863                         Projf(proj, mvec, axis);
864                         VecSubf(axis, mvec, proj);
865                         len[i] = Normalise(axis);
866                 }
867                 else {
868                         len[i] = 10000000000.0f;
869                 }
870         }
871
872         if (len[0] <= len[1] && len[0] <= len[2]) {
873                 if (G.qual & LR_SHIFTKEY) {
874                         t->con.mode |= (CON_AXIS1|CON_AXIS2);
875                         sprintf(t->con.text, " locking %s X axis", t->spacename);
876                 }
877                 else {
878                         t->con.mode |= CON_AXIS0;
879                         sprintf(t->con.text, " along %s X axis", t->spacename);
880                 }
881         }
882         else if (len[1] <= len[0] && len[1] <= len[2]) {
883                 if (G.qual & LR_SHIFTKEY) {
884                         t->con.mode |= (CON_AXIS0|CON_AXIS2);
885                         sprintf(t->con.text, " locking %s Y axis", t->spacename);
886                 }
887                 else {
888                         t->con.mode |= CON_AXIS1;
889                         sprintf(t->con.text, " along %s Y axis", t->spacename);
890                 }
891         }
892         else if (len[2] <= len[1] && len[2] <= len[0]) {
893                 if (G.qual & LR_SHIFTKEY) {
894                         t->con.mode |= (CON_AXIS0|CON_AXIS1);
895                         sprintf(t->con.text, " locking %s Z axis", t->spacename);
896                 }
897                 else {
898                         t->con.mode |= CON_AXIS2;
899                         sprintf(t->con.text, " along %s Z axis", t->spacename);
900                 }
901         }
902         getConstraintMatrix(t);
903 }
904
905 /*-------------- HELPER FUNCTIONS ----------------*/
906
907 char constraintModeToChar(TransInfo *t) {
908         if ((t->con.mode & CON_APPLY)==0) {
909                 return '\0';
910         }
911         switch (t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2)) {
912         case (CON_AXIS0):
913         case (CON_AXIS1|CON_AXIS2):
914                 return 'X';
915         case (CON_AXIS1):
916         case (CON_AXIS0|CON_AXIS2):
917                 return 'Y';
918         case (CON_AXIS2):
919         case (CON_AXIS0|CON_AXIS1):
920                 return 'Z';
921         default:
922                 return '\0';
923         }
924 }
925
926
927 int isLockConstraint(TransInfo *t) {
928         int mode = t->con.mode;
929
930         if ( (mode & (CON_AXIS0|CON_AXIS1)) == (CON_AXIS0|CON_AXIS1))
931                 return 1;
932
933         if ( (mode & (CON_AXIS1|CON_AXIS2)) == (CON_AXIS1|CON_AXIS2))
934                 return 1;
935
936         if ( (mode & (CON_AXIS0|CON_AXIS2)) == (CON_AXIS0|CON_AXIS2))
937                 return 1;
938
939         return 0;
940 }
941
942 /*
943  * Returns the dimension of the constraint space.
944  * 
945  * For that reason, the flags always needs to be set to properly evaluate here,
946  * even if they aren't actually used in the callback function. (Which could happen
947  * for weird constraints not yet designed. Along a path for example.)
948  */
949
950 int getConstraintSpaceDimension(TransInfo *t)
951 {
952         int n = 0;
953
954         if (t->con.mode & CON_AXIS0)
955                 n++;
956
957         if (t->con.mode & CON_AXIS1)
958                 n++;
959
960         if (t->con.mode & CON_AXIS2)
961                 n++;
962
963         return n;
964 /*
965   Someone willing to do it criptically could do the following instead:
966
967   return t->con & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
968         
969   Based on the assumptions that the axis flags are one after the other and start at 1
970 */
971 }