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