0012334cf929876d3c3d989001e09ca733488889
[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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 /** \file blender/editors/transform/transform_constraints.c
31  *  \ingroup edtransform
32  */
33
34
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <math.h>
39
40 #ifndef WIN32
41 #include <unistd.h>
42 #else
43 #include <io.h>
44 #endif
45
46
47 #include "DNA_object_types.h"
48 #include "DNA_scene_types.h"
49 #include "DNA_screen_types.h"
50 #include "DNA_space_types.h"
51 #include "DNA_view3d_types.h"
52
53 #include "BIF_gl.h"
54 #include "BIF_glutil.h"
55
56 #include "BKE_context.h"
57
58
59 #include "ED_image.h"
60 #include "ED_view3d.h"
61
62 #include "BLI_math.h"
63 #include "BLI_utildefines.h"
64
65 //#include "blendef.h"
66 //
67 //#include "mydevice.h"
68
69 #include "UI_resources.h"
70
71
72 #include "transform.h"
73
74 static void drawObjectConstraint(TransInfo *t);
75
76 /* ************************** CONSTRAINTS ************************* */
77 static void constraintAutoValues(TransInfo *t, float vec[3])
78 {
79         int mode = t->con.mode;
80         if (mode & CON_APPLY)
81         {
82                 float nval = (t->flag & T_NULL_ONE)?1.0f:0.0f;
83
84                 if ((mode & CON_AXIS0) == 0)
85                 {
86                         vec[0] = nval;
87                 }
88                 if ((mode & CON_AXIS1) == 0)
89                 {
90                         vec[1] = nval;
91                 }
92                 if ((mode & CON_AXIS2) == 0)
93                 {
94                         vec[2] = nval;
95                 }
96         }
97 }
98
99 void constraintNumInput(TransInfo *t, float vec[3])
100 {
101         int mode = t->con.mode;
102         if (mode & CON_APPLY) {
103                 float nval = (t->flag & T_NULL_ONE)?1.0f:0.0f;
104
105                 if (getConstraintSpaceDimension(t) == 2) {
106                         int axis = mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
107                         if (axis == (CON_AXIS0|CON_AXIS1)) {
108                                 vec[0] = vec[0];
109                                 vec[1] = vec[1];
110                                 vec[2] = nval;
111                         }
112                         else if (axis == (CON_AXIS1|CON_AXIS2)) {
113                                 vec[2] = vec[1];
114                                 vec[1] = vec[0];
115                                 vec[0] = nval;
116                         }
117                         else if (axis == (CON_AXIS0|CON_AXIS2)) {
118                                 vec[0] = vec[0];
119                                 vec[2] = vec[1];
120                                 vec[1] = nval;
121                         }
122                 }
123                 else if (getConstraintSpaceDimension(t) == 1) {
124                         if (mode & CON_AXIS0) {
125                                 vec[0] = vec[0];
126                                 vec[1] = nval;
127                                 vec[2] = nval;
128                         }
129                         else if (mode & CON_AXIS1) {
130                                 vec[1] = vec[0];
131                                 vec[0] = nval;
132                                 vec[2] = nval;
133                         }
134                         else if (mode & CON_AXIS2) {
135                                 vec[2] = vec[0];
136                                 vec[0] = nval;
137                                 vec[1] = nval;
138                         }
139                 }
140         }
141 }
142
143 static void postConstraintChecks(TransInfo *t, float vec[3], float pvec[3]) {
144         int i = 0;
145
146         mul_m3_v3(t->con.imtx, vec);
147
148         snapGrid(t, vec);
149
150         if (t->num.flag & T_NULL_ONE) {
151                 if (!(t->con.mode & CON_AXIS0))
152                         vec[0] = 1.0f;
153
154                 if (!(t->con.mode & CON_AXIS1))
155                         vec[1] = 1.0f;
156
157                 if (!(t->con.mode & CON_AXIS2))
158                         vec[2] = 1.0f;
159         }
160
161         if (hasNumInput(&t->num)) {
162                 applyNumInput(&t->num, vec);
163                 removeAspectRatio(t, vec);
164                 constraintNumInput(t, vec);
165         }
166
167         /* autovalues is operator param, use that directly but not if snapping is forced */
168         if (t->flag & T_AUTOVALUES && (t->tsnap.status & SNAP_FORCED) == 0)
169         {
170                 mul_v3_m3v3(vec, t->con.imtx, t->auto_values);
171                 constraintAutoValues(t, vec);
172                 /* inverse transformation at the end */
173         }
174
175         if (t->con.mode & CON_AXIS0) {
176                 pvec[i++] = vec[0];
177         }
178         if (t->con.mode & CON_AXIS1) {
179                 pvec[i++] = vec[1];
180         }
181         if (t->con.mode & CON_AXIS2) {
182                 pvec[i++] = vec[2];
183         }
184
185         mul_m3_v3(t->con.mtx, vec);
186 }
187
188 static void axisProjection(TransInfo *t, float axis[3], float in[3], float out[3]) {
189         float norm[3], vec[3], factor, angle;
190
191         if(in[0]==0.0f && in[1]==0.0f && in[2]==0.0f)
192                 return;
193
194         angle = fabsf(angle_v3v3(axis, t->viewinv[2]));
195         if (angle > (float)M_PI / 2.0f) {
196                 angle = (float)M_PI - angle;
197         }
198         angle = RAD2DEGF(angle);
199
200         /* For when view is parallel to constraint... will cause NaNs otherwise
201            So we take vertical motion in 3D space and apply it to the
202            constraint axis. Nice for camera grab + MMB */
203         if(angle < 5.0f) {
204                 project_v3_v3v3(vec, in, t->viewinv[1]);
205                 factor = dot_v3v3(t->viewinv[1], vec) * 2.0f;
206                 /* since camera distance is quite relative, use quadratic relationship. holding shift can compensate */
207                 if(factor<0.0f) factor*= -factor;
208                 else factor*= factor;
209
210                 VECCOPY(out, axis);
211                 normalize_v3(out);
212                 mul_v3_fl(out, -factor);        /* -factor makes move down going backwards */
213         }
214         else {
215                 float v[3], i1[3], i2[3];
216                 float v2[3], v4[3];
217                 float norm_center[3];
218                 float plane[3];
219
220                 getViewVector(t, t->con.center, norm_center);
221                 cross_v3_v3v3(plane, norm_center, axis);
222
223                 project_v3_v3v3(vec, in, plane);
224                 sub_v3_v3v3(vec, in, vec);
225                 
226                 add_v3_v3v3(v, vec, t->con.center);
227                 getViewVector(t, v, norm);
228
229                 /* give arbitrary large value if projection is impossible */
230                 factor = dot_v3v3(axis, norm);
231                 if (1.0f - fabsf(factor) < 0.0002f) {
232                         VECCOPY(out, axis);
233                         if (factor > 0) {
234                                 mul_v3_fl(out, 1000000000.0f);
235                         } else {
236                                 mul_v3_fl(out, -1000000000.0f);
237                         }
238                 } else {
239                         add_v3_v3v3(v2, t->con.center, axis);
240                         add_v3_v3v3(v4, v, norm);
241                         
242                         isect_line_line_v3(t->con.center, v2, v, v4, i1, i2);
243                         
244                         sub_v3_v3v3(v, i2, v);
245         
246                         sub_v3_v3v3(out, i1, t->con.center);
247                 }
248         }
249 }
250
251 static void planeProjection(TransInfo *t, float in[3], float out[3]) {
252         float vec[3], factor, norm[3];
253
254         add_v3_v3v3(vec, in, t->con.center);
255         getViewVector(t, vec, norm);
256
257         sub_v3_v3v3(vec, out, in);
258
259         factor = dot_v3v3(vec, norm);
260         if (fabs(factor) <= 0.001) {
261                 return; /* prevent divide by zero */
262         }
263         factor = dot_v3v3(vec, vec) / factor;
264
265         VECCOPY(vec, norm);
266         mul_v3_fl(vec, factor);
267
268         add_v3_v3v3(out, in, vec);
269 }
270
271 /*
272  * Generic callback for constant spacial constraints applied to linear motion
273  *
274  * The IN vector in projected into the constrained space and then further
275  * projected along the view vector.
276  * (in perspective mode, the view vector is relative to the position on screen)
277  *
278  */
279
280 static void applyAxisConstraintVec(TransInfo *t, TransData *td, float in[3], float out[3], float pvec[3])
281 {
282         VECCOPY(out, in);
283         if (!td && t->con.mode & CON_APPLY) {
284                 mul_m3_v3(t->con.pmtx, out);
285
286                 // With snap, a projection is alright, no need to correct for view alignment
287                 if (!(t->tsnap.mode != SCE_SNAP_MODE_INCREMENT && activeSnap(t))) {
288                         if (getConstraintSpaceDimension(t) == 2) {
289                                 if (out[0] != 0.0f || out[1] != 0.0f || out[2] != 0.0f) {
290                                         planeProjection(t, in, out);
291                                 }
292                         }
293                         else if (getConstraintSpaceDimension(t) == 1) {
294                                 float c[3];
295
296                                 if (t->con.mode & CON_AXIS0) {
297                                         VECCOPY(c, t->con.mtx[0]);
298                                 }
299                                 else if (t->con.mode & CON_AXIS1) {
300                                         VECCOPY(c, t->con.mtx[1]);
301                                 }
302                                 else if (t->con.mode & CON_AXIS2) {
303                                         VECCOPY(c, t->con.mtx[2]);
304                                 }
305                                 axisProjection(t, c, in, out);
306                         }
307                 }
308                 postConstraintChecks(t, out, pvec);
309         }
310 }
311
312 /*
313  * Generic callback for object based spatial constraints applied to linear motion
314  *
315  * At first, the following is applied to the first data in the array
316  * The IN vector in projected into the constrained space and then further
317  * projected along the view vector.
318  * (in perspective mode, the view vector is relative to the position on screen)
319  *
320  * Further down, that vector is mapped to each data's space.
321  */
322
323 static void applyObjectConstraintVec(TransInfo *t, TransData *td, float in[3], float out[3], float pvec[3])
324 {
325         VECCOPY(out, in);
326         if (t->con.mode & CON_APPLY) {
327                 if (!td) {
328                         mul_m3_v3(t->con.pmtx, out);
329                         if (getConstraintSpaceDimension(t) == 2) {
330                                 if (out[0] != 0.0f || out[1] != 0.0f || out[2] != 0.0f) {
331                                         planeProjection(t, in, out);
332                                 }
333                         }
334                         else if (getConstraintSpaceDimension(t) == 1) {
335                                 float c[3];
336
337                                 if (t->con.mode & CON_AXIS0) {
338                                         VECCOPY(c, t->con.mtx[0]);
339                                 }
340                                 else if (t->con.mode & CON_AXIS1) {
341                                         VECCOPY(c, t->con.mtx[1]);
342                                 }
343                                 else if (t->con.mode & CON_AXIS2) {
344                                         VECCOPY(c, t->con.mtx[2]);
345                                 }
346                                 axisProjection(t, c, in, out);
347                         }
348                         postConstraintChecks(t, out, pvec);
349                         VECCOPY(out, pvec);
350                 }
351                 else {
352                         int i=0;
353
354                         out[0] = out[1] = out[2] = 0.0f;
355                         if (t->con.mode & CON_AXIS0) {
356                                 out[0] = in[i++];
357                         }
358                         if (t->con.mode & CON_AXIS1) {
359                                 out[1] = in[i++];
360                         }
361                         if (t->con.mode & CON_AXIS2) {
362                                 out[2] = in[i++];
363                         }
364                         mul_m3_v3(td->axismtx, out);
365                 }
366         }
367 }
368
369 /*
370  * Generic callback for constant spacial constraints applied to resize motion
371  *
372  *
373  */
374
375 static void applyAxisConstraintSize(TransInfo *t, TransData *td, float smat[3][3])
376 {
377         if (!td && t->con.mode & CON_APPLY) {
378                 float tmat[3][3];
379
380                 if (!(t->con.mode & CON_AXIS0)) {
381                         smat[0][0] = 1.0f;
382                 }
383                 if (!(t->con.mode & CON_AXIS1)) {
384                         smat[1][1] = 1.0f;
385                 }
386                 if (!(t->con.mode & CON_AXIS2)) {
387                         smat[2][2] = 1.0f;
388                 }
389
390                 mul_m3_m3m3(tmat, smat, t->con.imtx);
391                 mul_m3_m3m3(smat, t->con.mtx, tmat);
392         }
393 }
394
395 /*
396  * Callback for object based spacial constraints applied to resize motion
397  *
398  *
399  */
400
401 static void applyObjectConstraintSize(TransInfo *t, TransData *td, float smat[3][3])
402 {
403         if (td && t->con.mode & CON_APPLY) {
404                 float tmat[3][3];
405                 float imat[3][3];
406
407                 invert_m3_m3(imat, td->axismtx);
408
409                 if (!(t->con.mode & CON_AXIS0)) {
410                         smat[0][0] = 1.0f;
411                 }
412                 if (!(t->con.mode & CON_AXIS1)) {
413                         smat[1][1] = 1.0f;
414                 }
415                 if (!(t->con.mode & CON_AXIS2)) {
416                         smat[2][2] = 1.0f;
417                 }
418
419                 mul_m3_m3m3(tmat, smat, imat);
420                 mul_m3_m3m3(smat, td->axismtx, tmat);
421         }
422 }
423
424 /*
425  * Generic callback for constant spacial constraints applied to rotations
426  *
427  * The rotation axis is copied into VEC.
428  *
429  * In the case of single axis constraints, the rotation axis is directly the one constrained to.
430  * For planar constraints (2 axis), the rotation axis is the normal of the plane.
431  *
432  * The following only applies when CON_NOFLIP is not set.
433  * The vector is then modified to always point away from the screen (in global space)
434  * This insures that the rotation is always logically following the mouse.
435  * (ie: not doing counterclockwise rotations when the mouse moves clockwise).
436  */
437
438 static void applyAxisConstraintRot(TransInfo *t, TransData *td, float vec[3], float *angle)
439 {
440         if (!td && t->con.mode & CON_APPLY) {
441                 int mode = t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
442
443                 switch(mode) {
444                 case CON_AXIS0:
445                 case (CON_AXIS1|CON_AXIS2):
446                         VECCOPY(vec, t->con.mtx[0]);
447                         break;
448                 case CON_AXIS1:
449                 case (CON_AXIS0|CON_AXIS2):
450                         VECCOPY(vec, t->con.mtx[1]);
451                         break;
452                 case CON_AXIS2:
453                 case (CON_AXIS0|CON_AXIS1):
454                         VECCOPY(vec, t->con.mtx[2]);
455                         break;
456                 }
457                 /* don't flip axis if asked to or if num input */
458                 if (angle && (mode & CON_NOFLIP) == 0 && hasNumInput(&t->num) == 0) {
459                         if (dot_v3v3(vec, t->viewinv[2]) > 0.0f) {
460                                 *angle = -(*angle);
461                         }
462                 }
463         }
464 }
465
466 /*
467  * Callback for object based spacial constraints applied to rotations
468  *
469  * The rotation axis is copied into VEC.
470  *
471  * In the case of single axis constraints, the rotation axis is directly the one constrained to.
472  * For planar constraints (2 axis), the rotation axis is the normal of the plane.
473  *
474  * The following only applies when CON_NOFLIP is not set.
475  * The vector is then modified to always point away from the screen (in global space)
476  * This insures that the rotation is always logically following the mouse.
477  * (ie: not doing counterclockwise rotations when the mouse moves clockwise).
478  */
479
480 static void applyObjectConstraintRot(TransInfo *t, TransData *td, float vec[3], float *angle)
481 {
482         if (t->con.mode & CON_APPLY) {
483                 int mode = t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
484
485                 /* on setup call, use first object */
486                 if (td == NULL) {
487                         td= t->data;
488                 }
489
490                 switch(mode) {
491                 case CON_AXIS0:
492                 case (CON_AXIS1|CON_AXIS2):
493                         VECCOPY(vec, td->axismtx[0]);
494                         break;
495                 case CON_AXIS1:
496                 case (CON_AXIS0|CON_AXIS2):
497                         VECCOPY(vec, td->axismtx[1]);
498                         break;
499                 case CON_AXIS2:
500                 case (CON_AXIS0|CON_AXIS1):
501                         VECCOPY(vec, td->axismtx[2]);
502                         break;
503                 }
504                 if (angle && (mode & CON_NOFLIP) == 0 && hasNumInput(&t->num) == 0) {
505                         if (dot_v3v3(vec, t->viewinv[2]) > 0.0f) {
506                                 *angle = -(*angle);
507                         }
508                 }
509         }
510 }
511
512 /*--------------------- INTERNAL SETUP CALLS ------------------*/
513
514 void setConstraint(TransInfo *t, float space[3][3], int mode, const char text[]) {
515         strncpy(t->con.text + 1, text, 48);
516         copy_m3_m3(t->con.mtx, space);
517         t->con.mode = mode;
518         getConstraintMatrix(t);
519
520         startConstraint(t);
521
522         t->con.drawExtra = NULL;
523         t->con.applyVec = applyAxisConstraintVec;
524         t->con.applySize = applyAxisConstraintSize;
525         t->con.applyRot = applyAxisConstraintRot;
526         t->redraw = 1;
527 }
528
529 void setLocalConstraint(TransInfo *t, int mode, const char text[]) {
530         if (t->flag & T_EDIT) {
531                 float obmat[3][3];
532                 copy_m3_m4(obmat, t->scene->obedit->obmat);
533                 normalize_m3(obmat);
534                 setConstraint(t, obmat, mode, text);
535         }
536         else {
537                 if (t->total == 1) {
538                         setConstraint(t, t->data->axismtx, mode, text);
539                 }
540                 else {
541                         strncpy(t->con.text + 1, text, 48);
542                         copy_m3_m3(t->con.mtx, t->data->axismtx);
543                         t->con.mode = mode;
544                         getConstraintMatrix(t);
545
546                         startConstraint(t);
547
548                         t->con.drawExtra = drawObjectConstraint;
549                         t->con.applyVec = applyObjectConstraintVec;
550                         t->con.applySize = applyObjectConstraintSize;
551                         t->con.applyRot = applyObjectConstraintRot;
552                         t->redraw = 1;
553                 }
554         }
555 }
556
557 /*
558         Set the constraint according to the user defined orientation
559
560         ftext is a format string passed to sprintf. It will add the name of
561         the orientation where %s is (logically).
562 */
563 void setUserConstraint(TransInfo *t, short orientation, int mode, const char ftext[]) {
564         char text[40];
565
566         switch(orientation) {
567         case V3D_MANIP_GLOBAL:
568                 {
569                         float mtx[3][3]= MAT3_UNITY;
570                         sprintf(text, ftext, "global");
571                         setConstraint(t, mtx, mode, text);
572                 }
573                 break;
574         case V3D_MANIP_LOCAL:
575                 sprintf(text, ftext, "local");
576                 setLocalConstraint(t, mode, text);
577                 break;
578         case V3D_MANIP_NORMAL:
579                 sprintf(text, ftext, "normal");
580                 setConstraint(t, t->spacemtx, mode, text);
581                 break;
582         case V3D_MANIP_VIEW:
583                 sprintf(text, ftext, "view");
584                 setConstraint(t, t->spacemtx, mode, text);
585                 break;
586         case V3D_MANIP_GIMBAL:
587                 sprintf(text, ftext, "gimbal");
588                 setConstraint(t, t->spacemtx, mode, text);
589                 break;
590         default: /* V3D_MANIP_CUSTOM */
591                 sprintf(text, ftext, t->spacename);
592                 setConstraint(t, t->spacemtx, mode, text);
593                 break;
594         }
595
596         t->con.orientation = orientation;
597
598         t->con.mode |= CON_USER;
599 }
600
601 /*----------------- DRAWING CONSTRAINTS -------------------*/
602
603 void drawConstraint(TransInfo *t)
604 {
605         TransCon *tc = &(t->con);
606
607         if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE))
608                 return;
609         if (!(tc->mode & CON_APPLY))
610                 return;
611         if (t->flag & T_USES_MANIPULATOR)
612                 return;
613         if (t->flag & T_NO_CONSTRAINT)
614                 return;
615
616         /* nasty exception for Z constraint in camera view */
617         // TRANSFORM_FIX_ME
618 //      if((t->flag & T_OBJECT) && G.vd->camera==OBACT && G.vd->persp==V3D_CAMOB)
619 //              return;
620
621         if (tc->drawExtra) {
622                 tc->drawExtra(t);
623         }
624         else {
625                 if (tc->mode & CON_SELECT) {
626                         float vec[3];
627                         char col2[3] = {255,255,255};
628                         int depth_test_enabled;
629
630                         convertViewVec(t, vec, (short)(t->mval[0] - t->con.imval[0]), (short)(t->mval[1] - t->con.imval[1]));
631                         add_v3_v3(vec, tc->center);
632
633                         drawLine(t, tc->center, tc->mtx[0], 'X', 0);
634                         drawLine(t, tc->center, tc->mtx[1], 'Y', 0);
635                         drawLine(t, tc->center, tc->mtx[2], 'Z', 0);
636
637                         glColor3ubv((GLubyte *)col2);
638
639                         depth_test_enabled = glIsEnabled(GL_DEPTH_TEST);
640                         if(depth_test_enabled)
641                                 glDisable(GL_DEPTH_TEST);
642
643                         setlinestyle(1);
644                         glBegin(GL_LINE_STRIP);
645                                 glVertex3fv(tc->center);
646                                 glVertex3fv(vec);
647                         glEnd();
648                         setlinestyle(0);
649
650                         if(depth_test_enabled)
651                                 glEnable(GL_DEPTH_TEST);
652                 }
653
654                 if (tc->mode & CON_AXIS0) {
655                         drawLine(t, tc->center, tc->mtx[0], 'X', DRAWLIGHT);
656                 }
657                 if (tc->mode & CON_AXIS1) {
658                         drawLine(t, tc->center, tc->mtx[1], 'Y', DRAWLIGHT);
659                 }
660                 if (tc->mode & CON_AXIS2) {
661                         drawLine(t, tc->center, tc->mtx[2], 'Z', DRAWLIGHT);
662                 }
663         }
664 }
665
666 /* called from drawview.c, as an extra per-window draw option */
667 void drawPropCircle(const struct bContext *C, TransInfo *t)
668 {
669         if (t->flag & T_PROP_EDIT) {
670                 RegionView3D *rv3d = CTX_wm_region_view3d(C);
671                 float tmat[4][4], imat[4][4];
672                 float center[3];
673
674                 UI_ThemeColor(TH_GRID);
675
676                 if(t->spacetype == SPACE_VIEW3D && rv3d != NULL)
677                 {
678                         copy_m4_m4(tmat, rv3d->viewmat);
679                         invert_m4_m4(imat, tmat);
680                 }
681                 else
682                 {
683                         unit_m4(tmat);
684                         unit_m4(imat);
685                 }
686
687                 glPushMatrix();
688
689                 VECCOPY(center, t->center);
690
691                 if((t->spacetype == SPACE_VIEW3D) && t->obedit)
692                 {
693                         mul_m4_v3(t->obedit->obmat, center); /* because t->center is in local space */
694                 }
695                 else if(t->spacetype == SPACE_IMAGE)
696                 {
697                         float aspx, aspy;
698
699                         ED_space_image_uv_aspect(t->sa->spacedata.first, &aspx, &aspy);
700                         glScalef(1.0f/aspx, 1.0f/aspy, 1.0);
701                 }
702
703                 set_inverted_drawing(1);
704                 drawcircball(GL_LINE_LOOP, center, t->prop_size, imat);
705                 set_inverted_drawing(0);
706
707                 glPopMatrix();
708         }
709 }
710
711 static void drawObjectConstraint(TransInfo *t) {
712         int i;
713         TransData * td = t->data;
714
715         /* Draw the first one lighter because that's the one who controls the others.
716            Meaning the transformation is projected on that one and just copied on the others
717            constraint space.
718            In a nutshell, the object with light axis is controlled by the user and the others follow.
719            Without drawing the first light, users have little clue what they are doing.
720          */
721         if (t->con.mode & CON_AXIS0) {
722                 drawLine(t, td->ob->obmat[3], td->axismtx[0], 'X', DRAWLIGHT);
723         }
724         if (t->con.mode & CON_AXIS1) {
725                 drawLine(t, td->ob->obmat[3], td->axismtx[1], 'Y', DRAWLIGHT);
726         }
727         if (t->con.mode & CON_AXIS2) {
728                 drawLine(t, td->ob->obmat[3], td->axismtx[2], 'Z', DRAWLIGHT);
729         }
730
731         td++;
732
733         for(i=1;i<t->total;i++,td++) {
734                 if (t->con.mode & CON_AXIS0) {
735                         drawLine(t, td->ob->obmat[3], td->axismtx[0], 'X', 0);
736                 }
737                 if (t->con.mode & CON_AXIS1) {
738                         drawLine(t, td->ob->obmat[3], td->axismtx[1], 'Y', 0);
739                 }
740                 if (t->con.mode & CON_AXIS2) {
741                         drawLine(t, td->ob->obmat[3], td->axismtx[2], 'Z', 0);
742                 }
743         }
744 }
745
746 /*--------------------- START / STOP CONSTRAINTS ---------------------- */
747
748 void startConstraint(TransInfo *t) {
749         t->con.mode |= CON_APPLY;
750         *t->con.text = ' ';
751         t->num.idx_max = MIN2(getConstraintSpaceDimension(t) - 1, t->idx_max);
752 }
753
754 void stopConstraint(TransInfo *t) {
755         t->con.mode &= ~(CON_APPLY|CON_SELECT);
756         *t->con.text = '\0';
757         t->num.idx_max = t->idx_max;
758 }
759
760 void getConstraintMatrix(TransInfo *t)
761 {
762         float mat[3][3];
763         invert_m3_m3(t->con.imtx, t->con.mtx);
764         unit_m3(t->con.pmtx);
765
766         if (!(t->con.mode & CON_AXIS0)) {
767                 t->con.pmtx[0][0]               =
768                         t->con.pmtx[0][1]       =
769                         t->con.pmtx[0][2]       = 0.0f;
770         }
771
772         if (!(t->con.mode & CON_AXIS1)) {
773                 t->con.pmtx[1][0]               =
774                         t->con.pmtx[1][1]       =
775                         t->con.pmtx[1][2]       = 0.0f;
776         }
777
778         if (!(t->con.mode & CON_AXIS2)) {
779                 t->con.pmtx[2][0]               =
780                         t->con.pmtx[2][1]       =
781                         t->con.pmtx[2][2]       = 0.0f;
782         }
783
784         mul_m3_m3m3(mat, t->con.pmtx, t->con.imtx);
785         mul_m3_m3m3(t->con.pmtx, t->con.mtx, mat);
786 }
787
788 /*------------------------- MMB Select -------------------------------*/
789
790 void initSelectConstraint(TransInfo *t, float mtx[3][3])
791 {
792         copy_m3_m3(t->con.mtx, mtx);
793         t->con.mode |= CON_APPLY;
794         t->con.mode |= CON_SELECT;
795
796         setNearestAxis(t);
797         t->con.drawExtra = NULL;
798         t->con.applyVec = applyAxisConstraintVec;
799         t->con.applySize = applyAxisConstraintSize;
800         t->con.applyRot = applyAxisConstraintRot;
801 }
802
803 void selectConstraint(TransInfo *t) {
804         if (t->con.mode & CON_SELECT) {
805                 setNearestAxis(t);
806                 startConstraint(t);
807         }
808 }
809
810 void postSelectConstraint(TransInfo *t)
811 {
812         if (!(t->con.mode & CON_SELECT))
813                 return;
814
815         t->con.mode &= ~CON_AXIS0;
816         t->con.mode &= ~CON_AXIS1;
817         t->con.mode &= ~CON_AXIS2;
818         t->con.mode &= ~CON_SELECT;
819
820         setNearestAxis(t);
821
822         startConstraint(t);
823         t->redraw = 1;
824 }
825
826 static void setNearestAxis2d(TransInfo *t)
827 {
828         /* no correction needed... just use whichever one is lower */
829         if ( abs(t->mval[0]-t->con.imval[0]) < abs(t->mval[1]-t->con.imval[1]) ) {
830                 t->con.mode |= CON_AXIS1;
831                 sprintf(t->con.text, " along Y axis");
832         }
833         else {
834                 t->con.mode |= CON_AXIS0;
835                 sprintf(t->con.text, " along X axis");
836         }
837 }
838
839 static void setNearestAxis3d(TransInfo *t)
840 {
841         float zfac;
842         float mvec[3], axis[3], proj[3];
843         float len[3];
844         int i, icoord[2];
845
846         /* calculate mouse movement */
847         mvec[0] = (float)(t->mval[0] - t->con.imval[0]);
848         mvec[1] = (float)(t->mval[1] - t->con.imval[1]);
849         mvec[2] = 0.0f;
850
851         /* we need to correct axis length for the current zoomlevel of view,
852            this to prevent projected values to be clipped behind the camera
853            and to overflow the short integers.
854            The formula used is a bit stupid, just a simplification of the substraction
855            of two 2D points 30 pixels apart (that's the last factor in the formula) after
856            projecting them with window_to_3d_delta and then get the length of that vector.
857         */
858         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];
859         zfac = len_v3(t->persinv[0]) * 2.0f/t->ar->winx * zfac * 30.0f;
860
861         for (i = 0; i<3; i++) {
862                 VECCOPY(axis, t->con.mtx[i]);
863
864                 mul_v3_fl(axis, zfac);
865                 /* now we can project to get window coordinate */
866                 add_v3_v3(axis, t->con.center);
867                 projectIntView(t, axis, icoord);
868
869                 axis[0] = (float)(icoord[0] - t->center2d[0]);
870                 axis[1] = (float)(icoord[1] - t->center2d[1]);
871                 axis[2] = 0.0f;
872
873                  if (normalize_v3(axis) != 0.0f) {
874                         project_v3_v3v3(proj, mvec, axis);
875                         sub_v3_v3v3(axis, mvec, proj);
876                         len[i] = normalize_v3(axis);
877                 }
878                 else {
879                         len[i] = 10000000000.0f;
880                 }
881         }
882
883         if (len[0] <= len[1] && len[0] <= len[2]) {
884                 if (t->modifiers & MOD_CONSTRAINT_PLANE) {
885                         t->con.mode |= (CON_AXIS1|CON_AXIS2);
886                         sprintf(t->con.text, " locking %s X axis", t->spacename);
887                 }
888                 else {
889                         t->con.mode |= CON_AXIS0;
890                         sprintf(t->con.text, " along %s X axis", t->spacename);
891                 }
892         }
893         else if (len[1] <= len[0] && len[1] <= len[2]) {
894                 if (t->modifiers & MOD_CONSTRAINT_PLANE) {
895                         t->con.mode |= (CON_AXIS0|CON_AXIS2);
896                         sprintf(t->con.text, " locking %s Y axis", t->spacename);
897                 }
898                 else {
899                         t->con.mode |= CON_AXIS1;
900                         sprintf(t->con.text, " along %s Y axis", t->spacename);
901                 }
902         }
903         else if (len[2] <= len[1] && len[2] <= len[0]) {
904                 if (t->modifiers & MOD_CONSTRAINT_PLANE) {
905                         t->con.mode |= (CON_AXIS0|CON_AXIS1);
906                         sprintf(t->con.text, " locking %s Z axis", t->spacename);
907                 }
908                 else {
909                         t->con.mode |= CON_AXIS2;
910                         sprintf(t->con.text, " along %s Z axis", t->spacename);
911                 }
912         }
913 }
914
915 void setNearestAxis(TransInfo *t)
916 {
917         /* clear any prior constraint flags */
918         t->con.mode &= ~CON_AXIS0;
919         t->con.mode &= ~CON_AXIS1;
920         t->con.mode &= ~CON_AXIS2;
921
922         /* constraint setting - depends on spacetype */
923         if (t->spacetype == SPACE_VIEW3D) {
924                 /* 3d-view */
925                 setNearestAxis3d(t);
926         }
927         else {
928                 /* assume that this means a 2D-Editor */
929                 setNearestAxis2d(t);
930         }
931
932         getConstraintMatrix(t);
933 }
934
935 /*-------------- HELPER FUNCTIONS ----------------*/
936
937 char constraintModeToChar(TransInfo *t) {
938         if ((t->con.mode & CON_APPLY)==0) {
939                 return '\0';
940         }
941         switch (t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2)) {
942         case (CON_AXIS0):
943         case (CON_AXIS1|CON_AXIS2):
944                 return 'X';
945         case (CON_AXIS1):
946         case (CON_AXIS0|CON_AXIS2):
947                 return 'Y';
948         case (CON_AXIS2):
949         case (CON_AXIS0|CON_AXIS1):
950                 return 'Z';
951         default:
952                 return '\0';
953         }
954 }
955
956
957 int isLockConstraint(TransInfo *t) {
958         int mode = t->con.mode;
959
960         if ( (mode & (CON_AXIS0|CON_AXIS1)) == (CON_AXIS0|CON_AXIS1))
961                 return 1;
962
963         if ( (mode & (CON_AXIS1|CON_AXIS2)) == (CON_AXIS1|CON_AXIS2))
964                 return 1;
965
966         if ( (mode & (CON_AXIS0|CON_AXIS2)) == (CON_AXIS0|CON_AXIS2))
967                 return 1;
968
969         return 0;
970 }
971
972 /*
973  * Returns the dimension of the constraint space.
974  *
975  * For that reason, the flags always needs to be set to properly evaluate here,
976  * even if they aren't actually used in the callback function. (Which could happen
977  * for weird constraints not yet designed. Along a path for example.)
978  */
979
980 int getConstraintSpaceDimension(TransInfo *t)
981 {
982         int n = 0;
983
984         if (t->con.mode & CON_AXIS0)
985                 n++;
986
987         if (t->con.mode & CON_AXIS1)
988                 n++;
989
990         if (t->con.mode & CON_AXIS2)
991                 n++;
992
993         return n;
994 /*
995   Someone willing to do it criptically could do the following instead:
996
997   return t->con & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
998
999   Based on the assumptions that the axis flags are one after the other and start at 1
1000 */
1001 }