26d240be3fc5191031d8d6bbb601a2b0ce788af0
[blender-staging.git] / source / blender / editors / transform / 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 "ED_image.h"
76 #include "ED_view3d.h"
77
78 #include "BLI_arithb.h"
79
80 //#include "blendef.h"
81 //
82 //#include "mydevice.h"
83
84 #include "WM_types.h"
85 #include "UI_resources.h"
86
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(t, 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(t, 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                         if (getConstraintSpaceDimension(t) == 2) {
250                                 if (out[0] != 0.0f || out[1] != 0.0f || out[2] != 0.0f) {
251                                         planeProjection(t, in, out);
252                                 }
253                         }
254                         else if (getConstraintSpaceDimension(t) == 1) {
255                                 float c[3];
256         
257                                 if (t->con.mode & CON_AXIS0) {
258                                         VECCOPY(c, t->con.mtx[0]);
259                                 }
260                                 else if (t->con.mode & CON_AXIS1) {
261                                         VECCOPY(c, t->con.mtx[1]);
262                                 }
263                                 else if (t->con.mode & CON_AXIS2) {
264                                         VECCOPY(c, t->con.mtx[2]);
265                                 }
266                                 axisProjection(t, c, in, out);
267                         }
268                 }
269                 postConstraintChecks(t, out, pvec);
270         }
271 }
272
273 /*
274  * Generic callback for object based spacial constraints applied to linear motion
275  * 
276  * At first, the following is applied to the first data in the array
277  * The IN vector in projected into the constrained space and then further
278  * projected along the view vector.
279  * (in perspective mode, the view vector is relative to the position on screen)
280  *
281  * Further down, that vector is mapped to each data's space.
282  */
283
284 static void applyObjectConstraintVec(TransInfo *t, TransData *td, float in[3], float out[3], float pvec[3])
285 {
286         VECCOPY(out, in);
287         if (t->con.mode & CON_APPLY) {
288                 if (!td) {
289                         Mat3MulVecfl(t->con.pmtx, out);
290                         if (getConstraintSpaceDimension(t) == 2) {
291                                 if (out[0] != 0.0f || out[1] != 0.0f || out[2] != 0.0f) {
292                                         planeProjection(t, in, out);
293                                 }
294                         }
295                         else if (getConstraintSpaceDimension(t) == 1) {
296                                 float c[3];
297
298                                 if (t->con.mode & CON_AXIS0) {
299                                         VECCOPY(c, t->con.mtx[0]);
300                                 }
301                                 else if (t->con.mode & CON_AXIS1) {
302                                         VECCOPY(c, t->con.mtx[1]);
303                                 }
304                                 else if (t->con.mode & CON_AXIS2) {
305                                         VECCOPY(c, t->con.mtx[2]);
306                                 }
307                                 axisProjection(t, c, in, out);
308                         }
309                         postConstraintChecks(t, out, pvec);
310                         VECCOPY(out, pvec);
311                 }
312                 else {
313                         int i=0;
314
315                         out[0] = out[1] = out[2] = 0.0f;
316                         if (t->con.mode & CON_AXIS0) {
317                                 out[0] = in[i++];
318                         }
319                         if (t->con.mode & CON_AXIS1) {
320                                 out[1] = in[i++];
321                         }
322                         if (t->con.mode & CON_AXIS2) {
323                                 out[2] = in[i++];
324                         }
325                         Mat3MulVecfl(td->axismtx, out);
326                 }
327         }
328 }
329
330 /*
331  * Generic callback for constant spacial constraints applied to resize motion
332  * 
333  *
334  */
335
336 static void applyAxisConstraintSize(TransInfo *t, TransData *td, float smat[3][3])
337 {
338         if (!td && t->con.mode & CON_APPLY) {
339                 float tmat[3][3];
340
341                 if (!(t->con.mode & CON_AXIS0)) {
342                         smat[0][0] = 1.0f;
343                 }
344                 if (!(t->con.mode & CON_AXIS1)) {
345                         smat[1][1] = 1.0f;
346                 }
347                 if (!(t->con.mode & CON_AXIS2)) {
348                         smat[2][2] = 1.0f;
349                 }
350
351                 Mat3MulMat3(tmat, smat, t->con.imtx);
352                 Mat3MulMat3(smat, t->con.mtx, tmat);
353         }
354 }
355
356 /*
357  * Callback for object based spacial constraints applied to resize motion
358  * 
359  *
360  */
361
362 static void applyObjectConstraintSize(TransInfo *t, TransData *td, float smat[3][3])
363 {
364         if (td && t->con.mode & CON_APPLY) {
365                 float tmat[3][3];
366                 float imat[3][3];
367
368                 Mat3Inv(imat, td->axismtx);
369
370                 if (!(t->con.mode & CON_AXIS0)) {
371                         smat[0][0] = 1.0f;
372                 }
373                 if (!(t->con.mode & CON_AXIS1)) {
374                         smat[1][1] = 1.0f;
375                 }
376                 if (!(t->con.mode & CON_AXIS2)) {
377                         smat[2][2] = 1.0f;
378                 }
379
380                 Mat3MulMat3(tmat, smat, imat);
381                 Mat3MulMat3(smat, td->axismtx, tmat);
382         }
383 }
384
385 /*
386  * Generic callback for constant spacial constraints applied to rotations
387  * 
388  * The rotation axis is copied into VEC.
389  *
390  * In the case of single axis constraints, the rotation axis is directly the one constrained to.
391  * For planar constraints (2 axis), the rotation axis is the normal of the plane.
392  *
393  * The following only applies when CON_NOFLIP is not set.
394  * The vector is then modified to always point away from the screen (in global space)
395  * This insures that the rotation is always logically following the mouse.
396  * (ie: not doing counterclockwise rotations when the mouse moves clockwise).
397  */
398
399 static void applyAxisConstraintRot(TransInfo *t, TransData *td, float vec[3], float *angle)
400 {
401         if (!td && t->con.mode & CON_APPLY) {
402                 int mode = t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
403
404                 switch(mode) {
405                 case CON_AXIS0:
406                 case (CON_AXIS1|CON_AXIS2):
407                         VECCOPY(vec, t->con.mtx[0]);
408                         break;
409                 case CON_AXIS1:
410                 case (CON_AXIS0|CON_AXIS2):
411                         VECCOPY(vec, t->con.mtx[1]);
412                         break;
413                 case CON_AXIS2:
414                 case (CON_AXIS0|CON_AXIS1):
415                         VECCOPY(vec, t->con.mtx[2]);
416                         break;
417                 }
418                 /* don't flip axis if asked to or if num input */
419                 if (angle && (mode & CON_NOFLIP) == 0 && hasNumInput(&t->num) == 0) {
420                         if (Inpf(vec, t->viewinv[2]) > 0.0f) {
421                                 *angle = -(*angle);
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], float *angle)
442 {
443         if (t->con.mode & CON_APPLY) {
444                 int mode = t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
445                 
446                 /* on setup call, use first object */
447                 if (td == NULL) {
448                         td= t->data;
449                 }
450
451                 switch(mode) {
452                 case CON_AXIS0:
453                 case (CON_AXIS1|CON_AXIS2):
454                         VECCOPY(vec, td->axismtx[0]);
455                         break;
456                 case CON_AXIS1:
457                 case (CON_AXIS0|CON_AXIS2):
458                         VECCOPY(vec, td->axismtx[1]);
459                         break;
460                 case CON_AXIS2:
461                 case (CON_AXIS0|CON_AXIS1):
462                         VECCOPY(vec, td->axismtx[2]);
463                         break;
464                 }
465                 if (angle && (mode & CON_NOFLIP) == 0 && hasNumInput(&t->num) == 0) {
466                         if (Inpf(vec, t->viewinv[2]) > 0.0f) {
467                                 *angle = -(*angle);
468                         }
469                 }
470         }
471 }
472
473 /*--------------------- INTERNAL SETUP CALLS ------------------*/
474
475 void setConstraint(TransInfo *t, float space[3][3], int mode, const char text[]) {
476         strncpy(t->con.text + 1, text, 48);
477         Mat3CpyMat3(t->con.mtx, space);
478         t->con.mode = mode;
479         getConstraintMatrix(t);
480
481         startConstraint(t);
482
483         t->con.drawExtra = NULL;
484         t->con.applyVec = applyAxisConstraintVec;
485         t->con.applySize = applyAxisConstraintSize;
486         t->con.applyRot = applyAxisConstraintRot;
487         t->redraw = 1;
488 }
489
490 void setLocalConstraint(TransInfo *t, int mode, const char text[]) {
491         if (t->flag & T_EDIT) {
492                 float obmat[3][3];
493                 Mat3CpyMat4(obmat, t->scene->obedit->obmat);
494                 setConstraint(t, obmat, mode, text);
495         }
496         else {
497                 if (t->total == 1) {
498                         setConstraint(t, t->data->axismtx, mode, text);
499                 }
500                 else {
501                         strncpy(t->con.text + 1, text, 48);
502                         Mat3CpyMat3(t->con.mtx, t->data->axismtx);
503                         t->con.mode = mode;
504                         getConstraintMatrix(t);
505
506                         startConstraint(t);
507
508                         t->con.drawExtra = drawObjectConstraint;
509                         t->con.applyVec = applyObjectConstraintVec;
510                         t->con.applySize = applyObjectConstraintSize;
511                         t->con.applyRot = applyObjectConstraintRot;
512                         t->redraw = 1;
513                 }
514         }
515 }
516
517 /*
518         Set the constraint according to the user defined orientation
519
520         ftext is a format string passed to sprintf. It will add the name of
521         the orientation where %s is (logically).
522 */
523 void setUserConstraint(TransInfo *t, int mode, const char ftext[]) {
524         char text[40];
525         short twmode= (t->spacetype==SPACE_VIEW3D)? ((View3D*)t->view)->twmode: V3D_MANIP_GLOBAL;
526
527         switch(twmode) {
528         case V3D_MANIP_GLOBAL:
529                 {
530                         float mtx[3][3];
531                         sprintf(text, ftext, "global");
532                         Mat3One(mtx);
533                         setConstraint(t, mtx, mode, text);
534                 }
535                 break;
536         case V3D_MANIP_LOCAL:
537                 sprintf(text, ftext, "local");
538                 setLocalConstraint(t, mode, text);
539                 break;
540         case V3D_MANIP_NORMAL:
541                 sprintf(text, ftext, "normal");
542                 setConstraint(t, t->spacemtx, mode, text);
543                 break;
544         case V3D_MANIP_VIEW:
545                 sprintf(text, ftext, "view");
546                 setConstraint(t, t->spacemtx, mode, text);
547                 break;
548         default: /* V3D_MANIP_CUSTOM */
549                 sprintf(text, ftext, t->spacename);
550                 setConstraint(t, t->spacemtx, mode, text);
551                 break;
552         }
553
554         t->con.mode |= CON_USER;
555 }
556
557 /*--------------------- EXTERNAL SETUP CALLS ------------------*/
558
559 void BIF_setLocalLockConstraint(char axis, char *text) {
560         TransInfo *t = BIF_GetTransInfo();
561
562         if (t->total == 0) {
563                 return;
564         }
565         
566         switch (axis) {
567         case 'x':
568                 setLocalConstraint(t, (CON_AXIS1|CON_AXIS2), text);
569                 break;
570         case 'y':
571                 setLocalConstraint(t, (CON_AXIS0|CON_AXIS2), text);
572                 break;
573         case 'z':
574                 setLocalConstraint(t, (CON_AXIS0|CON_AXIS1), text);
575                 break;
576         }
577 }
578
579 void BIF_setLocalAxisConstraint(char axis, char *text) {
580         TransInfo *t = BIF_GetTransInfo();
581
582         if (t->total == 0) {
583                 return;
584         }
585         
586         switch (axis) {
587         case 'X':
588                 setLocalConstraint(t, CON_AXIS0, text);
589                 break;
590         case 'Y':
591                 setLocalConstraint(t, CON_AXIS1, text);
592                 break;
593         case 'Z':
594                 setLocalConstraint(t, CON_AXIS2, text);
595                 break;
596         }
597 }
598
599 /* text is optional, for header print */
600 void BIF_setSingleAxisConstraint(float vec[3], char *text) {
601         TransInfo *t = BIF_GetTransInfo();
602         float space[3][3], v[3];
603         
604         if (t->total == 0) {
605                 return;
606         }
607         
608         VECCOPY(space[0], vec);
609
610         v[0] = vec[2];
611         v[1] = vec[0];
612         v[2] = vec[1];
613
614         Crossf(space[1], vec, v);
615         Crossf(space[2], vec, space[1]);
616         Mat3Ortho(space);
617
618         Mat3CpyMat3(t->con.mtx, space);
619         t->con.mode = CON_AXIS0;
620         
621         getConstraintMatrix(t);
622
623         startConstraint(t);
624         
625         /* start copying with an offset of 1, to reserve a spot for the SPACE char */
626         if(text)
627         {
628                 strncpy(t->con.text+1, text, 48);       /* 50 in struct */
629         }
630         else
631         {
632                 t->con.text[1] = '\0'; /* No text */
633         }
634         
635         t->con.drawExtra = NULL;
636         t->con.applyVec = applyAxisConstraintVec;
637         t->con.applySize = applyAxisConstraintSize;
638         t->con.applyRot = applyAxisConstraintRot;
639         t->redraw = 1;
640 }
641
642 void BIF_setDualAxisConstraint(float vec1[3], float vec2[3], char *text) {
643         TransInfo *t = BIF_GetTransInfo();
644         float space[3][3];
645         
646         if (t->total == 0) {
647                 return;
648         }
649
650         VECCOPY(space[0], vec1);
651         VECCOPY(space[1], vec2);
652         Crossf(space[2], space[0], space[1]);
653         Mat3Ortho(space);
654         
655         Mat3CpyMat3(t->con.mtx, space);
656         t->con.mode = CON_AXIS0|CON_AXIS1;
657
658         getConstraintMatrix(t);
659
660         startConstraint(t);
661         
662         /* start copying with an offset of 1, to reserve a spot for the SPACE char */
663         if(text)
664         {
665                 strncpy(t->con.text+1, text, 48);       /* 50 in struct */
666         }
667         else
668         {
669                 t->con.text[1] = '\0'; /* No text */
670         }
671
672         t->con.drawExtra = NULL;
673         t->con.applyVec = applyAxisConstraintVec;
674         t->con.applySize = applyAxisConstraintSize;
675         t->con.applyRot = applyAxisConstraintRot;
676         t->redraw = 1;
677 }
678
679 /*----------------- DRAWING CONSTRAINTS -------------------*/
680
681 void drawConstraint(TransInfo *t)
682 {
683         TransCon *tc = &(t->con);
684
685         if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE))
686                 return;
687         if (!(tc->mode & CON_APPLY))
688                 return;
689         if (t->flag & T_USES_MANIPULATOR)
690                 return;
691         if (t->flag & T_NO_CONSTRAINT)
692                 return;
693         
694         /* nasty exception for Z constraint in camera view */
695         // TRANSFORM_FIX_ME
696 //      if((t->flag & T_OBJECT) && G.vd->camera==OBACT && G.vd->persp==V3D_CAMOB) 
697 //              return;
698
699         if (tc->drawExtra) {
700                 tc->drawExtra(t);
701         }
702         else {
703                 if (tc->mode & CON_SELECT) {
704                         float vec[3];
705                         char col2[3] = {255,255,255};
706                         convertViewVec(t, vec, (short)(t->mval[0] - t->con.imval[0]), (short)(t->mval[1] - t->con.imval[1]));
707                         VecAddf(vec, vec, tc->center);
708
709                         drawLine(t, tc->center, tc->mtx[0], 'x', 0);
710                         drawLine(t, tc->center, tc->mtx[1], 'y', 0);
711                         drawLine(t, tc->center, tc->mtx[2], 'z', 0);
712
713                         glColor3ubv((GLubyte *)col2);
714                         
715                         glDisable(GL_DEPTH_TEST);
716                         setlinestyle(1);
717                         glBegin(GL_LINE_STRIP); 
718                                 glVertex3fv(tc->center); 
719                                 glVertex3fv(vec); 
720                         glEnd();
721                         setlinestyle(0);
722                         // TRANSFORM_FIX_ME
723                         //if(G.vd->zbuf)
724                                 glEnable(GL_DEPTH_TEST);        
725                 }
726
727                 if (tc->mode & CON_AXIS0) {
728                         drawLine(t, tc->center, tc->mtx[0], 'x', DRAWLIGHT);
729                 }
730                 if (tc->mode & CON_AXIS1) {
731                         drawLine(t, tc->center, tc->mtx[1], 'y', DRAWLIGHT);
732                 }
733                 if (tc->mode & CON_AXIS2) {
734                         drawLine(t, tc->center, tc->mtx[2], 'z', DRAWLIGHT);
735                 }
736         }
737 }
738
739 /* called from drawview.c, as an extra per-window draw option */
740 void drawPropCircle(TransInfo *t)
741 {
742         if (t->flag & T_PROP_EDIT) {
743                 float tmat[4][4], imat[4][4];
744
745                 UI_ThemeColor(TH_GRID);
746                 
747                 if(t->spacetype == SPACE_VIEW3D)
748                 {
749                         RegionView3D *rv3d = t->ar->regiondata;
750                         
751                         Mat4CpyMat4(tmat, rv3d->viewmat);
752                         Mat4Invert(imat, tmat);
753                 }
754                 else
755                 {
756                         Mat4One(tmat);
757                         Mat4One(imat);
758                 }
759
760                 glPushMatrix();
761
762                 if((t->spacetype == SPACE_VIEW3D) && t->obedit)
763                 {
764                         glMultMatrixf(t->obedit->obmat); /* because t->center is in local space */
765                 }
766                 else if(t->spacetype == SPACE_IMAGE)
767                 {
768                         float aspx, aspy;
769
770                         ED_space_image_uv_aspect(t->sa->spacedata.first, &aspx, &aspy);
771                         glScalef(1.0f/aspx, 1.0f/aspy, 1.0);
772                 }
773
774                 set_inverted_drawing(1);
775                 drawcircball(GL_LINE_LOOP, t->center, t->propsize, imat);
776                 set_inverted_drawing(0);
777                 
778                 glPopMatrix();
779         }
780 }
781
782 void BIF_getPropCenter(float *center)
783 {
784         TransInfo *t = BIF_GetTransInfo();
785
786         if (t && t->flag & T_PROP_EDIT) {
787                 VECCOPY(center, t->center);
788         }
789         else
790                 center[0] = center[1] = center[2] = 0.0f;
791 }
792
793 static void drawObjectConstraint(TransInfo *t) {
794         int i;
795         TransData * td = t->data;
796
797         /* Draw the first one lighter because that's the one who controls the others.
798            Meaning the transformation is projected on that one and just copied on the others
799            constraint space.
800            In a nutshell, the object with light axis is controlled by the user and the others follow.
801            Without drawing the first light, users have little clue what they are doing.
802          */
803         if (t->con.mode & CON_AXIS0) {
804                 drawLine(t, td->ob->obmat[3], td->axismtx[0], 'x', DRAWLIGHT);
805         }
806         if (t->con.mode & CON_AXIS1) {
807                 drawLine(t, td->ob->obmat[3], td->axismtx[1], 'y', DRAWLIGHT);
808         }
809         if (t->con.mode & CON_AXIS2) {
810                 drawLine(t, td->ob->obmat[3], td->axismtx[2], 'z', DRAWLIGHT);
811         }
812         
813         td++;
814
815         for(i=1;i<t->total;i++,td++) {
816                 if (t->con.mode & CON_AXIS0) {
817                         drawLine(t, td->ob->obmat[3], td->axismtx[0], 'x', 0);
818                 }
819                 if (t->con.mode & CON_AXIS1) {
820                         drawLine(t, td->ob->obmat[3], td->axismtx[1], 'y', 0);
821                 }
822                 if (t->con.mode & CON_AXIS2) {
823                         drawLine(t, td->ob->obmat[3], td->axismtx[2], 'z', 0);
824                 }
825         }
826 }
827
828 /*--------------------- START / STOP CONSTRAINTS ---------------------- */
829
830 void startConstraint(TransInfo *t) {
831         t->con.mode |= CON_APPLY;
832         *t->con.text = ' ';
833         t->num.idx_max = MIN2(getConstraintSpaceDimension(t) - 1, t->idx_max);
834 }
835
836 void stopConstraint(TransInfo *t) {
837         t->con.mode &= ~(CON_APPLY|CON_SELECT);
838         *t->con.text = '\0';
839         t->num.idx_max = t->idx_max;
840 }
841
842 void getConstraintMatrix(TransInfo *t)
843 {
844         float mat[3][3];
845         Mat3Inv(t->con.imtx, t->con.mtx);
846         Mat3One(t->con.pmtx);
847
848         if (!(t->con.mode & CON_AXIS0)) {
849                 t->con.pmtx[0][0]               =
850                         t->con.pmtx[0][1]       =
851                         t->con.pmtx[0][2]       = 0.0f;
852         }
853
854         if (!(t->con.mode & CON_AXIS1)) {
855                 t->con.pmtx[1][0]               =
856                         t->con.pmtx[1][1]       =
857                         t->con.pmtx[1][2]       = 0.0f;
858         }
859
860         if (!(t->con.mode & CON_AXIS2)) {
861                 t->con.pmtx[2][0]               =
862                         t->con.pmtx[2][1]       =
863                         t->con.pmtx[2][2]       = 0.0f;
864         }
865
866         Mat3MulMat3(mat, t->con.pmtx, t->con.imtx);
867         Mat3MulMat3(t->con.pmtx, t->con.mtx, mat);
868 }
869
870 /*------------------------- MMB Select -------------------------------*/
871
872 void initSelectConstraint(TransInfo *t, float mtx[3][3])
873 {
874         Mat3CpyMat3(t->con.mtx, mtx);
875         t->con.mode |= CON_APPLY;
876         t->con.mode |= CON_SELECT;
877
878         setNearestAxis(t);
879         t->con.drawExtra = NULL;
880         t->con.applyVec = applyAxisConstraintVec;
881         t->con.applySize = applyAxisConstraintSize;
882         t->con.applyRot = applyAxisConstraintRot;
883 }
884
885 void selectConstraint(TransInfo *t) {
886         if (t->con.mode & CON_SELECT) {
887                 setNearestAxis(t);
888                 startConstraint(t);
889         }
890 }
891
892 void postSelectConstraint(TransInfo *t)
893 {
894         if (!(t->con.mode & CON_SELECT))
895                 return;
896
897         t->con.mode &= ~CON_AXIS0;
898         t->con.mode &= ~CON_AXIS1;
899         t->con.mode &= ~CON_AXIS2;
900         t->con.mode &= ~CON_SELECT;
901
902         setNearestAxis(t);
903
904         startConstraint(t);
905         t->redraw = 1;
906 }
907
908 static void setNearestAxis2d(TransInfo *t)
909 {
910         /* no correction needed... just use whichever one is lower */
911         if ( abs(t->mval[0]-t->con.imval[0]) < abs(t->mval[1]-t->con.imval[1]) ) {
912                 t->con.mode |= CON_AXIS1;
913                 sprintf(t->con.text, " along Y axis");
914         }
915         else {
916                 t->con.mode |= CON_AXIS0;
917                 sprintf(t->con.text, " along X axis");
918         }
919 }
920
921 static void setNearestAxis3d(TransInfo *t)
922 {
923         float zfac;
924         float mvec[3], axis[3], proj[3];
925         float len[3];
926         int i, icoord[2];
927         
928         /* calculate mouse movement */
929         mvec[0] = (float)(t->mval[0] - t->con.imval[0]);
930         mvec[1] = (float)(t->mval[1] - t->con.imval[1]);
931         mvec[2] = 0.0f;
932         
933         /* we need to correct axis length for the current zoomlevel of view,
934            this to prevent projected values to be clipped behind the camera
935            and to overflow the short integers.
936            The formula used is a bit stupid, just a simplification of the substraction
937            of two 2D points 30 pixels apart (that's the last factor in the formula) after
938            projecting them with window_to_3d_delta and then get the length of that vector.
939         */
940         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];
941         zfac = VecLength(t->persinv[0]) * 2.0f/t->ar->winx * zfac * 30.0f;
942
943         for (i = 0; i<3; i++) {
944                 VECCOPY(axis, t->con.mtx[i]);
945                 
946                 VecMulf(axis, zfac);
947                 /* now we can project to get window coordinate */
948                 VecAddf(axis, axis, t->con.center);
949                 projectIntView(t, axis, icoord);
950                 
951                 axis[0] = (float)(icoord[0] - t->center2d[0]);
952                 axis[1] = (float)(icoord[1] - t->center2d[1]);
953                 axis[2] = 0.0f;
954
955                 if (Normalize(axis) != 0.0f) {
956                         Projf(proj, mvec, axis);
957                         VecSubf(axis, mvec, proj);
958                         len[i] = Normalize(axis);
959                 }
960                 else {
961                         len[i] = 10000000000.0f;
962                 }
963         }
964
965         if (len[0] <= len[1] && len[0] <= len[2]) {
966                 if (t->modifiers & MOD_CONSTRAINT_PLANE) {
967                         t->con.mode |= (CON_AXIS1|CON_AXIS2);
968                         sprintf(t->con.text, " locking %s X axis", t->spacename);
969                 }
970                 else {
971                         t->con.mode |= CON_AXIS0;
972                         sprintf(t->con.text, " along %s X axis", t->spacename);
973                 }
974         }
975         else if (len[1] <= len[0] && len[1] <= len[2]) {
976                 if (t->modifiers & MOD_CONSTRAINT_PLANE) {
977                         t->con.mode |= (CON_AXIS0|CON_AXIS2);
978                         sprintf(t->con.text, " locking %s Y axis", t->spacename);
979                 }
980                 else {
981                         t->con.mode |= CON_AXIS1;
982                         sprintf(t->con.text, " along %s Y axis", t->spacename);
983                 }
984         }
985         else if (len[2] <= len[1] && len[2] <= len[0]) {
986                 if (t->modifiers & MOD_CONSTRAINT_PLANE) {
987                         t->con.mode |= (CON_AXIS0|CON_AXIS1);
988                         sprintf(t->con.text, " locking %s Z axis", t->spacename);
989                 }
990                 else {
991                         t->con.mode |= CON_AXIS2;
992                         sprintf(t->con.text, " along %s Z axis", t->spacename);
993                 }
994         }
995 }
996
997 void setNearestAxis(TransInfo *t)
998 {
999         /* clear any prior constraint flags */
1000         t->con.mode &= ~CON_AXIS0;
1001         t->con.mode &= ~CON_AXIS1;
1002         t->con.mode &= ~CON_AXIS2;
1003
1004         /* constraint setting - depends on spacetype */
1005         if (t->spacetype == SPACE_VIEW3D) {
1006                 /* 3d-view */
1007                 setNearestAxis3d(t);    
1008         }
1009         else {
1010                 /* assume that this means a 2D-Editor */
1011                 setNearestAxis2d(t);
1012         }
1013         
1014         getConstraintMatrix(t);
1015 }
1016
1017 /*-------------- HELPER FUNCTIONS ----------------*/
1018
1019 char constraintModeToChar(TransInfo *t) {
1020         if ((t->con.mode & CON_APPLY)==0) {
1021                 return '\0';
1022         }
1023         switch (t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2)) {
1024         case (CON_AXIS0):
1025         case (CON_AXIS1|CON_AXIS2):
1026                 return 'X';
1027         case (CON_AXIS1):
1028         case (CON_AXIS0|CON_AXIS2):
1029                 return 'Y';
1030         case (CON_AXIS2):
1031         case (CON_AXIS0|CON_AXIS1):
1032                 return 'Z';
1033         default:
1034                 return '\0';
1035         }
1036 }
1037
1038
1039 int isLockConstraint(TransInfo *t) {
1040         int mode = t->con.mode;
1041
1042         if ( (mode & (CON_AXIS0|CON_AXIS1)) == (CON_AXIS0|CON_AXIS1))
1043                 return 1;
1044
1045         if ( (mode & (CON_AXIS1|CON_AXIS2)) == (CON_AXIS1|CON_AXIS2))
1046                 return 1;
1047
1048         if ( (mode & (CON_AXIS0|CON_AXIS2)) == (CON_AXIS0|CON_AXIS2))
1049                 return 1;
1050
1051         return 0;
1052 }
1053
1054 /*
1055  * Returns the dimension of the constraint space.
1056  * 
1057  * For that reason, the flags always needs to be set to properly evaluate here,
1058  * even if they aren't actually used in the callback function. (Which could happen
1059  * for weird constraints not yet designed. Along a path for example.)
1060  */
1061
1062 int getConstraintSpaceDimension(TransInfo *t)
1063 {
1064         int n = 0;
1065
1066         if (t->con.mode & CON_AXIS0)
1067                 n++;
1068
1069         if (t->con.mode & CON_AXIS1)
1070                 n++;
1071
1072         if (t->con.mode & CON_AXIS2)
1073                 n++;
1074
1075         return n;
1076 /*
1077   Someone willing to do it criptically could do the following instead:
1078
1079   return t->con & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
1080         
1081   Based on the assumptions that the axis flags are one after the other and start at 1
1082 */
1083 }