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