Fix/Cleanup: I18N: Bad usage of IFACE_ instead of TIP_.
[blender.git] / source / blender / editors / transform / transform_constraints.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup edtransform
22  */
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <math.h>
28
29 #include "DNA_object_types.h"
30 #include "DNA_scene_types.h"
31 #include "DNA_screen_types.h"
32 #include "DNA_space_types.h"
33 #include "DNA_view3d_types.h"
34
35 #include "BIF_glutil.h"
36
37 #include "GPU_immediate.h"
38 #include "GPU_matrix.h"
39 #include "GPU_state.h"
40
41 #include "BLI_math.h"
42 #include "BLI_utildefines.h"
43 #include "BLI_string.h"
44 #include "BLI_rect.h"
45
46 #include "BKE_context.h"
47
48 #include "ED_image.h"
49 #include "ED_view3d.h"
50
51 #include "BLT_translation.h"
52
53 #include "UI_resources.h"
54
55 #include "transform.h"
56
57 static void drawObjectConstraint(TransInfo *t);
58
59 /* ************************** CONSTRAINTS ************************* */
60 static void constraintAutoValues(TransInfo *t, float vec[3])
61 {
62   int mode = t->con.mode;
63   if (mode & CON_APPLY) {
64     float nval = (t->flag & T_NULL_ONE) ? 1.0f : 0.0f;
65
66     if ((mode & CON_AXIS0) == 0) {
67       vec[0] = nval;
68     }
69     if ((mode & CON_AXIS1) == 0) {
70       vec[1] = nval;
71     }
72     if ((mode & CON_AXIS2) == 0) {
73       vec[2] = nval;
74     }
75   }
76 }
77
78 void constraintNumInput(TransInfo *t, float vec[3])
79 {
80   int mode = t->con.mode;
81   if (mode & CON_APPLY) {
82     float nval = (t->flag & T_NULL_ONE) ? 1.0f : 0.0f;
83
84     const int dims = getConstraintSpaceDimension(t);
85     if (dims == 2) {
86       int axis = mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2);
87       if (axis == (CON_AXIS0 | CON_AXIS1)) {
88         /* vec[0] = vec[0]; */ /* same */
89         /* vec[1] = vec[1]; */ /* same */
90         vec[2] = nval;
91       }
92       else if (axis == (CON_AXIS1 | CON_AXIS2)) {
93         vec[2] = vec[1];
94         vec[1] = vec[0];
95         vec[0] = nval;
96       }
97       else if (axis == (CON_AXIS0 | CON_AXIS2)) {
98         /* vec[0] = vec[0]; */ /* same */
99         vec[2] = vec[1];
100         vec[1] = nval;
101       }
102     }
103     else if (dims == 1) {
104       if (mode & CON_AXIS0) {
105         /* vec[0] = vec[0]; */ /* same */
106         vec[1] = nval;
107         vec[2] = nval;
108       }
109       else if (mode & CON_AXIS1) {
110         vec[1] = vec[0];
111         vec[0] = nval;
112         vec[2] = nval;
113       }
114       else if (mode & CON_AXIS2) {
115         vec[2] = vec[0];
116         vec[0] = nval;
117         vec[1] = nval;
118       }
119     }
120   }
121 }
122
123 static void postConstraintChecks(TransInfo *t, float vec[3], float pvec[3])
124 {
125   int i = 0;
126
127   mul_m3_v3(t->con.imtx, vec);
128
129   snapGridIncrement(t, vec);
130
131   if (t->flag & T_NULL_ONE) {
132     if (!(t->con.mode & CON_AXIS0)) {
133       vec[0] = 1.0f;
134     }
135
136     if (!(t->con.mode & CON_AXIS1)) {
137       vec[1] = 1.0f;
138     }
139
140     if (!(t->con.mode & CON_AXIS2)) {
141       vec[2] = 1.0f;
142     }
143   }
144
145   if (applyNumInput(&t->num, vec)) {
146     constraintNumInput(t, vec);
147     removeAspectRatio(t, vec);
148   }
149
150   /* autovalues is operator param, use that directly but not if snapping is forced */
151   if (t->flag & T_AUTOVALUES && (t->tsnap.status & SNAP_FORCED) == 0) {
152     copy_v3_v3(vec, t->auto_values);
153     constraintAutoValues(t, vec);
154     /* inverse transformation at the end */
155   }
156
157   if (t->con.mode & CON_AXIS0) {
158     pvec[i++] = vec[0];
159   }
160   if (t->con.mode & CON_AXIS1) {
161     pvec[i++] = vec[1];
162   }
163   if (t->con.mode & CON_AXIS2) {
164     pvec[i++] = vec[2];
165   }
166
167   mul_m3_v3(t->con.mtx, vec);
168 }
169
170 static void viewAxisCorrectCenter(const TransInfo *t, float t_con_center[3])
171 {
172   if (t->spacetype == SPACE_VIEW3D) {
173     // View3D *v3d = t->sa->spacedata.first;
174     const float min_dist = 1.0f; /* v3d->clip_start; */
175     float dir[3];
176     float l;
177
178     sub_v3_v3v3(dir, t_con_center, t->viewinv[3]);
179     if (dot_v3v3(dir, t->viewinv[2]) < 0.0f) {
180       negate_v3(dir);
181     }
182     project_v3_v3v3(dir, dir, t->viewinv[2]);
183
184     l = len_v3(dir);
185
186     if (l < min_dist) {
187       float diff[3];
188       normalize_v3_v3_length(diff, t->viewinv[2], min_dist - l);
189       sub_v3_v3(t_con_center, diff);
190     }
191   }
192 }
193
194 /**
195  * Axis calculation taking the view into account, correcting view-aligned axis.
196  */
197 static void axisProjection(const TransInfo *t,
198                            const float axis[3],
199                            const float in[3],
200                            float out[3])
201 {
202   float norm[3], vec[3], factor, angle;
203   float t_con_center[3];
204
205   if (is_zero_v3(in)) {
206     return;
207   }
208
209   copy_v3_v3(t_con_center, t->center_global);
210
211   /* checks for center being too close to the view center */
212   viewAxisCorrectCenter(t, t_con_center);
213
214   angle = fabsf(angle_v3v3(axis, t->viewinv[2]));
215   if (angle > (float)M_PI_2) {
216     angle = (float)M_PI - angle;
217   }
218
219   /* For when view is parallel to constraint... will cause NaNs otherwise
220    * So we take vertical motion in 3D space and apply it to the
221    * constraint axis. Nice for camera grab + MMB */
222   if (angle < DEG2RADF(5.0f)) {
223     project_v3_v3v3(vec, in, t->viewinv[1]);
224     factor = dot_v3v3(t->viewinv[1], vec) * 2.0f;
225     /* Since camera distance is quite relative, use quadratic relationship.
226      * holding shift can compensate. */
227     if (factor < 0.0f) {
228       factor *= -factor;
229     }
230     else {
231       factor *= factor;
232     }
233
234     /* -factor makes move down going backwards */
235     normalize_v3_v3_length(out, axis, -factor);
236   }
237   else {
238     float v[3], i1[3], i2[3];
239     float v2[3], v4[3];
240     float norm_center[3];
241     float plane[3];
242
243     getViewVector(t, t_con_center, norm_center);
244     cross_v3_v3v3(plane, norm_center, axis);
245
246     project_v3_v3v3(vec, in, plane);
247     sub_v3_v3v3(vec, in, vec);
248
249     add_v3_v3v3(v, vec, t_con_center);
250     getViewVector(t, v, norm);
251
252     /* give arbitrary large value if projection is impossible */
253     factor = dot_v3v3(axis, norm);
254     if (1.0f - fabsf(factor) < 0.0002f) {
255       copy_v3_v3(out, axis);
256       if (factor > 0) {
257         mul_v3_fl(out, 1000000000.0f);
258       }
259       else {
260         mul_v3_fl(out, -1000000000.0f);
261       }
262     }
263     else {
264       add_v3_v3v3(v2, t_con_center, axis);
265       add_v3_v3v3(v4, v, norm);
266
267       isect_line_line_v3(t_con_center, v2, v, v4, i1, i2);
268
269       sub_v3_v3v3(v, i2, v);
270
271       sub_v3_v3v3(out, i1, t_con_center);
272
273       /* possible some values become nan when
274        * viewpoint and object are both zero */
275       if (!isfinite(out[0])) {
276         out[0] = 0.0f;
277       }
278       if (!isfinite(out[1])) {
279         out[1] = 0.0f;
280       }
281       if (!isfinite(out[2])) {
282         out[2] = 0.0f;
283       }
284     }
285   }
286 }
287
288 /**
289  * Return true if the 2x axis are both aligned when projected into the view.
290  * In this case, we can't usefully project the cursor onto the plane.
291  */
292 static bool isPlaneProjectionViewAligned(const TransInfo *t)
293 {
294   const float eps = 0.001f;
295   const float *constraint_vector[2];
296   int n = 0;
297   for (int i = 0; i < 3; i++) {
298     if (t->con.mode & (CON_AXIS0 << i)) {
299       constraint_vector[n++] = t->con.mtx[i];
300       if (n == 2) {
301         break;
302       }
303     }
304   }
305   BLI_assert(n == 2);
306
307   float view_to_plane[3], plane_normal[3];
308
309   getViewVector(t, t->center_global, view_to_plane);
310
311   cross_v3_v3v3(plane_normal, constraint_vector[0], constraint_vector[1]);
312   normalize_v3(plane_normal);
313
314   float factor = dot_v3v3(plane_normal, view_to_plane);
315   return fabsf(factor) < eps;
316 }
317
318 static void planeProjection(const TransInfo *t, const float in[3], float out[3])
319 {
320   float vec[3], factor, norm[3];
321
322   add_v3_v3v3(vec, in, t->center_global);
323   getViewVector(t, vec, norm);
324
325   sub_v3_v3v3(vec, out, in);
326
327   factor = dot_v3v3(vec, norm);
328   if (fabsf(factor) <= 0.001f) {
329     return; /* prevent divide by zero */
330   }
331   factor = dot_v3v3(vec, vec) / factor;
332
333   copy_v3_v3(vec, norm);
334   mul_v3_fl(vec, factor);
335
336   add_v3_v3v3(out, in, vec);
337 }
338
339 /*
340  * Generic callback for constant spatial constraints applied to linear motion
341  *
342  * The IN vector in projected into the constrained space and then further
343  * projected along the view vector.
344  * (in perspective mode, the view vector is relative to the position on screen)
345  */
346
347 static void applyAxisConstraintVec(TransInfo *t,
348                                    TransDataContainer *UNUSED(tc),
349                                    TransData *td,
350                                    const float in[3],
351                                    float out[3],
352                                    float pvec[3])
353 {
354   copy_v3_v3(out, in);
355   if (!td && t->con.mode & CON_APPLY) {
356     mul_m3_v3(t->con.pmtx, out);
357
358     // With snap, a projection is alright, no need to correct for view alignment
359     if (!validSnap(t)) {
360       const int dims = getConstraintSpaceDimension(t);
361       if (dims == 2) {
362         if (!is_zero_v3(out)) {
363           if (!isPlaneProjectionViewAligned(t)) {
364             planeProjection(t, in, out);
365           }
366         }
367       }
368       else if (dims == 1) {
369         float c[3];
370
371         if (t->con.mode & CON_AXIS0) {
372           copy_v3_v3(c, t->con.mtx[0]);
373         }
374         else if (t->con.mode & CON_AXIS1) {
375           copy_v3_v3(c, t->con.mtx[1]);
376         }
377         else if (t->con.mode & CON_AXIS2) {
378           copy_v3_v3(c, t->con.mtx[2]);
379         }
380         axisProjection(t, c, in, out);
381       }
382     }
383     postConstraintChecks(t, out, pvec);
384   }
385 }
386
387 /*
388  * Generic callback for object based spatial constraints applied to linear motion
389  *
390  * At first, the following is applied to the first data in the array
391  * The IN vector in projected into the constrained space and then further
392  * projected along the view vector.
393  * (in perspective mode, the view vector is relative to the position on screen)
394  *
395  * Further down, that vector is mapped to each data's space.
396  */
397
398 static void applyObjectConstraintVec(TransInfo *t,
399                                      TransDataContainer *tc,
400                                      TransData *td,
401                                      const float in[3],
402                                      float out[3],
403                                      float pvec[3])
404 {
405   copy_v3_v3(out, in);
406   if (t->con.mode & CON_APPLY) {
407     if (!td) {
408       mul_m3_v3(t->con.pmtx, out);
409
410       const int dims = getConstraintSpaceDimension(t);
411       if (dims == 2) {
412         if (!is_zero_v3(out)) {
413           if (!isPlaneProjectionViewAligned(t)) {
414             planeProjection(t, in, out);
415           }
416         }
417       }
418       else if (dims == 1) {
419         float c[3];
420
421         if (t->con.mode & CON_AXIS0) {
422           copy_v3_v3(c, t->con.mtx[0]);
423         }
424         else if (t->con.mode & CON_AXIS1) {
425           copy_v3_v3(c, t->con.mtx[1]);
426         }
427         else if (t->con.mode & CON_AXIS2) {
428           copy_v3_v3(c, t->con.mtx[2]);
429         }
430         axisProjection(t, c, in, out);
431       }
432       postConstraintChecks(t, out, pvec);
433       copy_v3_v3(out, pvec);
434     }
435     else {
436       int i = 0;
437
438       out[0] = out[1] = out[2] = 0.0f;
439       if (t->con.mode & CON_AXIS0) {
440         out[0] = in[i++];
441       }
442       if (t->con.mode & CON_AXIS1) {
443         out[1] = in[i++];
444       }
445       if (t->con.mode & CON_AXIS2) {
446         out[2] = in[i++];
447       }
448
449       mul_m3_v3(td->axismtx, out);
450       if (t->flag & T_EDIT) {
451         mul_m3_v3(tc->mat3_unit, out);
452       }
453     }
454   }
455 }
456
457 /*
458  * Generic callback for constant spatial constraints applied to resize motion
459  */
460
461 static void applyAxisConstraintSize(TransInfo *t,
462                                     TransDataContainer *UNUSED(tc),
463                                     TransData *td,
464                                     float smat[3][3])
465 {
466   if (!td && t->con.mode & CON_APPLY) {
467     float tmat[3][3];
468
469     if (!(t->con.mode & CON_AXIS0)) {
470       smat[0][0] = 1.0f;
471     }
472     if (!(t->con.mode & CON_AXIS1)) {
473       smat[1][1] = 1.0f;
474     }
475     if (!(t->con.mode & CON_AXIS2)) {
476       smat[2][2] = 1.0f;
477     }
478
479     mul_m3_m3m3(tmat, smat, t->con.imtx);
480     mul_m3_m3m3(smat, t->con.mtx, tmat);
481   }
482 }
483
484 /*
485  * Callback for object based spatial constraints applied to resize motion
486  */
487
488 static void applyObjectConstraintSize(TransInfo *t,
489                                       TransDataContainer *tc,
490                                       TransData *td,
491                                       float smat[3][3])
492 {
493   if (td && t->con.mode & CON_APPLY) {
494     float tmat[3][3];
495     float imat[3][3];
496
497     invert_m3_m3(imat, td->axismtx);
498
499     if (!(t->con.mode & CON_AXIS0)) {
500       smat[0][0] = 1.0f;
501     }
502     if (!(t->con.mode & CON_AXIS1)) {
503       smat[1][1] = 1.0f;
504     }
505     if (!(t->con.mode & CON_AXIS2)) {
506       smat[2][2] = 1.0f;
507     }
508
509     mul_m3_m3m3(tmat, smat, imat);
510     if (t->flag & T_EDIT) {
511       mul_m3_m3m3(smat, tc->mat3_unit, smat);
512     }
513     mul_m3_m3m3(smat, td->axismtx, tmat);
514   }
515 }
516
517 /*
518  * Generic callback for constant spatial constraints applied to rotations
519  *
520  * The rotation axis is copied into VEC.
521  *
522  * In the case of single axis constraints, the rotation axis is directly the one constrained to.
523  * For planar constraints (2 axis), the rotation axis is the normal of the plane.
524  *
525  * The following only applies when CON_NOFLIP is not set.
526  * The vector is then modified to always point away from the screen (in global space)
527  * This insures that the rotation is always logically following the mouse.
528  * (ie: not doing counterclockwise rotations when the mouse moves clockwise).
529  */
530
531 static void applyAxisConstraintRot(
532     TransInfo *t, TransDataContainer *UNUSED(tc), TransData *td, float vec[3], float *angle)
533 {
534   if (!td && t->con.mode & CON_APPLY) {
535     int mode = t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2);
536
537     switch (mode) {
538       case CON_AXIS0:
539       case (CON_AXIS1 | CON_AXIS2):
540         copy_v3_v3(vec, t->con.mtx[0]);
541         break;
542       case CON_AXIS1:
543       case (CON_AXIS0 | CON_AXIS2):
544         copy_v3_v3(vec, t->con.mtx[1]);
545         break;
546       case CON_AXIS2:
547       case (CON_AXIS0 | CON_AXIS1):
548         copy_v3_v3(vec, t->con.mtx[2]);
549         break;
550     }
551     /* don't flip axis if asked to or if num input */
552     if (angle && (mode & CON_NOFLIP) == 0 && hasNumInput(&t->num) == 0) {
553       if (dot_v3v3(vec, t->viewinv[2]) > 0.0f) {
554         *angle = -(*angle);
555       }
556     }
557   }
558 }
559
560 /*
561  * Callback for object based spatial constraints applied to rotations
562  *
563  * The rotation axis is copied into VEC.
564  *
565  * In the case of single axis constraints, the rotation axis is directly the one constrained to.
566  * For planar constraints (2 axis), the rotation axis is the normal of the plane.
567  *
568  * The following only applies when CON_NOFLIP is not set.
569  * The vector is then modified to always point away from the screen (in global space)
570  * This insures that the rotation is always logically following the mouse.
571  * (ie: not doing counterclockwise rotations when the mouse moves clockwise).
572  */
573
574 static void applyObjectConstraintRot(
575     TransInfo *t, TransDataContainer *tc, TransData *td, float vec[3], float *angle)
576 {
577   if (t->con.mode & CON_APPLY) {
578     int mode = t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2);
579     float tmp_axismtx[3][3];
580     float(*axismtx)[3];
581
582     /* on setup call, use first object */
583     if (td == NULL) {
584       BLI_assert(tc == NULL);
585       tc = TRANS_DATA_CONTAINER_FIRST_OK(t);
586       td = tc->data;
587     }
588
589     if (t->flag & T_EDIT) {
590       mul_m3_m3m3(tmp_axismtx, tc->mat3_unit, td->axismtx);
591       axismtx = tmp_axismtx;
592     }
593     else {
594       axismtx = td->axismtx;
595     }
596
597     switch (mode) {
598       case CON_AXIS0:
599       case (CON_AXIS1 | CON_AXIS2):
600         copy_v3_v3(vec, axismtx[0]);
601         break;
602       case CON_AXIS1:
603       case (CON_AXIS0 | CON_AXIS2):
604         copy_v3_v3(vec, axismtx[1]);
605         break;
606       case CON_AXIS2:
607       case (CON_AXIS0 | CON_AXIS1):
608         copy_v3_v3(vec, axismtx[2]);
609         break;
610     }
611     if (angle && (mode & CON_NOFLIP) == 0 && hasNumInput(&t->num) == 0) {
612       if (dot_v3v3(vec, t->viewinv[2]) > 0.0f) {
613         *angle = -(*angle);
614       }
615     }
616   }
617 }
618
619 /*--------------------- INTERNAL SETUP CALLS ------------------*/
620
621 void setConstraint(TransInfo *t, float space[3][3], int mode, const char text[])
622 {
623   BLI_strncpy(t->con.text + 1, text, sizeof(t->con.text) - 1);
624   copy_m3_m3(t->con.mtx, space);
625   t->con.mode = mode;
626   getConstraintMatrix(t);
627
628   startConstraint(t);
629
630   t->con.drawExtra = NULL;
631   t->con.applyVec = applyAxisConstraintVec;
632   t->con.applySize = applyAxisConstraintSize;
633   t->con.applyRot = applyAxisConstraintRot;
634   t->redraw = TREDRAW_HARD;
635 }
636
637 /* applies individual td->axismtx constraints */
638 void setAxisMatrixConstraint(TransInfo *t, int mode, const char text[])
639 {
640   TransDataContainer *tc = t->data_container;
641   if (t->data_len_all == 1) {
642     float axismtx[3][3];
643     if (t->flag & T_EDIT) {
644       mul_m3_m3m3(axismtx, tc->mat3_unit, tc->data->axismtx);
645     }
646     else {
647       copy_m3_m3(axismtx, tc->data->axismtx);
648     }
649
650     setConstraint(t, axismtx, mode, text);
651   }
652   else {
653     BLI_strncpy(t->con.text + 1, text, sizeof(t->con.text) - 1);
654     copy_m3_m3(t->con.mtx, tc->data->axismtx);
655     t->con.mode = mode;
656     getConstraintMatrix(t);
657
658     startConstraint(t);
659
660     t->con.drawExtra = drawObjectConstraint;
661     t->con.applyVec = applyObjectConstraintVec;
662     t->con.applySize = applyObjectConstraintSize;
663     t->con.applyRot = applyObjectConstraintRot;
664     t->redraw = TREDRAW_HARD;
665   }
666 }
667
668 void setLocalConstraint(TransInfo *t, int mode, const char text[])
669 {
670   /* edit-mode now allows local transforms too */
671   if (t->flag & T_EDIT) {
672     /* Use the active (first) edit object. */
673     TransDataContainer *tc = t->data_container;
674     setConstraint(t, tc->mat3_unit, mode, text);
675   }
676   else {
677     setAxisMatrixConstraint(t, mode, text);
678   }
679 }
680
681 /*
682  * Set the constraint according to the user defined orientation
683  *
684  * ftext is a format string passed to BLI_snprintf. It will add the name of
685  * the orientation where %s is (logically).
686  */
687 void setUserConstraint(TransInfo *t, short orientation, int mode, const char ftext[])
688 {
689   char text[256];
690
691   switch (orientation) {
692     case V3D_ORIENT_GLOBAL: {
693       float mtx[3][3];
694       BLI_snprintf(text, sizeof(text), ftext, TIP_("global"));
695       unit_m3(mtx);
696       setConstraint(t, mtx, mode, text);
697       break;
698     }
699     case V3D_ORIENT_LOCAL:
700       BLI_snprintf(text, sizeof(text), ftext, TIP_("local"));
701       setLocalConstraint(t, mode, text);
702       break;
703     case V3D_ORIENT_NORMAL:
704       BLI_snprintf(text, sizeof(text), ftext, TIP_("normal"));
705       if (checkUseAxisMatrix(t)) {
706         setAxisMatrixConstraint(t, mode, text);
707       }
708       else {
709         setConstraint(t, t->spacemtx, mode, text);
710       }
711       break;
712     case V3D_ORIENT_VIEW:
713       BLI_snprintf(text, sizeof(text), ftext, TIP_("view"));
714       setConstraint(t, t->spacemtx, mode, text);
715       break;
716     case V3D_ORIENT_CURSOR:
717       BLI_snprintf(text, sizeof(text), ftext, TIP_("cursor"));
718       setConstraint(t, t->spacemtx, mode, text);
719       break;
720     case V3D_ORIENT_GIMBAL:
721       BLI_snprintf(text, sizeof(text), ftext, TIP_("gimbal"));
722       setConstraint(t, t->spacemtx, mode, text);
723       break;
724     case V3D_ORIENT_CUSTOM_MATRIX:
725       BLI_snprintf(text, sizeof(text), ftext, TIP_("custom matrix"));
726       setConstraint(t, t->spacemtx, mode, text);
727       break;
728     case V3D_ORIENT_CUSTOM: {
729       char orientation_str[128];
730       BLI_snprintf(orientation_str,
731                    sizeof(orientation_str),
732                    "%s \"%s\"",
733                    TIP_("custom orientation"),
734                    t->orientation.custom->name);
735       BLI_snprintf(text, sizeof(text), ftext, orientation_str);
736       setConstraint(t, t->spacemtx, mode, text);
737       break;
738     }
739   }
740
741   t->con.orientation = orientation;
742
743   t->con.mode |= CON_USER;
744 }
745
746 /*----------------- DRAWING CONSTRAINTS -------------------*/
747
748 void drawConstraint(TransInfo *t)
749 {
750   TransCon *tc = &(t->con);
751
752   if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_NODE)) {
753     return;
754   }
755   if (!(tc->mode & CON_APPLY)) {
756     return;
757   }
758   if (t->flag & T_NO_CONSTRAINT) {
759     return;
760   }
761
762   if (tc->drawExtra) {
763     tc->drawExtra(t);
764   }
765   else {
766     if (tc->mode & CON_SELECT) {
767       float vec[3];
768       int depth_test_enabled;
769
770       convertViewVec(t, vec, (t->mval[0] - t->con.imval[0]), (t->mval[1] - t->con.imval[1]));
771       add_v3_v3(vec, t->center_global);
772
773       drawLine(t, t->center_global, tc->mtx[0], 'X', 0);
774       drawLine(t, t->center_global, tc->mtx[1], 'Y', 0);
775       drawLine(t, t->center_global, tc->mtx[2], 'Z', 0);
776
777       depth_test_enabled = GPU_depth_test_enabled();
778       if (depth_test_enabled) {
779         GPU_depth_test(false);
780       }
781
782       const uint shdr_pos = GPU_vertformat_attr_add(
783           immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
784
785       immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
786
787       float viewport_size[4];
788       GPU_viewport_size_get_f(viewport_size);
789       immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
790
791       immUniform1i("colors_len", 0); /* "simple" mode */
792       immUniformColor4f(1.0f, 1.0f, 1.0f, 1.0f);
793       immUniform1f("dash_width", 2.0f);
794       immUniform1f("dash_factor", 0.5f);
795
796       immBegin(GPU_PRIM_LINES, 2);
797       immVertex3fv(shdr_pos, t->center_global);
798       immVertex3fv(shdr_pos, vec);
799       immEnd();
800
801       immUnbindProgram();
802
803       if (depth_test_enabled) {
804         GPU_depth_test(true);
805       }
806     }
807
808     if (tc->mode & CON_AXIS0) {
809       drawLine(t, t->center_global, tc->mtx[0], 'X', DRAWLIGHT);
810     }
811     if (tc->mode & CON_AXIS1) {
812       drawLine(t, t->center_global, tc->mtx[1], 'Y', DRAWLIGHT);
813     }
814     if (tc->mode & CON_AXIS2) {
815       drawLine(t, t->center_global, tc->mtx[2], 'Z', DRAWLIGHT);
816     }
817   }
818 }
819
820 /* called from drawview.c, as an extra per-window draw option */
821 void drawPropCircle(const struct bContext *C, TransInfo *t)
822 {
823   if (t->flag & T_PROP_EDIT) {
824     RegionView3D *rv3d = CTX_wm_region_view3d(C);
825     float tmat[4][4], imat[4][4];
826     int depth_test_enabled;
827
828     if (t->spacetype == SPACE_VIEW3D && rv3d != NULL) {
829       copy_m4_m4(tmat, rv3d->viewmat);
830       invert_m4_m4(imat, tmat);
831     }
832     else {
833       unit_m4(tmat);
834       unit_m4(imat);
835     }
836
837     GPU_matrix_push();
838
839     if (t->spacetype == SPACE_VIEW3D) {
840       /* pass */
841     }
842     else if (t->spacetype == SPACE_IMAGE) {
843       GPU_matrix_scale_2f(1.0f / t->aspect[0], 1.0f / t->aspect[1]);
844     }
845     else if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_ACTION)) {
846       /* only scale y */
847       rcti *mask = &t->ar->v2d.mask;
848       rctf *datamask = &t->ar->v2d.cur;
849       float xsize = BLI_rctf_size_x(datamask);
850       float ysize = BLI_rctf_size_y(datamask);
851       float xmask = BLI_rcti_size_x(mask);
852       float ymask = BLI_rcti_size_y(mask);
853       GPU_matrix_scale_2f(1.0f, (ysize / xsize) * (xmask / ymask));
854     }
855
856     depth_test_enabled = GPU_depth_test_enabled();
857     if (depth_test_enabled) {
858       GPU_depth_test(false);
859     }
860
861     uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
862
863     immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
864     immUniformThemeColor(TH_GRID);
865
866     set_inverted_drawing(1);
867     imm_drawcircball(t->center_global, t->prop_size, imat, pos);
868     set_inverted_drawing(0);
869
870     immUnbindProgram();
871
872     if (depth_test_enabled) {
873       GPU_depth_test(true);
874     }
875
876     GPU_matrix_pop();
877   }
878 }
879
880 static void drawObjectConstraint(TransInfo *t)
881 {
882   /* Draw the first one lighter because that's the one who controls the others.
883    * Meaning the transformation is projected on that one and just copied on the others
884    * constraint space.
885    * In a nutshell, the object with light axis is controlled by the user and the others follow.
886    * Without drawing the first light, users have little clue what they are doing.
887    */
888   short options = DRAWLIGHT;
889   int i;
890   float tmp_axismtx[3][3];
891
892   FOREACH_TRANS_DATA_CONTAINER (t, tc) {
893     TransData *td = tc->data;
894     for (i = 0; i < tc->data_len; i++, td++) {
895       float co[3];
896       float(*axismtx)[3];
897
898       if (t->flag & T_PROP_EDIT) {
899         /* we're sorted, so skip the rest */
900         if (td->factor == 0.0f) {
901           break;
902         }
903       }
904
905       if (t->options & CTX_GPENCIL_STROKES) {
906         /* only draw a constraint line for one point, otherwise we can't see anything */
907         if ((options & DRAWLIGHT) == 0) {
908           break;
909         }
910       }
911
912       if (t->flag & T_OBJECT) {
913         copy_v3_v3(co, td->ob->obmat[3]);
914         axismtx = td->axismtx;
915       }
916       else if (t->flag & T_EDIT) {
917         mul_v3_m4v3(co, tc->mat, td->center);
918
919         mul_m3_m3m3(tmp_axismtx, tc->mat3_unit, td->axismtx);
920         axismtx = tmp_axismtx;
921       }
922       else if (t->flag & T_POSE) {
923         mul_v3_m4v3(co, tc->mat, td->center);
924         axismtx = td->axismtx;
925       }
926       else {
927         copy_v3_v3(co, td->center);
928         axismtx = td->axismtx;
929       }
930
931       if (t->con.mode & CON_AXIS0) {
932         drawLine(t, co, axismtx[0], 'X', options);
933       }
934       if (t->con.mode & CON_AXIS1) {
935         drawLine(t, co, axismtx[1], 'Y', options);
936       }
937       if (t->con.mode & CON_AXIS2) {
938         drawLine(t, co, axismtx[2], 'Z', options);
939       }
940       options &= ~DRAWLIGHT;
941     }
942   }
943 }
944
945 /*--------------------- START / STOP CONSTRAINTS ---------------------- */
946
947 void startConstraint(TransInfo *t)
948 {
949   t->con.mode |= CON_APPLY;
950   *t->con.text = ' ';
951   t->num.idx_max = min_ii(getConstraintSpaceDimension(t) - 1, t->idx_max);
952 }
953
954 void stopConstraint(TransInfo *t)
955 {
956   t->con.mode &= ~(CON_APPLY | CON_SELECT);
957   *t->con.text = '\0';
958   t->num.idx_max = t->idx_max;
959 }
960
961 void getConstraintMatrix(TransInfo *t)
962 {
963   float mat[3][3];
964   invert_m3_m3(t->con.imtx, t->con.mtx);
965   unit_m3(t->con.pmtx);
966
967   if (!(t->con.mode & CON_AXIS0)) {
968     zero_v3(t->con.pmtx[0]);
969   }
970
971   if (!(t->con.mode & CON_AXIS1)) {
972     zero_v3(t->con.pmtx[1]);
973   }
974
975   if (!(t->con.mode & CON_AXIS2)) {
976     zero_v3(t->con.pmtx[2]);
977   }
978
979   mul_m3_m3m3(mat, t->con.pmtx, t->con.imtx);
980   mul_m3_m3m3(t->con.pmtx, t->con.mtx, mat);
981 }
982
983 /*------------------------- MMB Select -------------------------------*/
984
985 void initSelectConstraint(TransInfo *t, float mtx[3][3])
986 {
987   copy_m3_m3(t->con.mtx, mtx);
988   t->con.mode |= CON_APPLY;
989   t->con.mode |= CON_SELECT;
990
991   setNearestAxis(t);
992   t->con.drawExtra = NULL;
993   t->con.applyVec = applyAxisConstraintVec;
994   t->con.applySize = applyAxisConstraintSize;
995   t->con.applyRot = applyAxisConstraintRot;
996 }
997
998 void selectConstraint(TransInfo *t)
999 {
1000   if (t->con.mode & CON_SELECT) {
1001     setNearestAxis(t);
1002     startConstraint(t);
1003   }
1004 }
1005
1006 void postSelectConstraint(TransInfo *t)
1007 {
1008   if (!(t->con.mode & CON_SELECT)) {
1009     return;
1010   }
1011
1012   t->con.mode &= ~CON_AXIS0;
1013   t->con.mode &= ~CON_AXIS1;
1014   t->con.mode &= ~CON_AXIS2;
1015   t->con.mode &= ~CON_SELECT;
1016
1017   setNearestAxis(t);
1018
1019   startConstraint(t);
1020   t->redraw = TREDRAW_HARD;
1021 }
1022
1023 static void setNearestAxis2d(TransInfo *t)
1024 {
1025   /* no correction needed... just use whichever one is lower */
1026   if (abs(t->mval[0] - t->con.imval[0]) < abs(t->mval[1] - t->con.imval[1])) {
1027     t->con.mode |= CON_AXIS1;
1028     BLI_strncpy(t->con.text, TIP_(" along Y axis"), sizeof(t->con.text));
1029   }
1030   else {
1031     t->con.mode |= CON_AXIS0;
1032     BLI_strncpy(t->con.text, TIP_(" along X axis"), sizeof(t->con.text));
1033   }
1034 }
1035
1036 static void setNearestAxis3d(TransInfo *t)
1037 {
1038   float zfac;
1039   float mvec[3], proj[3];
1040   float len[3];
1041   int i;
1042
1043   /* calculate mouse movement */
1044   mvec[0] = (float)(t->mval[0] - t->con.imval[0]);
1045   mvec[1] = (float)(t->mval[1] - t->con.imval[1]);
1046   mvec[2] = 0.0f;
1047
1048   /* We need to correct axis length for the current zoom-level of view,
1049    * this to prevent projected values to be clipped behind the camera
1050    * and to overflow the short integers.
1051    * The formula used is a bit stupid, just a simplification of the subtraction
1052    * of two 2D points 30 pixels apart (that's the last factor in the formula) after
1053    * projecting them with ED_view3d_win_to_delta and then get the length of that vector.
1054    */
1055   zfac = mul_project_m4_v3_zfac(t->persmat, t->center_global);
1056   zfac = len_v3(t->persinv[0]) * 2.0f / t->ar->winx * zfac * 30.0f;
1057
1058   for (i = 0; i < 3; i++) {
1059     float axis[3], axis_2d[2];
1060
1061     copy_v3_v3(axis, t->con.mtx[i]);
1062
1063     mul_v3_fl(axis, zfac);
1064     /* now we can project to get window coordinate */
1065     add_v3_v3(axis, t->center_global);
1066     projectFloatView(t, axis, axis_2d);
1067
1068     sub_v2_v2v2(axis, axis_2d, t->center2d);
1069     axis[2] = 0.0f;
1070
1071     if (normalize_v3(axis) > 1e-3f) {
1072       project_v3_v3v3(proj, mvec, axis);
1073       sub_v3_v3v3(axis, mvec, proj);
1074       len[i] = normalize_v3(axis);
1075     }
1076     else {
1077       len[i] = 1e10f;
1078     }
1079   }
1080
1081   if (len[0] <= len[1] && len[0] <= len[2]) {
1082     if (t->modifiers & MOD_CONSTRAINT_PLANE) {
1083       t->con.mode |= (CON_AXIS1 | CON_AXIS2);
1084       BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s X axis"), t->spacename);
1085     }
1086     else {
1087       t->con.mode |= CON_AXIS0;
1088       BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" along %s X axis"), t->spacename);
1089     }
1090   }
1091   else if (len[1] <= len[0] && len[1] <= len[2]) {
1092     if (t->modifiers & MOD_CONSTRAINT_PLANE) {
1093       t->con.mode |= (CON_AXIS0 | CON_AXIS2);
1094       BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s Y axis"), t->spacename);
1095     }
1096     else {
1097       t->con.mode |= CON_AXIS1;
1098       BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" along %s Y axis"), t->spacename);
1099     }
1100   }
1101   else if (len[2] <= len[1] && len[2] <= len[0]) {
1102     if (t->modifiers & MOD_CONSTRAINT_PLANE) {
1103       t->con.mode |= (CON_AXIS0 | CON_AXIS1);
1104       BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s Z axis"), t->spacename);
1105     }
1106     else {
1107       t->con.mode |= CON_AXIS2;
1108       BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" along %s Z axis"), t->spacename);
1109     }
1110   }
1111 }
1112
1113 void setNearestAxis(TransInfo *t)
1114 {
1115   /* clear any prior constraint flags */
1116   t->con.mode &= ~CON_AXIS0;
1117   t->con.mode &= ~CON_AXIS1;
1118   t->con.mode &= ~CON_AXIS2;
1119
1120   /* constraint setting - depends on spacetype */
1121   if (t->spacetype == SPACE_VIEW3D) {
1122     /* 3d-view */
1123     setNearestAxis3d(t);
1124   }
1125   else {
1126     /* assume that this means a 2D-Editor */
1127     setNearestAxis2d(t);
1128   }
1129
1130   getConstraintMatrix(t);
1131 }
1132
1133 /*-------------- HELPER FUNCTIONS ----------------*/
1134
1135 int constraintModeToIndex(const TransInfo *t)
1136 {
1137   if ((t->con.mode & CON_APPLY) == 0) {
1138     return -1;
1139   }
1140   switch (t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2)) {
1141     case (CON_AXIS0):
1142     case (CON_AXIS1 | CON_AXIS2):
1143       return 0;
1144     case (CON_AXIS1):
1145     case (CON_AXIS0 | CON_AXIS2):
1146       return 1;
1147     case (CON_AXIS2):
1148     case (CON_AXIS0 | CON_AXIS1):
1149       return 2;
1150     default:
1151       return -1;
1152   }
1153 }
1154
1155 char constraintModeToChar(const TransInfo *t)
1156 {
1157   int index = constraintModeToIndex(t);
1158   if (index == -1) {
1159     return '\0';
1160   }
1161   BLI_assert((uint)index < 3);
1162   return 'X' + index;
1163 }
1164
1165 bool isLockConstraint(TransInfo *t)
1166 {
1167   int mode = t->con.mode;
1168
1169   if ((mode & (CON_AXIS0 | CON_AXIS1)) == (CON_AXIS0 | CON_AXIS1)) {
1170     return true;
1171   }
1172
1173   if ((mode & (CON_AXIS1 | CON_AXIS2)) == (CON_AXIS1 | CON_AXIS2)) {
1174     return true;
1175   }
1176
1177   if ((mode & (CON_AXIS0 | CON_AXIS2)) == (CON_AXIS0 | CON_AXIS2)) {
1178     return true;
1179   }
1180
1181   return false;
1182 }
1183
1184 /*
1185  * Returns the dimension of the constraint space.
1186  *
1187  * For that reason, the flags always needs to be set to properly evaluate here,
1188  * even if they aren't actually used in the callback function. (Which could happen
1189  * for weird constraints not yet designed. Along a path for example.)
1190  */
1191
1192 int getConstraintSpaceDimension(TransInfo *t)
1193 {
1194   int n = 0;
1195
1196   if (t->con.mode & CON_AXIS0) {
1197     n++;
1198   }
1199
1200   if (t->con.mode & CON_AXIS1) {
1201     n++;
1202   }
1203
1204   if (t->con.mode & CON_AXIS2) {
1205     n++;
1206   }
1207
1208   return n;
1209   /*
1210    * Someone willing to do it cryptically could do the following instead:
1211    *
1212    * return t->con & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
1213    *
1214    * Based on the assumptions that the axis flags are one after the other and start at 1
1215    */
1216 }