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