Merged 15170:15635 from trunk (no conflicts or even merges)
[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], float *angle)
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 (angle && (mode & CON_NOFLIP) == 0 && hasNumInput(&t->num) == 0) {
417                         if (Inpf(vec, t->viewinv[2]) > 0.0f) {
418                                 *angle = -(*angle);
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], float *angle)
439 {
440         if (t->con.mode & CON_APPLY) {
441                 int mode = t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
442                 
443                 /* on setup call, use first object */
444                 if (td == NULL) {
445                         td= t->data;
446                 }
447
448                 switch(mode) {
449                 case CON_AXIS0:
450                 case (CON_AXIS1|CON_AXIS2):
451                         VECCOPY(vec, td->axismtx[0]);
452                         break;
453                 case CON_AXIS1:
454                 case (CON_AXIS0|CON_AXIS2):
455                         VECCOPY(vec, td->axismtx[1]);
456                         break;
457                 case CON_AXIS2:
458                 case (CON_AXIS0|CON_AXIS1):
459                         VECCOPY(vec, td->axismtx[2]);
460                         break;
461                 }
462                 if (angle && (mode & CON_NOFLIP) == 0 && hasNumInput(&t->num) == 0) {
463                         if (Inpf(vec, t->viewinv[2]) > 0.0f) {
464                                 *angle = -(*angle);
465                         }
466                 }
467         }
468 }
469
470 /*--------------------- INTERNAL SETUP CALLS ------------------*/
471
472 void setConstraint(TransInfo *t, float space[3][3], int mode, const char text[]) {
473         strncpy(t->con.text + 1, text, 48);
474         Mat3CpyMat3(t->con.mtx, space);
475         t->con.mode = mode;
476         getConstraintMatrix(t);
477
478         startConstraint(t);
479
480         t->con.drawExtra = NULL;
481         t->con.applyVec = applyAxisConstraintVec;
482         t->con.applySize = applyAxisConstraintSize;
483         t->con.applyRot = applyAxisConstraintRot;
484         t->redraw = 1;
485 }
486
487 void setLocalConstraint(TransInfo *t, int mode, const char text[]) {
488         if (t->flag & T_EDIT) {
489                 float obmat[3][3];
490                 Mat3CpyMat4(obmat, G.obedit->obmat);
491                 setConstraint(t, obmat, mode|CON_LOCAL, text);
492         }
493         else {
494                 if (t->total == 1) {
495                         setConstraint(t, t->data->axismtx, mode|CON_LOCAL, text);
496                 }
497                 else {
498                         strncpy(t->con.text + 1, text, 48);
499                         Mat3CpyMat3(t->con.mtx, t->data->axismtx);
500                         t->con.mode = mode|CON_LOCAL;
501                         getConstraintMatrix(t);
502
503                         startConstraint(t);
504
505                         t->con.drawExtra = drawObjectConstraint;
506                         t->con.applyVec = applyObjectConstraintVec;
507                         t->con.applySize = applyObjectConstraintSize;
508                         t->con.applyRot = applyObjectConstraintRot;
509                         t->redraw = 1;
510                 }
511         }
512 }
513
514 /*
515         Set the constraint according to the user defined orientation
516
517         ftext is a format string passed to sprintf. It will add the name of
518         the orientation where %s is (logically).
519 */
520 void setUserConstraint(TransInfo *t, int mode, const char ftext[]) {
521         char text[40];
522         short twmode= (t->spacetype==SPACE_VIEW3D)? G.vd->twmode: V3D_MANIP_GLOBAL;
523
524         switch(twmode) {
525         case V3D_MANIP_GLOBAL:
526         /*
527                 sprintf(text, ftext, "global");
528                 Mat3One(mtx);
529                 setConstraint(t, mtx, mode, text);
530                 break;
531         */
532         case V3D_MANIP_LOCAL:
533                 sprintf(text, ftext, "local");
534                 setLocalConstraint(t, mode, text);
535                 break;
536         case V3D_MANIP_NORMAL:
537                 sprintf(text, ftext, "normal");
538                 setConstraint(t, t->spacemtx, mode, text);
539                 break;
540         case V3D_MANIP_VIEW:
541                 sprintf(text, ftext, "view");
542                 setConstraint(t, t->spacemtx, mode, text);
543                 break;
544         default: /* V3D_MANIP_CUSTOM */
545                 sprintf(text, ftext, t->spacename);
546                 setConstraint(t, t->spacemtx, mode, text);
547                 break;
548         }
549
550         t->con.mode |= CON_USER;
551 }
552
553 /*--------------------- EXTERNAL SETUP CALLS ------------------*/
554
555 void BIF_setLocalLockConstraint(char axis, char *text) {
556         TransInfo *t = BIF_GetTransInfo();
557
558         if (t->total == 0) {
559                 return;
560         }
561         
562         switch (axis) {
563         case 'x':
564                 setLocalConstraint(t, (CON_AXIS1|CON_AXIS2), text);
565                 break;
566         case 'y':
567                 setLocalConstraint(t, (CON_AXIS0|CON_AXIS2), text);
568                 break;
569         case 'z':
570                 setLocalConstraint(t, (CON_AXIS0|CON_AXIS1), text);
571                 break;
572         }
573 }
574
575 void BIF_setLocalAxisConstraint(char axis, char *text) {
576         TransInfo *t = BIF_GetTransInfo();
577
578         if (t->total == 0) {
579                 return;
580         }
581         
582         switch (axis) {
583         case 'X':
584                 setLocalConstraint(t, CON_AXIS0, text);
585                 break;
586         case 'Y':
587                 setLocalConstraint(t, CON_AXIS1, text);
588                 break;
589         case 'Z':
590                 setLocalConstraint(t, CON_AXIS2, text);
591                 break;
592         }
593 }
594
595 /* text is optional, for header print */
596 void BIF_setSingleAxisConstraint(float vec[3], char *text) {
597         TransInfo *t = BIF_GetTransInfo();
598         float space[3][3], v[3];
599         
600         if (t->total == 0) {
601                 return;
602         }
603         
604         VECCOPY(space[0], vec);
605
606         v[0] = vec[2];
607         v[1] = vec[0];
608         v[2] = vec[1];
609
610         Crossf(space[1], vec, v);
611         Crossf(space[2], vec, space[1]);
612         Mat3Ortho(space);
613
614         Mat3CpyMat3(t->con.mtx, space);
615         t->con.mode = CON_AXIS0;
616         
617         getConstraintMatrix(t);
618
619         startConstraint(t);
620         
621         /* start copying with an offset of 1, to reserve a spot for the SPACE char */
622         if(text)
623         {
624                 strncpy(t->con.text+1, text, 48);       /* 50 in struct */
625         }
626         else
627         {
628                 t->con.text[1] = '\0'; /* No text */
629         }
630         
631         t->con.drawExtra = NULL;
632         t->con.applyVec = applyAxisConstraintVec;
633         t->con.applySize = applyAxisConstraintSize;
634         t->con.applyRot = applyAxisConstraintRot;
635         t->redraw = 1;
636 }
637
638 void BIF_setDualAxisConstraint(float vec1[3], float vec2[3], char *text) {
639         TransInfo *t = BIF_GetTransInfo();
640         float space[3][3];
641         
642         if (t->total == 0) {
643                 return;
644         }
645
646         VECCOPY(space[0], vec1);
647         VECCOPY(space[1], vec2);
648         Crossf(space[2], space[0], space[1]);
649         Mat3Ortho(space);
650         
651         Mat3CpyMat3(t->con.mtx, space);
652         t->con.mode = CON_AXIS0|CON_AXIS1;
653
654         getConstraintMatrix(t);
655
656         startConstraint(t);
657         
658         /* start copying with an offset of 1, to reserve a spot for the SPACE char */
659         if(text)
660         {
661                 strncpy(t->con.text+1, text, 48);       /* 50 in struct */
662         }
663         else
664         {
665                 t->con.text[1] = '\0'; /* No text */
666         }
667
668         t->con.drawExtra = NULL;
669         t->con.applyVec = applyAxisConstraintVec;
670         t->con.applySize = applyAxisConstraintSize;
671         t->con.applyRot = applyAxisConstraintRot;
672         t->redraw = 1;
673 }
674
675 /*----------------- DRAWING CONSTRAINTS -------------------*/
676
677 void BIF_drawConstraint(void)
678 {
679         TransInfo *t = BIF_GetTransInfo();
680         TransCon *tc = &(t->con);
681
682         if (t->spacetype!=SPACE_VIEW3D)
683                 return;
684         if (!(tc->mode & CON_APPLY))
685                 return;
686         if (t->flag & T_USES_MANIPULATOR)
687                 return;
688         
689         /* nasty exception for Z constraint in camera view */
690         if((t->flag & T_OBJECT) && G.vd->camera==OBACT && G.vd->persp==V3D_CAMOB) 
691                 return;
692
693         if (tc->drawExtra) {
694                 tc->drawExtra(t);
695         }
696         else {
697                 if (tc->mode & CON_SELECT) {
698                         float vec[3];
699                         short mval[2];
700                         char col2[3] = {255,255,255};
701                         getmouseco_areawin(mval);
702                         convertViewVec(t, vec, (short)(mval[0] - t->con.imval[0]), (short)(mval[1] - t->con.imval[1]));
703                         VecAddf(vec, vec, tc->center);
704
705                         drawLine(tc->center, tc->mtx[0], 'x', 0);
706                         drawLine(tc->center, tc->mtx[1], 'y', 0);
707                         drawLine(tc->center, tc->mtx[2], 'z', 0);
708
709                         glColor3ubv((GLubyte *)col2);
710                         
711                         glDisable(GL_DEPTH_TEST);
712                         setlinestyle(1);
713                         glBegin(GL_LINE_STRIP); 
714                                 glVertex3fv(tc->center); 
715                                 glVertex3fv(vec); 
716                         glEnd();
717                         setlinestyle(0);
718                         if(G.vd->zbuf) glEnable(GL_DEPTH_TEST); 
719                 }
720
721                 if (tc->mode & CON_AXIS0) {
722                         drawLine(tc->center, tc->mtx[0], 'x', DRAWLIGHT);
723                 }
724                 if (tc->mode & CON_AXIS1) {
725                         drawLine(tc->center, tc->mtx[1], 'y', DRAWLIGHT);
726                 }
727                 if (tc->mode & CON_AXIS2) {
728                         drawLine(tc->center, tc->mtx[2], 'z', DRAWLIGHT);
729                 }
730         }
731 }
732
733 /* called from drawview.c, as an extra per-window draw option */
734 void BIF_drawPropCircle()
735 {
736         TransInfo *t = BIF_GetTransInfo();
737
738         if (t->flag & T_PROP_EDIT) {
739                 float tmat[4][4], imat[4][4];
740
741                 BIF_ThemeColor(TH_GRID);
742                 
743                 /* if editmode we need to go into object space */
744                 if(G.obedit && t->spacetype == SPACE_VIEW3D)
745                         mymultmatrix(G.obedit->obmat);
746                 
747                 mygetmatrix(tmat);
748                 Mat4Invert(imat, tmat);
749
750                 set_inverted_drawing(1);
751                 drawcircball(GL_LINE_LOOP, t->center, t->propsize, imat);
752                 set_inverted_drawing(0);
753                 
754                 /* if editmode we restore */
755                 if(G.obedit && t->spacetype == SPACE_VIEW3D)
756                         myloadmatrix(G.vd->viewmat);
757         }
758 }
759
760 void BIF_getPropCenter(float *center)
761 {
762         TransInfo *t = BIF_GetTransInfo();
763
764         if (t && t->flag & T_PROP_EDIT) {
765                 VECCOPY(center, t->center);
766         }
767         else
768                 center[0] = center[1] = center[2] = 0.0f;
769 }
770 static void drawObjectConstraint(TransInfo *t) {
771         int i;
772         TransData * td = t->data;
773
774         /* Draw the first one lighter because that's the one who controls the others.
775            Meaning the transformation is projected on that one and just copied on the others
776            constraint space.
777            In a nutshell, the object with light axis is controlled by the user and the others follow.
778            Without drawing the first light, users have little clue what they are doing.
779          */
780         if (t->con.mode & CON_AXIS0) {
781                 drawLine(td->ob->obmat[3], td->axismtx[0], 'x', DRAWLIGHT);
782         }
783         if (t->con.mode & CON_AXIS1) {
784                 drawLine(td->ob->obmat[3], td->axismtx[1], 'y', DRAWLIGHT);
785         }
786         if (t->con.mode & CON_AXIS2) {
787                 drawLine(td->ob->obmat[3], td->axismtx[2], 'z', DRAWLIGHT);
788         }
789         
790         td++;
791
792         for(i=1;i<t->total;i++,td++) {
793                 if (t->con.mode & CON_AXIS0) {
794                         drawLine(td->ob->obmat[3], td->axismtx[0], 'x', 0);
795                 }
796                 if (t->con.mode & CON_AXIS1) {
797                         drawLine(td->ob->obmat[3], td->axismtx[1], 'y', 0);
798                 }
799                 if (t->con.mode & CON_AXIS2) {
800                         drawLine(td->ob->obmat[3], td->axismtx[2], 'z', 0);
801                 }
802         }
803 }
804
805 /*--------------------- START / STOP CONSTRAINTS ---------------------- */
806
807 void startConstraint(TransInfo *t) {
808         t->con.mode |= CON_APPLY;
809         *t->con.text = ' ';
810         t->num.idx_max = MIN2(getConstraintSpaceDimension(t) - 1, t->idx_max);
811 }
812
813 void stopConstraint(TransInfo *t) {
814         t->con.mode &= ~(CON_APPLY|CON_SELECT);
815         *t->con.text = '\0';
816         t->num.idx_max = t->idx_max;
817 }
818
819 void getConstraintMatrix(TransInfo *t)
820 {
821         float mat[3][3];
822         Mat3Inv(t->con.imtx, t->con.mtx);
823         Mat3One(t->con.pmtx);
824
825         if (!(t->con.mode & CON_AXIS0)) {
826                 t->con.pmtx[0][0]               =
827                         t->con.pmtx[0][1]       =
828                         t->con.pmtx[0][2]       = 0.0f;
829         }
830
831         if (!(t->con.mode & CON_AXIS1)) {
832                 t->con.pmtx[1][0]               =
833                         t->con.pmtx[1][1]       =
834                         t->con.pmtx[1][2]       = 0.0f;
835         }
836
837         if (!(t->con.mode & CON_AXIS2)) {
838                 t->con.pmtx[2][0]               =
839                         t->con.pmtx[2][1]       =
840                         t->con.pmtx[2][2]       = 0.0f;
841         }
842
843         Mat3MulMat3(mat, t->con.pmtx, t->con.imtx);
844         Mat3MulMat3(t->con.pmtx, t->con.mtx, mat);
845 }
846
847 /*------------------------- MMB Select -------------------------------*/
848
849 void initSelectConstraint(TransInfo *t, float mtx[3][3])
850 {
851         Mat3CpyMat3(t->con.mtx, mtx);
852         t->con.mode |= CON_APPLY;
853         t->con.mode |= CON_SELECT;
854         t->con.mode &= ~CON_LOCAL;
855
856         setNearestAxis(t);
857         t->con.drawExtra = NULL;
858         t->con.applyVec = applyAxisConstraintVec;
859         t->con.applySize = applyAxisConstraintSize;
860         t->con.applyRot = applyAxisConstraintRot;
861 }
862
863 void selectConstraint(TransInfo *t) {
864         if (t->con.mode & CON_SELECT) {
865                 setNearestAxis(t);
866                 startConstraint(t);
867         }
868 }
869
870 void postSelectConstraint(TransInfo *t)
871 {
872         if (!(t->con.mode & CON_SELECT))
873                 return;
874
875         t->con.mode &= ~CON_AXIS0;
876         t->con.mode &= ~CON_AXIS1;
877         t->con.mode &= ~CON_AXIS2;
878         t->con.mode &= ~CON_SELECT;
879
880         setNearestAxis(t);
881
882         startConstraint(t);
883         t->redraw = 1;
884 }
885
886 static void setNearestAxis2d(TransInfo *t)
887 {
888         short mval[2];
889         short ival[2];
890         
891         getmouseco_areawin(mval);
892         ival[0]= t->imval[0];
893         ival[1]= t->imval[1];
894         
895         /* no correction needed... just use whichever one is lower */
896         if ( abs(mval[0]-ival[0]) < abs(mval[1]-ival[1]) ) {
897                 t->con.mode |= CON_AXIS1;
898                 sprintf(t->con.text, " along Y axis");
899         }
900         else {
901                 t->con.mode |= CON_AXIS0;
902                 sprintf(t->con.text, " along X axis");
903         }
904 }
905
906 static void setNearestAxis3d(TransInfo *t)
907 {
908         float zfac;
909         float mvec[3], axis[3], proj[3];
910         float len[3];
911         int i, icoord[2];
912         short coord[2];
913         
914         /* calculate mouse movement */
915         getmouseco_areawin(coord);
916         mvec[0] = (float)(coord[0] - t->con.imval[0]);
917         mvec[1] = (float)(coord[1] - t->con.imval[1]);
918         mvec[2] = 0.0f;
919         
920         /* we need to correct axis length for the current zoomlevel of view,
921            this to prevent projected values to be clipped behind the camera
922            and to overflow the short integers.
923            The formula used is a bit stupid, just a simplification of the substraction
924            of two 2D points 30 pixels apart (that's the last factor in the formula) after
925            projecting them with window_to_3d and then get the length of that vector.
926         */
927         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];
928         zfac = VecLength(t->persinv[0]) * 2.0f/curarea->winx * zfac * 30.0f;
929
930         for (i = 0; i<3; i++) {
931                 VECCOPY(axis, t->con.mtx[i]);
932                 
933                 VecMulf(axis, zfac);
934                 /* now we can project to get window coordinate */
935                 VecAddf(axis, axis, t->con.center);
936                 projectIntView(t, axis, icoord);
937                 
938                 axis[0] = (float)(icoord[0] - t->center2d[0]);
939                 axis[1] = (float)(icoord[1] - t->center2d[1]);
940                 axis[2] = 0.0f;
941
942                 if (Normalize(axis) != 0.0f) {
943                         Projf(proj, mvec, axis);
944                         VecSubf(axis, mvec, proj);
945                         len[i] = Normalize(axis);
946                 }
947                 else {
948                         len[i] = 10000000000.0f;
949                 }
950         }
951
952         if (len[0] <= len[1] && len[0] <= len[2]) {
953                 if (G.qual & LR_SHIFTKEY) {
954                         t->con.mode |= (CON_AXIS1|CON_AXIS2);
955                         sprintf(t->con.text, " locking %s X axis", t->spacename);
956                 }
957                 else {
958                         t->con.mode |= CON_AXIS0;
959                         sprintf(t->con.text, " along %s X axis", t->spacename);
960                 }
961         }
962         else if (len[1] <= len[0] && len[1] <= len[2]) {
963                 if (G.qual & LR_SHIFTKEY) {
964                         t->con.mode |= (CON_AXIS0|CON_AXIS2);
965                         sprintf(t->con.text, " locking %s Y axis", t->spacename);
966                 }
967                 else {
968                         t->con.mode |= CON_AXIS1;
969                         sprintf(t->con.text, " along %s Y axis", t->spacename);
970                 }
971         }
972         else if (len[2] <= len[1] && len[2] <= len[0]) {
973                 if (G.qual & LR_SHIFTKEY) {
974                         t->con.mode |= (CON_AXIS0|CON_AXIS1);
975                         sprintf(t->con.text, " locking %s Z axis", t->spacename);
976                 }
977                 else {
978                         t->con.mode |= CON_AXIS2;
979                         sprintf(t->con.text, " along %s Z axis", t->spacename);
980                 }
981         }
982 }
983
984 void setNearestAxis(TransInfo *t)
985 {
986         /* clear any prior constraint flags */
987         t->con.mode &= ~CON_AXIS0;
988         t->con.mode &= ~CON_AXIS1;
989         t->con.mode &= ~CON_AXIS2;
990
991         /* constraint setting - depends on spacetype */
992         if (t->spacetype == SPACE_VIEW3D) {
993                 /* 3d-view */
994                 setNearestAxis3d(t);    
995         }
996         else {
997                 /* assume that this means a 2D-Editor */
998                 setNearestAxis2d(t);
999         }
1000         
1001         getConstraintMatrix(t);
1002 }
1003
1004 /*-------------- HELPER FUNCTIONS ----------------*/
1005
1006 char constraintModeToChar(TransInfo *t) {
1007         if ((t->con.mode & CON_APPLY)==0) {
1008                 return '\0';
1009         }
1010         switch (t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2)) {
1011         case (CON_AXIS0):
1012         case (CON_AXIS1|CON_AXIS2):
1013                 return 'X';
1014         case (CON_AXIS1):
1015         case (CON_AXIS0|CON_AXIS2):
1016                 return 'Y';
1017         case (CON_AXIS2):
1018         case (CON_AXIS0|CON_AXIS1):
1019                 return 'Z';
1020         default:
1021                 return '\0';
1022         }
1023 }
1024
1025
1026 int isLockConstraint(TransInfo *t) {
1027         int mode = t->con.mode;
1028
1029         if ( (mode & (CON_AXIS0|CON_AXIS1)) == (CON_AXIS0|CON_AXIS1))
1030                 return 1;
1031
1032         if ( (mode & (CON_AXIS1|CON_AXIS2)) == (CON_AXIS1|CON_AXIS2))
1033                 return 1;
1034
1035         if ( (mode & (CON_AXIS0|CON_AXIS2)) == (CON_AXIS0|CON_AXIS2))
1036                 return 1;
1037
1038         return 0;
1039 }
1040
1041 /*
1042  * Returns the dimension of the constraint space.
1043  * 
1044  * For that reason, the flags always needs to be set to properly evaluate here,
1045  * even if they aren't actually used in the callback function. (Which could happen
1046  * for weird constraints not yet designed. Along a path for example.)
1047  */
1048
1049 int getConstraintSpaceDimension(TransInfo *t)
1050 {
1051         int n = 0;
1052
1053         if (t->con.mode & CON_AXIS0)
1054                 n++;
1055
1056         if (t->con.mode & CON_AXIS1)
1057                 n++;
1058
1059         if (t->con.mode & CON_AXIS2)
1060                 n++;
1061
1062         return n;
1063 /*
1064   Someone willing to do it criptically could do the following instead:
1065
1066   return t->con & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
1067         
1068   Based on the assumptions that the axis flags are one after the other and start at 1
1069 */
1070 }