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