Fixes for some long standing transform bugs.
[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 #include <math.h>
37
38 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif
41
42 #ifndef WIN32
43 #include <unistd.h>
44 #else
45 #include <io.h>
46 #endif
47
48 #include "MEM_guardedalloc.h"
49
50 #include "DNA_action_types.h"
51 #include "DNA_armature_types.h"
52 #include "DNA_camera_types.h"
53 #include "DNA_curve_types.h"
54 #include "DNA_effect_types.h"
55 #include "DNA_image_types.h"
56 #include "DNA_ipo_types.h"
57 #include "DNA_key_types.h"
58 #include "DNA_lamp_types.h"
59 #include "DNA_lattice_types.h"
60 #include "DNA_mesh_types.h"
61 #include "DNA_meshdata_types.h"
62 #include "DNA_meta_types.h"
63 #include "DNA_object_types.h"
64 #include "DNA_scene_types.h"
65 #include "DNA_screen_types.h"
66 #include "DNA_space_types.h"
67 #include "DNA_view3d_types.h"
68
69 #include "BIF_screen.h"
70 #include "BIF_resources.h"
71 #include "BIF_mywindow.h"
72 #include "BIF_gl.h"
73 #include "BIF_glutil.h"
74
75 #include "BKE_global.h"
76 #include "BKE_utildefines.h"
77
78 #include "BSE_view.h"
79
80 #include "BLI_arithb.h"
81
82 #include "BDR_drawobject.h"     /* drawcircball */
83
84 #include "blendef.h"
85
86 #include "mydevice.h"
87
88 #include "transform.h"
89
90 static void drawObjectConstraint(TransInfo *t);
91
92 /* ************************** CONSTRAINTS ************************* */
93 void constraintNumInput(TransInfo *t, float vec[3])
94 {
95         int mode = t->con.mode;
96         if (mode & CON_APPLY) {
97                 float nval = (t->flag & T_NULL_ONE)?1.0f:0.0f;
98
99                 if (getConstraintSpaceDimension(t) == 2) {
100                         int axis = mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
101                         if (axis == (CON_AXIS0|CON_AXIS1)) {
102                                 vec[2] = nval;
103                         }
104                         else if (axis == (CON_AXIS1|CON_AXIS2)) {
105                                 vec[2] = vec[1];
106                                 vec[1] = vec[0];
107                                 vec[0] = nval;
108                         }
109                         else if (axis == (CON_AXIS0|CON_AXIS2)) {
110                                 vec[2] = vec[1];
111                                 vec[1] = nval;
112                         }
113                 }
114                 else if (getConstraintSpaceDimension(t) == 1) {
115                         if (mode & CON_AXIS0) {
116                                 vec[1] = nval;
117                                 vec[2] = nval;
118                         }
119                         else if (mode & CON_AXIS1) {
120                                 vec[1] = vec[0];
121                                 vec[0] = nval;
122                                 vec[2] = nval;
123                         }
124                         else if (mode & CON_AXIS2) {
125                                 vec[2] = vec[0];
126                                 vec[0] = nval;
127                                 vec[1] = nval;
128                         }
129                 }
130         }
131 }
132
133 static void postConstraintChecks(TransInfo *t, float vec[3], float pvec[3]) {
134         int i = 0;
135
136         Mat3MulVecfl(t->con.imtx, vec);
137
138         snapGrid(t, vec);
139
140         if (t->num.flag & T_NULL_ONE) {
141                 if (!(t->con.mode & CON_AXIS0))
142                         vec[0] = 1.0f;
143
144                 if (!(t->con.mode & CON_AXIS1))
145                         vec[1] = 1.0f;
146
147                 if (!(t->con.mode & CON_AXIS2))
148                         vec[2] = 1.0f;
149         }
150
151         if (hasNumInput(&t->num)) {
152                 applyNumInput(&t->num, vec);
153                 constraintNumInput(t, vec);
154         }
155         
156         if (t->con.mode & CON_AXIS0) {
157                 pvec[i++] = vec[0];
158         }
159         if (t->con.mode & CON_AXIS1) {
160                 pvec[i++] = vec[1];
161         }
162         if (t->con.mode & CON_AXIS2) {
163                 pvec[i++] = vec[2];
164         }
165
166         Mat3MulVecfl(t->con.mtx, vec);
167 }
168
169 static void axisProjection(TransInfo *t, float axis[3], float in[3], float out[3]) {
170         float norm[3], vec[3], factor;
171         
172         if(in[0]==0.0f && in[1]==0.0f && in[2]==0.0f)
173                 return;
174         
175         /* For when view is parallel to constraint... will cause NaNs otherwise
176            So we take vertical motion in 3D space and apply it to the
177            constraint axis. Nice for camera grab + MMB */
178         if(1.0f - fabs(Inpf(axis, t->viewinv[2])) < 0.000001f) {
179                 Projf(vec, in, t->viewinv[1]);
180                 factor = Inpf(t->viewinv[1], vec) * 2.0f;
181                 /* since camera distance is quite relative, use quadratic relationship. holding shift can compensate */
182                 if(factor<0.0f) factor*= -factor;
183                 else factor*= factor;
184                 
185                 VECCOPY(out, axis);
186                 Normalize(out);
187                 VecMulf(out, -factor);  /* -factor makes move down going backwards */
188         }
189         else {
190                 float cb[3], ab[3];
191                 
192                 VECCOPY(out, axis);
193                 
194                 /* Get view vector on axis to define a plane */
195                 VecAddf(vec, t->con.center, in);
196                 getViewVector(vec, norm);
197                 
198                 Crossf(vec, norm, axis);
199                 
200                 /* Project input vector on the plane passing on axis */
201                 Projf(vec, in, vec);
202                 VecSubf(vec, in, vec);
203                 
204                 /* intersect the two lines: axis and norm */
205                 Crossf(cb, vec, norm);
206                 Crossf(ab, axis, norm);
207                 
208                 VecMulf(out, Inpf(cb, ab) / Inpf(ab, ab));
209         }
210 }
211
212 static void planeProjection(TransInfo *t, float in[3], float out[3]) {
213         float vec[3], factor, norm[3];
214
215         VecAddf(vec, in, t->con.center);
216         getViewVector(vec, norm);
217
218         VecSubf(vec, out, in);
219
220         factor = Inpf(vec, norm);
221         if (fabs(factor) <= 0.001) {
222                 return; /* prevent divide by zero */
223         }
224         factor = Inpf(vec, vec) / factor;
225
226         VECCOPY(vec, norm);
227         VecMulf(vec, factor);
228
229         VecAddf(out, in, vec);
230 }
231
232 /*
233  * Generic callback for constant spacial constraints applied to linear motion
234  * 
235  * The IN vector in projected into the constrained space and then further
236  * projected along the view vector.
237  * (in perspective mode, the view vector is relative to the position on screen)
238  *
239  */
240
241 static void applyAxisConstraintVec(TransInfo *t, TransData *td, float in[3], float out[3], float pvec[3])
242 {
243         VECCOPY(out, in);
244         if (!td && t->con.mode & CON_APPLY) {
245                 Mat3MulVecfl(t->con.pmtx, out);
246                 
247                 // With snap, a projection is alright, no need to correct for view alignment
248                 if ((t->tsnap.status & SNAP_ON) == 0)
249                         {
250                         if (getConstraintSpaceDimension(t) == 2) {
251                                 if (out[0] != 0.0f || out[1] != 0.0f || out[2] != 0.0f) {
252                                         planeProjection(t, in, out);
253                                 }
254                         }
255                         else if (getConstraintSpaceDimension(t) == 1) {
256                                 float c[3];
257         
258                                 if (t->con.mode & CON_AXIS0) {
259                                         VECCOPY(c, t->con.mtx[0]);
260                                 }
261                                 else if (t->con.mode & CON_AXIS1) {
262                                         VECCOPY(c, t->con.mtx[1]);
263                                 }
264                                 else if (t->con.mode & CON_AXIS2) {
265                                         VECCOPY(c, t->con.mtx[2]);
266                                 }
267                                 axisProjection(t, c, in, out);
268                         }
269                 }
270                 postConstraintChecks(t, out, pvec);
271         }
272 }
273
274 /*
275  * Generic callback for object based spacial constraints applied to linear motion
276  * 
277  * At first, the following is applied to the first data in the array
278  * The IN vector in projected into the constrained space and then further
279  * projected along the view vector.
280  * (in perspective mode, the view vector is relative to the position on screen)
281  *
282  * Further down, that vector is mapped to each data's space.
283  */
284
285 static void applyObjectConstraintVec(TransInfo *t, TransData *td, float in[3], float out[3], float pvec[3])
286 {
287         VECCOPY(out, in);
288         if (t->con.mode & CON_APPLY) {
289                 if (!td) {
290                         Mat3MulVecfl(t->con.pmtx, out);
291                         if (getConstraintSpaceDimension(t) == 2) {
292                                 if (out[0] != 0.0f || out[1] != 0.0f || out[2] != 0.0f) {
293                                         planeProjection(t, in, out);
294                                 }
295                         }
296                         else if (getConstraintSpaceDimension(t) == 1) {
297                                 float c[3];
298
299                                 if (t->con.mode & CON_AXIS0) {
300                                         VECCOPY(c, t->con.mtx[0]);
301                                 }
302                                 else if (t->con.mode & CON_AXIS1) {
303                                         VECCOPY(c, t->con.mtx[1]);
304                                 }
305                                 else if (t->con.mode & CON_AXIS2) {
306                                         VECCOPY(c, t->con.mtx[2]);
307                                 }
308                                 axisProjection(t, c, in, out);
309                         }
310                         postConstraintChecks(t, out, pvec);
311                         VECCOPY(out, pvec);
312                 }
313                 else {
314                         int i=0;
315
316                         out[0] = out[1] = out[2] = 0.0f;
317                         if (t->con.mode & CON_AXIS0) {
318                                 out[0] = in[i++];
319                         }
320                         if (t->con.mode & CON_AXIS1) {
321                                 out[1] = in[i++];
322                         }
323                         if (t->con.mode & CON_AXIS2) {
324                                 out[2] = in[i++];
325                         }
326                         Mat3MulVecfl(td->axismtx, out);
327                 }
328         }
329 }
330
331 /*
332  * Generic callback for constant spacial constraints applied to resize motion
333  * 
334  *
335  */
336
337 static void applyAxisConstraintSize(TransInfo *t, TransData *td, float smat[3][3])
338 {
339         if (!td && t->con.mode & CON_APPLY) {
340                 float tmat[3][3];
341
342                 if (!(t->con.mode & CON_AXIS0)) {
343                         smat[0][0] = 1.0f;
344                 }
345                 if (!(t->con.mode & CON_AXIS1)) {
346                         smat[1][1] = 1.0f;
347                 }
348                 if (!(t->con.mode & CON_AXIS2)) {
349                         smat[2][2] = 1.0f;
350                 }
351
352                 Mat3MulMat3(tmat, smat, t->con.imtx);
353                 Mat3MulMat3(smat, t->con.mtx, tmat);
354         }
355 }
356
357 /*
358  * Callback for object based spacial constraints applied to resize motion
359  * 
360  *
361  */
362
363 static void applyObjectConstraintSize(TransInfo *t, TransData *td, float smat[3][3])
364 {
365         if (td && t->con.mode & CON_APPLY) {
366                 float tmat[3][3];
367                 float imat[3][3];
368
369                 Mat3Inv(imat, td->axismtx);
370
371                 if (!(t->con.mode & CON_AXIS0)) {
372                         smat[0][0] = 1.0f;
373                 }
374                 if (!(t->con.mode & CON_AXIS1)) {
375                         smat[1][1] = 1.0f;
376                 }
377                 if (!(t->con.mode & CON_AXIS2)) {
378                         smat[2][2] = 1.0f;
379                 }
380
381                 Mat3MulMat3(tmat, smat, imat);
382                 Mat3MulMat3(smat, td->axismtx, tmat);
383         }
384 }
385
386 /*
387  * Generic callback for constant spacial constraints applied to rotations
388  * 
389  * The rotation axis is copied into VEC.
390  *
391  * In the case of single axis constraints, the rotation axis is directly the one constrained to.
392  * For planar constraints (2 axis), the rotation axis is the normal of the plane.
393  *
394  * The following only applies when CON_NOFLIP is not set.
395  * The vector is then modified to always point away from the screen (in global space)
396  * This insures that the rotation is always logically following the mouse.
397  * (ie: not doing counterclockwise rotations when the mouse moves clockwise).
398  */
399
400 static void applyAxisConstraintRot(TransInfo *t, TransData *td, float vec[3])
401 {
402         if (!td && t->con.mode & CON_APPLY) {
403                 int mode = t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
404
405                 switch(mode) {
406                 case CON_AXIS0:
407                 case (CON_AXIS1|CON_AXIS2):
408                         VECCOPY(vec, t->con.mtx[0]);
409                         break;
410                 case CON_AXIS1:
411                 case (CON_AXIS0|CON_AXIS2):
412                         VECCOPY(vec, t->con.mtx[1]);
413                         break;
414                 case CON_AXIS2:
415                 case (CON_AXIS0|CON_AXIS1):
416                         VECCOPY(vec, t->con.mtx[2]);
417                         break;
418                 }
419                 if (!(mode & CON_NOFLIP)) {
420                         if (Inpf(vec, t->viewinv[2]) > 0.0f) {
421                                 VecMulf(vec, -1.0f);
422                         }
423                 }
424         }
425 }
426
427 /*
428  * Callback for object based spacial constraints applied to rotations
429  * 
430  * The rotation axis is copied into VEC.
431  *
432  * In the case of single axis constraints, the rotation axis is directly the one constrained to.
433  * For planar constraints (2 axis), the rotation axis is the normal of the plane.
434  *
435  * The following only applies when CON_NOFLIP is not set.
436  * The vector is then modified to always point away from the screen (in global space)
437  * This insures that the rotation is always logically following the mouse.
438  * (ie: not doing counterclockwise rotations when the mouse moves clockwise).
439  */
440
441 static void applyObjectConstraintRot(TransInfo *t, TransData *td, float vec[3])
442 {
443         if (td && t->con.mode & CON_APPLY) {
444                 int mode = t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
445
446                 switch(mode) {
447                 case CON_AXIS0:
448                 case (CON_AXIS1|CON_AXIS2):
449                         VECCOPY(vec, td->axismtx[0]);
450                         break;
451                 case CON_AXIS1:
452                 case (CON_AXIS0|CON_AXIS2):
453                         VECCOPY(vec, td->axismtx[1]);
454                         break;
455                 case CON_AXIS2:
456                 case (CON_AXIS0|CON_AXIS1):
457                         VECCOPY(vec, td->axismtx[2]);
458                         break;
459                 }
460                 if (!(mode & CON_NOFLIP)) {
461                         if (Inpf(vec, t->viewinv[2]) > 0.0f) {
462                                 VecMulf(vec, -1.0f);
463                         }
464                 }
465         }
466 }
467
468 /*--------------------- INTERNAL SETUP CALLS ------------------*/
469
470 void setConstraint(TransInfo *t, float space[3][3], int mode, const char text[]) {
471         strncpy(t->con.text + 1, text, 48);
472         Mat3CpyMat3(t->con.mtx, space);
473         t->con.mode = mode;
474         getConstraintMatrix(t);
475
476         startConstraint(t);
477
478         t->con.drawExtra = NULL;
479         t->con.applyVec = applyAxisConstraintVec;
480         t->con.applySize = applyAxisConstraintSize;
481         t->con.applyRot = applyAxisConstraintRot;
482         t->redraw = 1;
483 }
484
485 void setLocalConstraint(TransInfo *t, int mode, const char text[]) {
486         if (t->flag & T_EDIT) {
487                 float obmat[3][3];
488                 Mat3CpyMat4(obmat, G.obedit->obmat);
489                 setConstraint(t, obmat, mode|CON_LOCAL, text);
490         }
491         else {
492                 if (t->total == 1) {
493                         setConstraint(t, t->data->axismtx, mode|CON_LOCAL, text);
494                 }
495                 else {
496                         strncpy(t->con.text + 1, text, 48);
497                         Mat3CpyMat3(t->con.mtx, t->data->axismtx);
498                         t->con.mode = mode|CON_LOCAL;
499                         getConstraintMatrix(t);
500
501                         startConstraint(t);
502
503                         t->con.drawExtra = drawObjectConstraint;
504                         t->con.applyVec = applyObjectConstraintVec;
505                         t->con.applySize = applyObjectConstraintSize;
506                         t->con.applyRot = applyObjectConstraintRot;
507                         t->redraw = 1;
508                 }
509         }
510 }
511
512 /*
513         Set the constraint according to the user defined orientation
514
515         ftext is a format string passed to sprintf. It will add the name of
516         the orientation where %s is (logically).
517 */
518 void setUserConstraint(TransInfo *t, int mode, const char ftext[]) {
519         char text[40];
520         short twmode= (t->spacetype==SPACE_VIEW3D)? G.vd->twmode: V3D_MANIP_GLOBAL;
521
522         switch(twmode) {
523         case V3D_MANIP_GLOBAL:
524         /*
525                 sprintf(text, ftext, "global");
526                 Mat3One(mtx);
527                 setConstraint(t, mtx, mode, text);
528                 break;
529         */
530         case V3D_MANIP_LOCAL:
531                 sprintf(text, ftext, "local");
532                 setLocalConstraint(t, mode, text);
533                 break;
534         case V3D_MANIP_NORMAL:
535                 sprintf(text, ftext, "normal");
536                 setConstraint(t, t->spacemtx, mode, text);
537                 break;
538         case V3D_MANIP_VIEW:
539                 sprintf(text, ftext, "view");
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>1) 
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) mymultmatrix(G.obedit->obmat);
723                 
724                 mygetmatrix(tmat);
725                 Mat4Invert(imat, tmat);
726
727                 set_inverted_drawing(1);
728                 drawcircball(GL_LINE_LOOP, t->center, t->propsize, imat);
729                 set_inverted_drawing(0);
730                 
731                 /* if editmode we restore */
732                 if(G.obedit) myloadmatrix(G.vd->viewmat);
733         }
734 }
735
736 void BIF_getPropCenter(float *center)
737 {
738         TransInfo *t = BIF_GetTransInfo();
739
740         if (t && t->flag & T_PROP_EDIT) {
741                 VECCOPY(center, t->center);
742         }
743         else
744                 center[0] = center[1] = center[2] = 0.0f;
745 }
746 static void drawObjectConstraint(TransInfo *t) {
747         int i;
748         TransData * td = t->data;
749
750         /* Draw the first one lighter because that's the one who controls the others.
751            Meaning the transformation is projected on that one and just copied on the others
752            constraint space.
753            In a nutshell, the object with light axis is controlled by the user and the others follow.
754            Without drawing the first light, users have little clue what they are doing.
755          */
756         if (t->con.mode & CON_AXIS0) {
757                 drawLine(td->ob->obmat[3], td->axismtx[0], 'x', DRAWLIGHT);
758         }
759         if (t->con.mode & CON_AXIS1) {
760                 drawLine(td->ob->obmat[3], td->axismtx[1], 'y', DRAWLIGHT);
761         }
762         if (t->con.mode & CON_AXIS2) {
763                 drawLine(td->ob->obmat[3], td->axismtx[2], 'z', DRAWLIGHT);
764         }
765         
766         td++;
767
768         for(i=1;i<t->total;i++,td++) {
769                 if (t->con.mode & CON_AXIS0) {
770                         drawLine(td->ob->obmat[3], td->axismtx[0], 'x', 0);
771                 }
772                 if (t->con.mode & CON_AXIS1) {
773                         drawLine(td->ob->obmat[3], td->axismtx[1], 'y', 0);
774                 }
775                 if (t->con.mode & CON_AXIS2) {
776                         drawLine(td->ob->obmat[3], td->axismtx[2], 'z', 0);
777                 }
778         }
779 }
780
781 /*--------------------- START / STOP CONSTRAINTS ---------------------- */
782
783 void startConstraint(TransInfo *t) {
784         t->con.mode |= CON_APPLY;
785         *t->con.text = ' ';
786         t->num.idx_max = MIN2(getConstraintSpaceDimension(t) - 1, t->idx_max);
787 }
788
789 void stopConstraint(TransInfo *t) {
790         t->con.mode &= ~(CON_APPLY|CON_SELECT);
791         *t->con.text = '\0';
792         t->num.idx_max = t->idx_max;
793 }
794
795 void getConstraintMatrix(TransInfo *t)
796 {
797         float mat[3][3];
798         Mat3Inv(t->con.imtx, t->con.mtx);
799         Mat3One(t->con.pmtx);
800
801         if (!(t->con.mode & CON_AXIS0)) {
802                 t->con.pmtx[0][0]               =
803                         t->con.pmtx[0][1]       =
804                         t->con.pmtx[0][2]       = 0.0f;
805         }
806
807         if (!(t->con.mode & CON_AXIS1)) {
808                 t->con.pmtx[1][0]               =
809                         t->con.pmtx[1][1]       =
810                         t->con.pmtx[1][2]       = 0.0f;
811         }
812
813         if (!(t->con.mode & CON_AXIS2)) {
814                 t->con.pmtx[2][0]               =
815                         t->con.pmtx[2][1]       =
816                         t->con.pmtx[2][2]       = 0.0f;
817         }
818
819         Mat3MulMat3(mat, t->con.pmtx, t->con.imtx);
820         Mat3MulMat3(t->con.pmtx, t->con.mtx, mat);
821 }
822
823 /*------------------------- MMB Select -------------------------------*/
824
825 void initSelectConstraint(TransInfo *t, float mtx[3][3])
826 {
827         Mat3CpyMat3(t->con.mtx, mtx);
828         t->con.mode |= CON_APPLY;
829         t->con.mode |= CON_SELECT;
830         t->con.mode &= ~CON_LOCAL;
831
832         setNearestAxis(t);
833         t->con.drawExtra = NULL;
834         t->con.applyVec = applyAxisConstraintVec;
835         t->con.applySize = applyAxisConstraintSize;
836         t->con.applyRot = applyAxisConstraintRot;
837 }
838
839 void selectConstraint(TransInfo *t) {
840         if (t->con.mode & CON_SELECT) {
841                 setNearestAxis(t);
842                 startConstraint(t);
843         }
844 }
845
846 void postSelectConstraint(TransInfo *t)
847 {
848         if (!(t->con.mode & CON_SELECT))
849                 return;
850
851         t->con.mode &= ~CON_AXIS0;
852         t->con.mode &= ~CON_AXIS1;
853         t->con.mode &= ~CON_AXIS2;
854         t->con.mode &= ~CON_SELECT;
855
856         setNearestAxis(t);
857
858         startConstraint(t);
859         t->redraw = 1;
860 }
861
862 static void setNearestAxis2d(TransInfo *t)
863 {
864         short mval[2];
865         short ival[2];
866         
867         getmouseco_areawin(mval);
868         ival[0]= t->imval[0];
869         ival[1]= t->imval[1];
870         
871         /* no correction needed... just use whichever one is lower */
872         if ( abs(mval[0]-ival[0]) < abs(mval[1]-ival[1]) ) {
873                 t->con.mode |= CON_AXIS1;
874                 sprintf(t->con.text, " along Y axis");
875         }
876         else {
877                 t->con.mode |= CON_AXIS0;
878                 sprintf(t->con.text, " along X axis");
879         }
880 }
881
882 static void setNearestAxis3d(TransInfo *t)
883 {
884         float zfac;
885         float mvec[3], axis[3], proj[3];
886         float len[3];
887         int i, icoord[2];
888         short coord[2];
889         
890         /* calculate mouse movement */
891         getmouseco_areawin(coord);
892         mvec[0] = (float)(coord[0] - t->con.imval[0]);
893         mvec[1] = (float)(coord[1] - t->con.imval[1]);
894         mvec[2] = 0.0f;
895         
896         /* we need to correct axis length for the current zoomlevel of view,
897            this to prevent projected values to be clipped behind the camera
898            and to overflow the short integers.
899            The formula used is a bit stupid, just a simplification of the substraction
900            of two 2D points 30 pixels apart (that's the last factor in the formula) after
901            projecting them with window_to_3d and then get the length of that vector.
902         */
903         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];
904         zfac = VecLength(t->persinv[0]) * 2.0f/curarea->winx * zfac * 30.0f;
905
906         for (i = 0; i<3; i++) {
907                 VECCOPY(axis, t->con.mtx[i]);
908                 
909                 VecMulf(axis, zfac);
910                 /* now we can project to get window coordinate */
911                 VecAddf(axis, axis, t->con.center);
912                 projectIntView(t, axis, icoord);
913                 
914                 axis[0] = (float)(icoord[0] - t->center2d[0]);
915                 axis[1] = (float)(icoord[1] - t->center2d[1]);
916                 axis[2] = 0.0f;
917
918                 if (Normalize(axis) != 0.0f) {
919                         Projf(proj, mvec, axis);
920                         VecSubf(axis, mvec, proj);
921                         len[i] = Normalize(axis);
922                 }
923                 else {
924                         len[i] = 10000000000.0f;
925                 }
926         }
927
928         if (len[0] <= len[1] && len[0] <= len[2]) {
929                 if (G.qual & LR_SHIFTKEY) {
930                         t->con.mode |= (CON_AXIS1|CON_AXIS2);
931                         sprintf(t->con.text, " locking %s X axis", t->spacename);
932                 }
933                 else {
934                         t->con.mode |= CON_AXIS0;
935                         sprintf(t->con.text, " along %s X axis", t->spacename);
936                 }
937         }
938         else if (len[1] <= len[0] && len[1] <= len[2]) {
939                 if (G.qual & LR_SHIFTKEY) {
940                         t->con.mode |= (CON_AXIS0|CON_AXIS2);
941                         sprintf(t->con.text, " locking %s Y axis", t->spacename);
942                 }
943                 else {
944                         t->con.mode |= CON_AXIS1;
945                         sprintf(t->con.text, " along %s Y axis", t->spacename);
946                 }
947         }
948         else if (len[2] <= len[1] && len[2] <= len[0]) {
949                 if (G.qual & LR_SHIFTKEY) {
950                         t->con.mode |= (CON_AXIS0|CON_AXIS1);
951                         sprintf(t->con.text, " locking %s Z axis", t->spacename);
952                 }
953                 else {
954                         t->con.mode |= CON_AXIS2;
955                         sprintf(t->con.text, " along %s Z axis", t->spacename);
956                 }
957         }
958 }
959
960 void setNearestAxis(TransInfo *t)
961 {
962         /* clear any prior constraint flags */
963         t->con.mode &= ~CON_AXIS0;
964         t->con.mode &= ~CON_AXIS1;
965         t->con.mode &= ~CON_AXIS2;
966
967         /* constraint setting - depends on spacetype */
968         if (t->spacetype == SPACE_VIEW3D) {
969                 /* 3d-view */
970                 setNearestAxis3d(t);    
971         }
972         else {
973                 /* assume that this means a 2D-Editor */
974                 setNearestAxis2d(t);
975         }
976         
977         getConstraintMatrix(t);
978 }
979
980 /*-------------- HELPER FUNCTIONS ----------------*/
981
982 char constraintModeToChar(TransInfo *t) {
983         if ((t->con.mode & CON_APPLY)==0) {
984                 return '\0';
985         }
986         switch (t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2)) {
987         case (CON_AXIS0):
988         case (CON_AXIS1|CON_AXIS2):
989                 return 'X';
990         case (CON_AXIS1):
991         case (CON_AXIS0|CON_AXIS2):
992                 return 'Y';
993         case (CON_AXIS2):
994         case (CON_AXIS0|CON_AXIS1):
995                 return 'Z';
996         default:
997                 return '\0';
998         }
999 }
1000
1001
1002 int isLockConstraint(TransInfo *t) {
1003         int mode = t->con.mode;
1004
1005         if ( (mode & (CON_AXIS0|CON_AXIS1)) == (CON_AXIS0|CON_AXIS1))
1006                 return 1;
1007
1008         if ( (mode & (CON_AXIS1|CON_AXIS2)) == (CON_AXIS1|CON_AXIS2))
1009                 return 1;
1010
1011         if ( (mode & (CON_AXIS0|CON_AXIS2)) == (CON_AXIS0|CON_AXIS2))
1012                 return 1;
1013
1014         return 0;
1015 }
1016
1017 /*
1018  * Returns the dimension of the constraint space.
1019  * 
1020  * For that reason, the flags always needs to be set to properly evaluate here,
1021  * even if they aren't actually used in the callback function. (Which could happen
1022  * for weird constraints not yet designed. Along a path for example.)
1023  */
1024
1025 int getConstraintSpaceDimension(TransInfo *t)
1026 {
1027         int n = 0;
1028
1029         if (t->con.mode & CON_AXIS0)
1030                 n++;
1031
1032         if (t->con.mode & CON_AXIS1)
1033                 n++;
1034
1035         if (t->con.mode & CON_AXIS2)
1036                 n++;
1037
1038         return n;
1039 /*
1040   Someone willing to do it criptically could do the following instead:
1041
1042   return t->con & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
1043         
1044   Based on the assumptions that the axis flags are one after the other and start at 1
1045 */
1046 }