Merge branch 'blender-v2.81-release'
[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 constraintValuesFinal(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   /* If `t->values` is operator param, use that directly but not if snapping is forced */
151   if (t->flag & T_INPUT_IS_VALUES_FINAL && (t->tsnap.status & SNAP_FORCED) == 0) {
152     copy_v3_v3(vec, t->values);
153     constraintValuesFinal(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];
239     float norm_center[3];
240     float plane[3];
241
242     getViewVector(t, t_con_center, norm_center);
243     cross_v3_v3v3(plane, norm_center, axis);
244
245     project_v3_v3v3(vec, in, plane);
246     sub_v3_v3v3(vec, in, vec);
247
248     add_v3_v3v3(v, vec, t_con_center);
249     getViewVector(t, v, norm);
250
251     /* give arbitrary large value if projection is impossible */
252     factor = dot_v3v3(axis, norm);
253     if (1.0f - fabsf(factor) < 0.0002f) {
254       copy_v3_v3(out, axis);
255       if (factor > 0) {
256         mul_v3_fl(out, 1000000000.0f);
257       }
258       else {
259         mul_v3_fl(out, -1000000000.0f);
260       }
261     }
262     else {
263       /* Use ray-ray intersection instead of line-line because this gave
264        * precision issues adding small values to large numbers. */
265       float mul;
266       if (isect_ray_ray_v3(t_con_center, axis, v, norm, &mul, NULL)) {
267         mul_v3_v3fl(out, axis, mul);
268       }
269       else {
270         /* In practice this should never fail. */
271         BLI_assert(0);
272       }
273
274       /* possible some values become nan when
275        * viewpoint and object are both zero */
276       if (!isfinite(out[0])) {
277         out[0] = 0.0f;
278       }
279       if (!isfinite(out[1])) {
280         out[1] = 0.0f;
281       }
282       if (!isfinite(out[2])) {
283         out[2] = 0.0f;
284       }
285     }
286   }
287 }
288
289 /**
290  * Return true if the 2x axis are both aligned when projected into the view.
291  * In this case, we can't usefully project the cursor onto the plane.
292  */
293 static bool isPlaneProjectionViewAligned(const TransInfo *t)
294 {
295   const float eps = 0.001f;
296   const float *constraint_vector[2];
297   int n = 0;
298   for (int i = 0; i < 3; i++) {
299     if (t->con.mode & (CON_AXIS0 << i)) {
300       constraint_vector[n++] = t->con.mtx[i];
301       if (n == 2) {
302         break;
303       }
304     }
305   }
306   BLI_assert(n == 2);
307
308   float view_to_plane[3], plane_normal[3];
309
310   getViewVector(t, t->center_global, view_to_plane);
311
312   cross_v3_v3v3(plane_normal, constraint_vector[0], constraint_vector[1]);
313   normalize_v3(plane_normal);
314
315   float factor = dot_v3v3(plane_normal, view_to_plane);
316   return fabsf(factor) < eps;
317 }
318
319 static void planeProjection(const TransInfo *t, const float in[3], float out[3])
320 {
321   float vec[3], factor, norm[3];
322
323   add_v3_v3v3(vec, in, t->center_global);
324   getViewVector(t, vec, norm);
325
326   sub_v3_v3v3(vec, out, in);
327
328   factor = dot_v3v3(vec, norm);
329   if (factor == 0.0f) {
330     return; /* prevent divide by zero */
331   }
332   factor = dot_v3v3(vec, vec) / factor;
333
334   copy_v3_v3(vec, norm);
335   mul_v3_fl(vec, factor);
336
337   add_v3_v3v3(out, in, vec);
338 }
339
340 /*
341  * Generic callback for constant spatial constraints applied to linear motion
342  *
343  * The IN vector in projected into the constrained space and then further
344  * projected along the view vector.
345  * (in perspective mode, the view vector is relative to the position on screen)
346  */
347
348 static void applyAxisConstraintVec(TransInfo *t,
349                                    TransDataContainer *UNUSED(tc),
350                                    TransData *td,
351                                    const float in[3],
352                                    float out[3],
353                                    float pvec[3])
354 {
355   copy_v3_v3(out, in);
356   if (!td && t->con.mode & CON_APPLY) {
357     mul_m3_v3(t->con.pmtx, out);
358
359     // With snap, a projection is alright, no need to correct for view alignment
360     if (!validSnap(t)) {
361       const int dims = getConstraintSpaceDimension(t);
362       if (dims == 2) {
363         if (!is_zero_v3(out)) {
364           if (!isPlaneProjectionViewAligned(t)) {
365             planeProjection(t, in, out);
366           }
367         }
368       }
369       else if (dims == 1) {
370         float c[3];
371
372         if (t->con.mode & CON_AXIS0) {
373           copy_v3_v3(c, t->con.mtx[0]);
374         }
375         else if (t->con.mode & CON_AXIS1) {
376           copy_v3_v3(c, t->con.mtx[1]);
377         }
378         else if (t->con.mode & CON_AXIS2) {
379           copy_v3_v3(c, t->con.mtx[2]);
380         }
381         axisProjection(t, c, in, out);
382       }
383     }
384     postConstraintChecks(t, out, pvec);
385   }
386 }
387
388 /*
389  * Generic callback for object based spatial constraints applied to linear motion
390  *
391  * At first, the following is applied to the first data in the array
392  * The IN vector in projected into the constrained space and then further
393  * projected along the view vector.
394  * (in perspective mode, the view vector is relative to the position on screen)
395  *
396  * Further down, that vector is mapped to each data's space.
397  */
398
399 static void applyObjectConstraintVec(TransInfo *t,
400                                      TransDataContainer *tc,
401                                      TransData *td,
402                                      const float in[3],
403                                      float out[3],
404                                      float pvec[3])
405 {
406   copy_v3_v3(out, in);
407   if (t->con.mode & CON_APPLY) {
408     if (!td) {
409       mul_m3_v3(t->con.pmtx, out);
410
411       const int dims = getConstraintSpaceDimension(t);
412       if (dims == 2) {
413         if (!is_zero_v3(out)) {
414           if (!isPlaneProjectionViewAligned(t)) {
415             planeProjection(t, in, out);
416           }
417         }
418       }
419       else if (dims == 1) {
420         float c[3];
421
422         if (t->con.mode & CON_AXIS0) {
423           copy_v3_v3(c, t->con.mtx[0]);
424         }
425         else if (t->con.mode & CON_AXIS1) {
426           copy_v3_v3(c, t->con.mtx[1]);
427         }
428         else if (t->con.mode & CON_AXIS2) {
429           copy_v3_v3(c, t->con.mtx[2]);
430         }
431         axisProjection(t, c, in, out);
432       }
433       postConstraintChecks(t, out, pvec);
434       copy_v3_v3(out, pvec);
435     }
436     else {
437       int i = 0;
438
439       out[0] = out[1] = out[2] = 0.0f;
440       if (t->con.mode & CON_AXIS0) {
441         out[0] = in[i++];
442       }
443       if (t->con.mode & CON_AXIS1) {
444         out[1] = in[i++];
445       }
446       if (t->con.mode & CON_AXIS2) {
447         out[2] = in[i++];
448       }
449
450       mul_m3_v3(td->axismtx, out);
451       if (t->flag & T_EDIT) {
452         mul_m3_v3(tc->mat3_unit, out);
453       }
454     }
455   }
456 }
457
458 /*
459  * Generic callback for constant spatial constraints applied to resize motion
460  */
461
462 static void applyAxisConstraintSize(TransInfo *t,
463                                     TransDataContainer *UNUSED(tc),
464                                     TransData *td,
465                                     float smat[3][3])
466 {
467   if (!td && t->con.mode & CON_APPLY) {
468     float tmat[3][3];
469
470     if (!(t->con.mode & CON_AXIS0)) {
471       smat[0][0] = 1.0f;
472     }
473     if (!(t->con.mode & CON_AXIS1)) {
474       smat[1][1] = 1.0f;
475     }
476     if (!(t->con.mode & CON_AXIS2)) {
477       smat[2][2] = 1.0f;
478     }
479
480     mul_m3_m3m3(tmat, smat, t->con.imtx);
481     mul_m3_m3m3(smat, t->con.mtx, tmat);
482   }
483 }
484
485 /*
486  * Callback for object based spatial constraints applied to resize motion
487  */
488
489 static void applyObjectConstraintSize(TransInfo *t,
490                                       TransDataContainer *tc,
491                                       TransData *td,
492                                       float smat[3][3])
493 {
494   if (td && t->con.mode & CON_APPLY) {
495     float tmat[3][3];
496     float imat[3][3];
497
498     invert_m3_m3(imat, td->axismtx);
499
500     if (!(t->con.mode & CON_AXIS0)) {
501       smat[0][0] = 1.0f;
502     }
503     if (!(t->con.mode & CON_AXIS1)) {
504       smat[1][1] = 1.0f;
505     }
506     if (!(t->con.mode & CON_AXIS2)) {
507       smat[2][2] = 1.0f;
508     }
509
510     mul_m3_m3m3(tmat, smat, imat);
511     if (t->flag & T_EDIT) {
512       mul_m3_m3m3(smat, tc->mat3_unit, smat);
513     }
514     mul_m3_m3m3(smat, td->axismtx, tmat);
515   }
516 }
517
518 /*
519  * Generic callback for constant spatial constraints applied to rotations
520  *
521  * The rotation axis is copied into VEC.
522  *
523  * In the case of single axis constraints, the rotation axis is directly the one constrained to.
524  * For planar constraints (2 axis), the rotation axis is the normal of the plane.
525  *
526  * The following only applies when CON_NOFLIP is not set.
527  * The vector is then modified to always point away from the screen (in global space)
528  * This insures that the rotation is always logically following the mouse.
529  * (ie: not doing counterclockwise rotations when the mouse moves clockwise).
530  */
531
532 static void applyAxisConstraintRot(
533     TransInfo *t, TransDataContainer *UNUSED(tc), TransData *td, float vec[3], float *angle)
534 {
535   if (!td && t->con.mode & CON_APPLY) {
536     int mode = t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2);
537
538     switch (mode) {
539       case CON_AXIS0:
540       case (CON_AXIS1 | CON_AXIS2):
541         copy_v3_v3(vec, t->con.mtx[0]);
542         break;
543       case CON_AXIS1:
544       case (CON_AXIS0 | CON_AXIS2):
545         copy_v3_v3(vec, t->con.mtx[1]);
546         break;
547       case CON_AXIS2:
548       case (CON_AXIS0 | CON_AXIS1):
549         copy_v3_v3(vec, t->con.mtx[2]);
550         break;
551     }
552     /* don't flip axis if asked to or if num input */
553     if (angle && (mode & CON_NOFLIP) == 0 && hasNumInput(&t->num) == 0) {
554       if (dot_v3v3(vec, t->viewinv[2]) > 0.0f) {
555         *angle = -(*angle);
556       }
557     }
558   }
559 }
560
561 /*
562  * Callback for object based spatial constraints applied to rotations
563  *
564  * The rotation axis is copied into VEC.
565  *
566  * In the case of single axis constraints, the rotation axis is directly the one constrained to.
567  * For planar constraints (2 axis), the rotation axis is the normal of the plane.
568  *
569  * The following only applies when CON_NOFLIP is not set.
570  * The vector is then modified to always point away from the screen (in global space)
571  * This insures that the rotation is always logically following the mouse.
572  * (ie: not doing counterclockwise rotations when the mouse moves clockwise).
573  */
574
575 static void applyObjectConstraintRot(
576     TransInfo *t, TransDataContainer *tc, TransData *td, float vec[3], float *angle)
577 {
578   if (t->con.mode & CON_APPLY) {
579     int mode = t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2);
580     float tmp_axismtx[3][3];
581     float(*axismtx)[3];
582
583     /* on setup call, use first object */
584     if (td == NULL) {
585       BLI_assert(tc == NULL);
586       tc = TRANS_DATA_CONTAINER_FIRST_OK(t);
587       td = tc->data;
588     }
589
590     if (t->flag & T_EDIT) {
591       mul_m3_m3m3(tmp_axismtx, tc->mat3_unit, td->axismtx);
592       axismtx = tmp_axismtx;
593     }
594     else {
595       axismtx = td->axismtx;
596     }
597
598     switch (mode) {
599       case CON_AXIS0:
600       case (CON_AXIS1 | CON_AXIS2):
601         copy_v3_v3(vec, axismtx[0]);
602         break;
603       case CON_AXIS1:
604       case (CON_AXIS0 | CON_AXIS2):
605         copy_v3_v3(vec, axismtx[1]);
606         break;
607       case CON_AXIS2:
608       case (CON_AXIS0 | CON_AXIS1):
609         copy_v3_v3(vec, axismtx[2]);
610         break;
611     }
612     if (angle && (mode & CON_NOFLIP) == 0 && hasNumInput(&t->num) == 0) {
613       if (dot_v3v3(vec, t->viewinv[2]) > 0.0f) {
614         *angle = -(*angle);
615       }
616     }
617   }
618 }
619
620 /*--------------------- INTERNAL SETUP CALLS ------------------*/
621
622 void setConstraint(TransInfo *t, float space[3][3], int mode, const char text[])
623 {
624   BLI_strncpy(t->con.text + 1, text, sizeof(t->con.text) - 1);
625   copy_m3_m3(t->con.mtx, space);
626   t->con.mode = mode;
627   getConstraintMatrix(t);
628
629   startConstraint(t);
630
631   t->con.drawExtra = NULL;
632   t->con.applyVec = applyAxisConstraintVec;
633   t->con.applySize = applyAxisConstraintSize;
634   t->con.applyRot = applyAxisConstraintRot;
635   t->redraw = TREDRAW_HARD;
636 }
637
638 /* applies individual td->axismtx constraints */
639 void setAxisMatrixConstraint(TransInfo *t, int mode, const char text[])
640 {
641   TransDataContainer *tc = t->data_container;
642   if (t->data_len_all == 1) {
643     float axismtx[3][3];
644     if (t->flag & T_EDIT) {
645       mul_m3_m3m3(axismtx, tc->mat3_unit, tc->data->axismtx);
646     }
647     else {
648       copy_m3_m3(axismtx, tc->data->axismtx);
649     }
650
651     setConstraint(t, axismtx, mode, text);
652   }
653   else {
654     BLI_strncpy(t->con.text + 1, text, sizeof(t->con.text) - 1);
655     copy_m3_m3(t->con.mtx, tc->data->axismtx);
656     t->con.mode = mode;
657     getConstraintMatrix(t);
658
659     startConstraint(t);
660
661     t->con.drawExtra = drawObjectConstraint;
662     t->con.applyVec = applyObjectConstraintVec;
663     t->con.applySize = applyObjectConstraintSize;
664     t->con.applyRot = applyObjectConstraintRot;
665     t->redraw = TREDRAW_HARD;
666   }
667 }
668
669 void setLocalConstraint(TransInfo *t, int mode, const char text[])
670 {
671   /* edit-mode now allows local transforms too */
672   if (t->flag & T_EDIT) {
673     /* Use the active (first) edit object. */
674     TransDataContainer *tc = t->data_container;
675     setConstraint(t, tc->mat3_unit, mode, text);
676   }
677   else {
678     setAxisMatrixConstraint(t, mode, text);
679   }
680 }
681
682 /*
683  * Set the constraint according to the user defined orientation
684  *
685  * ftext is a format string passed to BLI_snprintf. It will add the name of
686  * the orientation where %s is (logically).
687  */
688 void setUserConstraint(TransInfo *t, short orientation, int mode, const char ftext[])
689 {
690   char text[256];
691
692   switch (orientation) {
693     case V3D_ORIENT_GLOBAL: {
694       float mtx[3][3];
695       BLI_snprintf(text, sizeof(text), ftext, TIP_("global"));
696       unit_m3(mtx);
697       setConstraint(t, mtx, mode, text);
698       break;
699     }
700     case V3D_ORIENT_LOCAL:
701       BLI_snprintf(text, sizeof(text), ftext, TIP_("local"));
702       setLocalConstraint(t, mode, text);
703       break;
704     case V3D_ORIENT_NORMAL:
705       BLI_snprintf(text, sizeof(text), ftext, TIP_("normal"));
706       if (checkUseAxisMatrix(t)) {
707         setAxisMatrixConstraint(t, mode, text);
708       }
709       else {
710         setConstraint(t, t->spacemtx, mode, text);
711       }
712       break;
713     case V3D_ORIENT_VIEW:
714       BLI_snprintf(text, sizeof(text), ftext, TIP_("view"));
715       setConstraint(t, t->spacemtx, mode, text);
716       break;
717     case V3D_ORIENT_CURSOR:
718       BLI_snprintf(text, sizeof(text), ftext, TIP_("cursor"));
719       setConstraint(t, t->spacemtx, mode, text);
720       break;
721     case V3D_ORIENT_GIMBAL:
722       BLI_snprintf(text, sizeof(text), ftext, TIP_("gimbal"));
723       setConstraint(t, t->spacemtx, mode, text);
724       break;
725     case V3D_ORIENT_CUSTOM_MATRIX:
726       BLI_snprintf(text, sizeof(text), ftext, TIP_("custom matrix"));
727       setConstraint(t, t->spacemtx, mode, text);
728       break;
729     case V3D_ORIENT_CUSTOM: {
730       char orientation_str[128];
731       BLI_snprintf(orientation_str,
732                    sizeof(orientation_str),
733                    "%s \"%s\"",
734                    TIP_("custom orientation"),
735                    t->orientation.custom->name);
736       BLI_snprintf(text, sizeof(text), ftext, orientation_str);
737       setConstraint(t, t->spacemtx, mode, text);
738       break;
739     }
740   }
741
742   t->con.orientation = orientation;
743
744   t->con.mode |= CON_USER;
745 }
746
747 /*----------------- DRAWING CONSTRAINTS -------------------*/
748
749 void drawConstraint(TransInfo *t)
750 {
751   TransCon *tc = &(t->con);
752
753   if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_NODE)) {
754     return;
755   }
756   if (!(tc->mode & CON_APPLY)) {
757     return;
758   }
759   if (t->flag & T_NO_CONSTRAINT) {
760     return;
761   }
762
763   if (tc->drawExtra) {
764     tc->drawExtra(t);
765   }
766   else {
767     if (tc->mode & CON_SELECT) {
768       float vec[3];
769       int depth_test_enabled;
770
771       convertViewVec(t, vec, (t->mval[0] - t->con.imval[0]), (t->mval[1] - t->con.imval[1]));
772       add_v3_v3(vec, t->center_global);
773
774       drawLine(t, t->center_global, tc->mtx[0], 'X', 0);
775       drawLine(t, t->center_global, tc->mtx[1], 'Y', 0);
776       drawLine(t, t->center_global, tc->mtx[2], 'Z', 0);
777
778       depth_test_enabled = GPU_depth_test_enabled();
779       if (depth_test_enabled) {
780         GPU_depth_test(false);
781       }
782
783       const uint shdr_pos = GPU_vertformat_attr_add(
784           immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
785
786       immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
787
788       float viewport_size[4];
789       GPU_viewport_size_get_f(viewport_size);
790       immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
791
792       immUniform1i("colors_len", 0); /* "simple" mode */
793       immUniformColor4f(1.0f, 1.0f, 1.0f, 1.0f);
794       immUniform1f("dash_width", 2.0f);
795       immUniform1f("dash_factor", 0.5f);
796
797       immBegin(GPU_PRIM_LINES, 2);
798       immVertex3fv(shdr_pos, t->center_global);
799       immVertex3fv(shdr_pos, vec);
800       immEnd();
801
802       immUnbindProgram();
803
804       if (depth_test_enabled) {
805         GPU_depth_test(true);
806       }
807     }
808
809     if (tc->mode & CON_AXIS0) {
810       drawLine(t, t->center_global, tc->mtx[0], 'X', DRAWLIGHT);
811     }
812     if (tc->mode & CON_AXIS1) {
813       drawLine(t, t->center_global, tc->mtx[1], 'Y', DRAWLIGHT);
814     }
815     if (tc->mode & CON_AXIS2) {
816       drawLine(t, t->center_global, tc->mtx[2], 'Z', DRAWLIGHT);
817     }
818   }
819 }
820
821 /* called from drawview.c, as an extra per-window draw option */
822 void drawPropCircle(const struct bContext *C, TransInfo *t)
823 {
824   if (t->flag & T_PROP_EDIT) {
825     RegionView3D *rv3d = CTX_wm_region_view3d(C);
826     float tmat[4][4], imat[4][4];
827     int depth_test_enabled;
828
829     if (t->spacetype == SPACE_VIEW3D && rv3d != NULL) {
830       copy_m4_m4(tmat, rv3d->viewmat);
831       invert_m4_m4(imat, tmat);
832     }
833     else {
834       unit_m4(tmat);
835       unit_m4(imat);
836     }
837
838     GPU_matrix_push();
839
840     if (t->spacetype == SPACE_VIEW3D) {
841       /* pass */
842     }
843     else if (t->spacetype == SPACE_IMAGE) {
844       GPU_matrix_scale_2f(1.0f / t->aspect[0], 1.0f / t->aspect[1]);
845     }
846     else if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_ACTION)) {
847       /* only scale y */
848       rcti *mask = &t->ar->v2d.mask;
849       rctf *datamask = &t->ar->v2d.cur;
850       float xsize = BLI_rctf_size_x(datamask);
851       float ysize = BLI_rctf_size_y(datamask);
852       float xmask = BLI_rcti_size_x(mask);
853       float ymask = BLI_rcti_size_y(mask);
854       GPU_matrix_scale_2f(1.0f, (ysize / xsize) * (xmask / ymask));
855     }
856
857     depth_test_enabled = GPU_depth_test_enabled();
858     if (depth_test_enabled) {
859       GPU_depth_test(false);
860     }
861
862     uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
863
864     immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
865     immUniformThemeColor(TH_GRID);
866
867     GPU_logic_op_invert_set(true);
868     imm_drawcircball(t->center_global, t->prop_size, imat, pos);
869     GPU_logic_op_invert_set(false);
870
871     immUnbindProgram();
872
873     if (depth_test_enabled) {
874       GPU_depth_test(true);
875     }
876
877     GPU_matrix_pop();
878   }
879 }
880
881 static void drawObjectConstraint(TransInfo *t)
882 {
883   /* Draw the first one lighter because that's the one who controls the others.
884    * Meaning the transformation is projected on that one and just copied on the others
885    * constraint space.
886    * In a nutshell, the object with light axis is controlled by the user and the others follow.
887    * Without drawing the first light, users have little clue what they are doing.
888    */
889   short options = DRAWLIGHT;
890   int i;
891   float tmp_axismtx[3][3];
892
893   FOREACH_TRANS_DATA_CONTAINER (t, tc) {
894     TransData *td = tc->data;
895     for (i = 0; i < tc->data_len; i++, td++) {
896       float co[3];
897       float(*axismtx)[3];
898
899       if (t->flag & T_PROP_EDIT) {
900         /* we're sorted, so skip the rest */
901         if (td->factor == 0.0f) {
902           break;
903         }
904       }
905
906       if (t->options & CTX_GPENCIL_STROKES) {
907         /* only draw a constraint line for one point, otherwise we can't see anything */
908         if ((options & DRAWLIGHT) == 0) {
909           break;
910         }
911       }
912
913       if (t->flag & T_OBJECT) {
914         copy_v3_v3(co, td->ob->obmat[3]);
915         axismtx = td->axismtx;
916       }
917       else if (t->flag & T_EDIT) {
918         mul_v3_m4v3(co, tc->mat, td->center);
919
920         mul_m3_m3m3(tmp_axismtx, tc->mat3_unit, td->axismtx);
921         axismtx = tmp_axismtx;
922       }
923       else if (t->flag & T_POSE) {
924         mul_v3_m4v3(co, tc->mat, td->center);
925         axismtx = td->axismtx;
926       }
927       else {
928         copy_v3_v3(co, td->center);
929         axismtx = td->axismtx;
930       }
931
932       if (t->con.mode & CON_AXIS0) {
933         drawLine(t, co, axismtx[0], 'X', options);
934       }
935       if (t->con.mode & CON_AXIS1) {
936         drawLine(t, co, axismtx[1], 'Y', options);
937       }
938       if (t->con.mode & CON_AXIS2) {
939         drawLine(t, co, axismtx[2], 'Z', options);
940       }
941       options &= ~DRAWLIGHT;
942     }
943   }
944 }
945
946 /*--------------------- START / STOP CONSTRAINTS ---------------------- */
947
948 void startConstraint(TransInfo *t)
949 {
950   t->con.mode |= CON_APPLY;
951   *t->con.text = ' ';
952   t->num.idx_max = min_ii(getConstraintSpaceDimension(t) - 1, t->idx_max);
953 }
954
955 void stopConstraint(TransInfo *t)
956 {
957   t->con.mode &= ~(CON_APPLY | CON_SELECT);
958   *t->con.text = '\0';
959   t->num.idx_max = t->idx_max;
960 }
961
962 void getConstraintMatrix(TransInfo *t)
963 {
964   float mat[3][3];
965   invert_m3_m3(t->con.imtx, t->con.mtx);
966   unit_m3(t->con.pmtx);
967
968   if (!(t->con.mode & CON_AXIS0)) {
969     zero_v3(t->con.pmtx[0]);
970   }
971
972   if (!(t->con.mode & CON_AXIS1)) {
973     zero_v3(t->con.pmtx[1]);
974   }
975
976   if (!(t->con.mode & CON_AXIS2)) {
977     zero_v3(t->con.pmtx[2]);
978   }
979
980   mul_m3_m3m3(mat, t->con.pmtx, t->con.imtx);
981   mul_m3_m3m3(t->con.pmtx, t->con.mtx, mat);
982 }
983
984 /*------------------------- MMB Select -------------------------------*/
985
986 void initSelectConstraint(TransInfo *t, float mtx[3][3])
987 {
988   copy_m3_m3(t->con.mtx, mtx);
989   t->con.mode |= CON_APPLY;
990   t->con.mode |= CON_SELECT;
991
992   setNearestAxis(t);
993   t->con.drawExtra = NULL;
994   t->con.applyVec = applyAxisConstraintVec;
995   t->con.applySize = applyAxisConstraintSize;
996   t->con.applyRot = applyAxisConstraintRot;
997 }
998
999 void selectConstraint(TransInfo *t)
1000 {
1001   if (t->con.mode & CON_SELECT) {
1002     setNearestAxis(t);
1003     startConstraint(t);
1004   }
1005 }
1006
1007 void postSelectConstraint(TransInfo *t)
1008 {
1009   if (!(t->con.mode & CON_SELECT)) {
1010     return;
1011   }
1012
1013   t->con.mode &= ~CON_AXIS0;
1014   t->con.mode &= ~CON_AXIS1;
1015   t->con.mode &= ~CON_AXIS2;
1016   t->con.mode &= ~CON_SELECT;
1017
1018   setNearestAxis(t);
1019
1020   startConstraint(t);
1021   t->redraw = TREDRAW_HARD;
1022 }
1023
1024 static void setNearestAxis2d(TransInfo *t)
1025 {
1026   /* no correction needed... just use whichever one is lower */
1027   if (abs(t->mval[0] - t->con.imval[0]) < abs(t->mval[1] - t->con.imval[1])) {
1028     t->con.mode |= CON_AXIS1;
1029     BLI_strncpy(t->con.text, TIP_(" along Y axis"), sizeof(t->con.text));
1030   }
1031   else {
1032     t->con.mode |= CON_AXIS0;
1033     BLI_strncpy(t->con.text, TIP_(" along X axis"), sizeof(t->con.text));
1034   }
1035 }
1036
1037 static void setNearestAxis3d(TransInfo *t)
1038 {
1039   float zfac;
1040   float mvec[3], proj[3];
1041   float len[3];
1042   int i;
1043
1044   /* calculate mouse movement */
1045   mvec[0] = (float)(t->mval[0] - t->con.imval[0]);
1046   mvec[1] = (float)(t->mval[1] - t->con.imval[1]);
1047   mvec[2] = 0.0f;
1048
1049   /* We need to correct axis length for the current zoom-level of view,
1050    * this to prevent projected values to be clipped behind the camera
1051    * and to overflow the short integers.
1052    * The formula used is a bit stupid, just a simplification of the subtraction
1053    * of two 2D points 30 pixels apart (that's the last factor in the formula) after
1054    * projecting them with ED_view3d_win_to_delta and then get the length of that vector.
1055    */
1056   zfac = mul_project_m4_v3_zfac(t->persmat, t->center_global);
1057   zfac = len_v3(t->persinv[0]) * 2.0f / t->ar->winx * zfac * 30.0f;
1058
1059   for (i = 0; i < 3; i++) {
1060     float axis[3], axis_2d[2];
1061
1062     copy_v3_v3(axis, t->con.mtx[i]);
1063
1064     mul_v3_fl(axis, zfac);
1065     /* now we can project to get window coordinate */
1066     add_v3_v3(axis, t->center_global);
1067     projectFloatView(t, axis, axis_2d);
1068
1069     sub_v2_v2v2(axis, axis_2d, t->center2d);
1070     axis[2] = 0.0f;
1071
1072     if (normalize_v3(axis) > 1e-3f) {
1073       project_v3_v3v3(proj, mvec, axis);
1074       sub_v3_v3v3(axis, mvec, proj);
1075       len[i] = normalize_v3(axis);
1076     }
1077     else {
1078       len[i] = 1e10f;
1079     }
1080   }
1081
1082   if (len[0] <= len[1] && len[0] <= len[2]) {
1083     if (t->modifiers & MOD_CONSTRAINT_PLANE) {
1084       t->con.mode |= (CON_AXIS1 | CON_AXIS2);
1085       BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s X axis"), t->spacename);
1086     }
1087     else {
1088       t->con.mode |= CON_AXIS0;
1089       BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" along %s X axis"), t->spacename);
1090     }
1091   }
1092   else if (len[1] <= len[0] && len[1] <= len[2]) {
1093     if (t->modifiers & MOD_CONSTRAINT_PLANE) {
1094       t->con.mode |= (CON_AXIS0 | CON_AXIS2);
1095       BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s Y axis"), t->spacename);
1096     }
1097     else {
1098       t->con.mode |= CON_AXIS1;
1099       BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" along %s Y axis"), t->spacename);
1100     }
1101   }
1102   else if (len[2] <= len[1] && len[2] <= len[0]) {
1103     if (t->modifiers & MOD_CONSTRAINT_PLANE) {
1104       t->con.mode |= (CON_AXIS0 | CON_AXIS1);
1105       BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s Z axis"), t->spacename);
1106     }
1107     else {
1108       t->con.mode |= CON_AXIS2;
1109       BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" along %s Z axis"), t->spacename);
1110     }
1111   }
1112 }
1113
1114 void setNearestAxis(TransInfo *t)
1115 {
1116   /* clear any prior constraint flags */
1117   t->con.mode &= ~CON_AXIS0;
1118   t->con.mode &= ~CON_AXIS1;
1119   t->con.mode &= ~CON_AXIS2;
1120
1121   /* constraint setting - depends on spacetype */
1122   if (t->spacetype == SPACE_VIEW3D) {
1123     /* 3d-view */
1124     setNearestAxis3d(t);
1125   }
1126   else {
1127     /* assume that this means a 2D-Editor */
1128     setNearestAxis2d(t);
1129   }
1130
1131   getConstraintMatrix(t);
1132 }
1133
1134 /*-------------- HELPER FUNCTIONS ----------------*/
1135
1136 int constraintModeToIndex(const TransInfo *t)
1137 {
1138   if ((t->con.mode & CON_APPLY) == 0) {
1139     return -1;
1140   }
1141   switch (t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2)) {
1142     case (CON_AXIS0):
1143     case (CON_AXIS1 | CON_AXIS2):
1144       return 0;
1145     case (CON_AXIS1):
1146     case (CON_AXIS0 | CON_AXIS2):
1147       return 1;
1148     case (CON_AXIS2):
1149     case (CON_AXIS0 | CON_AXIS1):
1150       return 2;
1151     default:
1152       return -1;
1153   }
1154 }
1155
1156 char constraintModeToChar(const TransInfo *t)
1157 {
1158   int index = constraintModeToIndex(t);
1159   if (index == -1) {
1160     return '\0';
1161   }
1162   BLI_assert((uint)index < 3);
1163   return 'X' + index;
1164 }
1165
1166 bool isLockConstraint(TransInfo *t)
1167 {
1168   int mode = t->con.mode;
1169
1170   if ((mode & (CON_AXIS0 | CON_AXIS1)) == (CON_AXIS0 | CON_AXIS1)) {
1171     return true;
1172   }
1173
1174   if ((mode & (CON_AXIS1 | CON_AXIS2)) == (CON_AXIS1 | CON_AXIS2)) {
1175     return true;
1176   }
1177
1178   if ((mode & (CON_AXIS0 | CON_AXIS2)) == (CON_AXIS0 | CON_AXIS2)) {
1179     return true;
1180   }
1181
1182   return false;
1183 }
1184
1185 /*
1186  * Returns the dimension of the constraint space.
1187  *
1188  * For that reason, the flags always needs to be set to properly evaluate here,
1189  * even if they aren't actually used in the callback function. (Which could happen
1190  * for weird constraints not yet designed. Along a path for example.)
1191  */
1192
1193 int getConstraintSpaceDimension(TransInfo *t)
1194 {
1195   int n = 0;
1196
1197   if (t->con.mode & CON_AXIS0) {
1198     n++;
1199   }
1200
1201   if (t->con.mode & CON_AXIS1) {
1202     n++;
1203   }
1204
1205   if (t->con.mode & CON_AXIS2) {
1206     n++;
1207   }
1208
1209   return n;
1210   /*
1211    * Someone willing to do it cryptically could do the following instead:
1212    *
1213    * return t->con & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
1214    *
1215    * Based on the assumptions that the axis flags are one after the other and start at 1
1216    */
1217 }