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