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