2
* $Id: transform_constraints.c,v 1.54 2005/06/05 13:50:21 theeth Exp $
4
* ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
6
* This program is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU General Public License
8
* as published by the Free Software Foundation; either version 2
9
* of the License, or (at your option) any later version. The Blender
10
* Foundation also sells licenses for use in proprietary software under
11
* the Blender License. See http://www.blender.org/BL/ for information
14
* This program is distributed in the hope that it will be useful,
15
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
* GNU General Public License for more details.
19
* You should have received a copy of the GNU General Public License
20
* along with this program; if not, write to the Free Software Foundation,
21
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24
* All rights reserved.
26
* The Original Code is: all of this file.
28
* Contributor(s): none yet.
30
* ***** END GPL/BL DUAL LICENSE BLOCK *****
47
#include "MEM_guardedalloc.h"
49
#include "DNA_action_types.h"
50
#include "DNA_armature_types.h"
51
#include "DNA_camera_types.h"
52
#include "DNA_curve_types.h"
53
#include "DNA_effect_types.h"
54
#include "DNA_ika_types.h"
55
#include "DNA_image_types.h"
56
#include "DNA_ipo_types.h"
57
#include "DNA_key_types.h"
58
#include "DNA_lamp_types.h"
59
#include "DNA_lattice_types.h"
60
#include "DNA_mesh_types.h"
61
#include "DNA_meshdata_types.h"
62
#include "DNA_meta_types.h"
63
#include "DNA_object_types.h"
64
#include "DNA_scene_types.h"
65
#include "DNA_screen_types.h"
66
#include "DNA_texture_types.h"
67
#include "DNA_view3d_types.h"
68
#include "DNA_world_types.h"
69
#include "DNA_userdef_types.h"
70
#include "DNA_property_types.h"
71
#include "DNA_vfont_types.h"
72
#include "DNA_constraint_types.h"
74
#include "BIF_screen.h"
75
#include "BIF_space.h"
76
#include "BIF_editview.h"
77
#include "BIF_resources.h"
78
#include "BIF_mywindow.h"
80
#include "BIF_editlattice.h"
81
#include "BIF_editarmature.h"
82
#include "BIF_editmesh.h"
84
#include "BKE_global.h"
85
#include "BKE_object.h"
86
#include "BKE_utildefines.h"
87
#include "BKE_lattice.h"
88
#include "BKE_armature.h"
89
#include "BKE_curve.h"
90
#include "BKE_displist.h"
95
#include "BLI_arithb.h"
96
#include "BLI_editVert.h"
98
#include "BDR_drawobject.h"
102
#include "mydevice.h"
104
#include "transform.h"
106
extern ListBase editNurb;
107
extern ListBase editelems;
111
/* ************************** CONSTRAINTS ************************* */
112
void getConstraintMatrix(TransInfo *t);
114
void constraintNumInput(TransInfo *t, float vec[3])
116
int mode = t->con.mode;
117
if (mode & CON_APPLY) {
118
float nval = (t->flag & T_NULL_ONE)?1.0f:0.0f;
120
if (getConstraintSpaceDimension(t) == 2) {
121
if (mode & (CON_AXIS0|CON_AXIS1)) {
124
else if (mode & (CON_AXIS1|CON_AXIS2)) {
129
else if (mode & (CON_AXIS0|CON_AXIS2)) {
134
else if (getConstraintSpaceDimension(t) == 1) {
135
if (mode & CON_AXIS0) {
139
else if (mode & CON_AXIS1) {
144
else if (mode & CON_AXIS2) {
153
static void postConstraintChecks(TransInfo *t, float vec[3], float pvec[3]) {
156
Mat3MulVecfl(t->con.imtx, vec);
160
if (t->num.flag & T_NULL_ONE) {
161
if (!(t->con.mode & CON_AXIS0))
164
if (!(t->con.mode & CON_AXIS1))
167
if (!(t->con.mode & CON_AXIS2))
171
if (hasNumInput(&t->num)) {
172
applyNumInput(&t->num, vec);
173
constraintNumInput(t, vec);
176
if (t->con.mode & CON_AXIS0) {
179
if (t->con.mode & CON_AXIS1) {
182
if (t->con.mode & CON_AXIS2) {
186
Mat3MulVecfl(t->con.mtx, vec);
190
static void axisProjection(TransInfo *t, float axis[3], float in[3], float out[3]) {
191
float norm[3], n[3], vec[3], factor;
193
VecAddf(vec, in, t->con.center);
194
getViewVector(vec, norm);
199
Mat4MulVecfl(t->viewmat, n);
200
n[2] = t->viewmat[3][2];
201
Mat4MulVecfl(t->viewinv, n);
203
/* For when view is parallel to constraint... will cause NaNs otherwise
204
So we take vertical motion in 3D space and apply it to the
205
constraint axis. Nice for camera grab + MMB */
206
if(n[0]*n[0] + n[1]*n[1] + n[2]*n[2] < 0.000001f) {
207
Projf(vec, in, t->viewinv[1]);
208
factor = Inpf(t->viewinv[1], vec) * 2.0f;
209
/* since camera distance is quite relative, use quadratic relationship. holding shift can compensate */
210
if(factor<0.0f) factor*= -factor;
211
else factor*= factor;
215
VecMulf(out, -factor); /* -factor makes move down going backwards */
218
// prevent division by zero, happens on constrainting without initial delta transform */
219
if(in[0]!=0.0f || in[1]!=0.0f || in[2]!=0.0) {
221
factor = Normalise(vec);
222
// prevent NaN for 0.0/0.0
224
factor /= Inpf(axis, vec);
226
VecMulf(axis, factor);
232
static void planeProjection(TransInfo *t, float in[3], float out[3]) {
233
float vec[3], factor, angle, norm[3];
235
VecAddf(vec, in, t->con.center);
236
getViewVector(vec, norm);
238
VecSubf(vec, out, in);
239
factor = Normalise(vec);
240
angle = Inpf(vec, norm);
242
if (angle * angle >= 0.000001f) {
246
VecMulf(vec, factor);
248
VecAddf(out, in, vec);
253
* Generic callback for constant spacial constraints applied to linear motion
255
* The IN vector in projected into the constrained space and then further
256
* projected along the view vector.
257
* (in perspective mode, the view vector is relative to the position on screen)
261
static void applyAxisConstraintVec(TransInfo *t, TransData *td, float in[3], float out[3], float pvec[3])
264
if (!td && t->con.mode & CON_APPLY) {
265
Mat3MulVecfl(t->con.pmtx, out);
266
if (getConstraintSpaceDimension(t) == 2) {
267
if (out[0] != 0.0f || out[1] != 0.0f || out[2] != 0.0f) {
268
planeProjection(t, in, out);
271
else if (getConstraintSpaceDimension(t) == 1) {
274
if (t->con.mode & CON_AXIS0) {
275
VECCOPY(c, t->con.mtx[0]);
277
else if (t->con.mode & CON_AXIS1) {
278
VECCOPY(c, t->con.mtx[1]);
280
else if (t->con.mode & CON_AXIS2) {
281
VECCOPY(c, t->con.mtx[2]);
283
axisProjection(t, c, in, out);
285
postConstraintChecks(t, out, pvec);
290
* Generic callback for object based spacial constraints applied to linear motion
292
* At first, the following is applied to the first data in the array
293
* The IN vector in projected into the constrained space and then further
294
* projected along the view vector.
295
* (in perspective mode, the view vector is relative to the position on screen)
297
* Further down, that vector is mapped to each data's space.
300
static void applyObjectConstraintVec(TransInfo *t, TransData *td, float in[3], float out[3], float pvec[3])
303
if (t->con.mode & CON_APPLY) {
305
Mat3MulVecfl(t->con.pmtx, out);
306
if (getConstraintSpaceDimension(t) == 2) {
307
if (out[0] != 0.0f || out[1] != 0.0f || out[2] != 0.0f) {
308
planeProjection(t, in, out);
311
else if (getConstraintSpaceDimension(t) == 1) {
314
if (t->con.mode & CON_AXIS0) {
315
VECCOPY(c, t->con.mtx[0]);
317
else if (t->con.mode & CON_AXIS1) {
318
VECCOPY(c, t->con.mtx[1]);
320
else if (t->con.mode & CON_AXIS2) {
321
VECCOPY(c, t->con.mtx[2]);
323
axisProjection(t, c, in, out);
325
postConstraintChecks(t, out, pvec);
329
Mat3MulVecfl(td->axismtx, out);
335
* Generic callback for constant spacial constraints applied to resize motion
340
static void applyAxisConstraintSize(TransInfo *t, TransData *td, float smat[3][3])
342
if (!td && t->con.mode & CON_APPLY) {
345
if (!(t->con.mode & CON_AXIS0)) {
348
if (!(t->con.mode & CON_AXIS1)) {
351
if (!(t->con.mode & CON_AXIS2)) {
355
Mat3MulMat3(tmat, smat, t->con.imtx);
356
Mat3MulMat3(smat, t->con.mtx, tmat);
361
* Callback for object based spacial constraints applied to resize motion
366
static void applyObjectConstraintSize(TransInfo *t, TransData *td, float smat[3][3])
368
if (td && t->con.mode & CON_APPLY) {
372
Mat3Inv(imat, td->axismtx);
374
if (!(t->con.mode & CON_AXIS0)) {
377
if (!(t->con.mode & CON_AXIS1)) {
380
if (!(t->con.mode & CON_AXIS2)) {
384
Mat3MulMat3(tmat, smat, imat);
385
Mat3MulMat3(smat, td->axismtx, tmat);
390
* Generic callback for constant spacial constraints applied to rotations
392
* The rotation axis is copied into VEC.
394
* In the case of single axis constraints, the rotation axis is directly the one constrained to.
395
* For planar constraints (2 axis), the rotation axis is the normal of the plane.
397
* The following only applies when CON_NOFLIP is not set.
398
* The vector is then modified to always point away from the screen (in global space)
399
* This insures that the rotation is always logically following the mouse.
400
* (ie: not doing counterclockwise rotations when the mouse moves clockwise).
403
static void applyAxisConstraintRot(TransInfo *t, TransData *td, float vec[3])
405
if (!td && t->con.mode & CON_APPLY) {
406
int mode = t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
410
case (CON_AXIS1|CON_AXIS2):
411
VECCOPY(vec, t->con.mtx[0]);
414
case (CON_AXIS0|CON_AXIS2):
415
VECCOPY(vec, t->con.mtx[1]);
418
case (CON_AXIS0|CON_AXIS1):
419
VECCOPY(vec, t->con.mtx[2]);
422
if (!(mode & CON_NOFLIP)) {
423
if (Inpf(vec, t->viewinv[2]) > 0.0f) {
431
* Callback for object based spacial constraints applied to rotations
433
* The rotation axis is copied into VEC.
435
* In the case of single axis constraints, the rotation axis is directly the one constrained to.
436
* For planar constraints (2 axis), the rotation axis is the normal of the plane.
438
* The following only applies when CON_NOFLIP is not set.
439
* The vector is then modified to always point away from the screen (in global space)
440
* This insures that the rotation is always logically following the mouse.
441
* (ie: not doing counterclockwise rotations when the mouse moves clockwise).
444
static void applyObjectConstraintRot(TransInfo *t, TransData *td, float vec[3])
446
if (td && t->con.mode & CON_APPLY) {
447
int mode = t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
451
case (CON_AXIS1|CON_AXIS2):
452
VECCOPY(vec, td->axismtx[0]);
455
case (CON_AXIS0|CON_AXIS2):
456
VECCOPY(vec, td->axismtx[1]);
459
case (CON_AXIS0|CON_AXIS1):
460
VECCOPY(vec, td->axismtx[2]);
463
if (!(mode & CON_NOFLIP)) {
464
if (Inpf(vec, t->viewinv[2]) > 0.0f) {
471
static void drawObjectConstraint(TransInfo *t) {
473
TransData * td = t->data;
475
/* Draw the first one lighter because that's the one who controls the others.
476
Meaning the transformation is projected on that one and just copied on the others
478
In a nutshell, the object with light axis is controlled by the user and the others follow.
479
Without drawing the first light, users have little clue what they are doing.
481
if (t->con.mode & CON_AXIS0) {
482
drawLine(td->ob->obmat[3], td->axismtx[0], 'x', DRAWLIGHT);
484
if (t->con.mode & CON_AXIS1) {
485
drawLine(td->ob->obmat[3], td->axismtx[1], 'y', DRAWLIGHT);
487
if (t->con.mode & CON_AXIS2) {
488
drawLine(td->ob->obmat[3], td->axismtx[2], 'z', DRAWLIGHT);
493
for(i=1;i<t->total;i++,td++) {
494
if (t->con.mode & CON_AXIS0) {
495
drawLine(td->ob->obmat[3], td->axismtx[0], 'x', 0);
497
if (t->con.mode & CON_AXIS1) {
498
drawLine(td->ob->obmat[3], td->axismtx[1], 'y', 0);
500
if (t->con.mode & CON_AXIS2) {
501
drawLine(td->ob->obmat[3], td->axismtx[2], 'z', 0);
507
* Returns the dimension of the constraint space.
509
* For that reason, the flags always needs to be set to properly evaluate here,
510
* even if they aren't actually used in the callback function. (Which could happen
511
* for weird constraints not yet designed. Along a path for example.)
514
int getConstraintSpaceDimension(TransInfo *t)
518
if (t->con.mode & CON_AXIS0)
521
if (t->con.mode & CON_AXIS1)
524
if (t->con.mode & CON_AXIS2)
529
Someone willing to do it criptically could do the following instead:
531
return t->con & (CON_AXIS0|CON_AXIS1|CON_AXIS2);
533
Based on the assumptions that the axis flags are one after the other and start at 1
537
void setConstraint(TransInfo *t, float space[3][3], int mode, const char text[]) {
538
strncpy(t->con.text + 1, text, 48);
539
Mat3CpyMat3(t->con.mtx, space);
541
getConstraintMatrix(t);
545
t->con.drawExtra = NULL;
546
t->con.applyVec = applyAxisConstraintVec;
547
t->con.applySize = applyAxisConstraintSize;
548
t->con.applyRot = applyAxisConstraintRot;
552
void BIF_setLocalAxisConstraint(char axis, char *text) {
553
TransInfo *t = BIF_GetTransInfo();
557
setLocalConstraint(t, CON_AXIS0, text);
560
setLocalConstraint(t, CON_AXIS1, text);
563
setLocalConstraint(t, CON_AXIS2, text);
568
void BIF_setLocalLockConstraint(char axis, char *text) {
569
TransInfo *t = BIF_GetTransInfo();
573
setLocalConstraint(t, (CON_AXIS1|CON_AXIS2), text);
576
setLocalConstraint(t, (CON_AXIS0|CON_AXIS2), text);
579
setLocalConstraint(t, (CON_AXIS0|CON_AXIS1), text);
584
void setLocalConstraint(TransInfo *t, int mode, const char text[]) {
585
if (t->flag & T_EDIT) {
587
Mat3CpyMat4(obmat, G.obedit->obmat);
588
setConstraint(t, obmat, mode|CON_LOCAL, text);
592
setConstraint(t, t->data->axismtx, mode|CON_LOCAL, text);
595
strncpy(t->con.text + 1, text, 48);
596
Mat3CpyMat3(t->con.mtx, t->data->axismtx);
597
t->con.mode = mode|CON_LOCAL;
598
getConstraintMatrix(t);
602
t->con.drawExtra = drawObjectConstraint;
603
t->con.applyVec = applyObjectConstraintVec;
604
t->con.applySize = applyObjectConstraintSize;
605
t->con.applyRot = applyObjectConstraintRot;
611
/* text is optional, for header print */
612
void BIF_setSingleAxisConstraint(float vec[3], char *text) {
613
TransInfo *t = BIF_GetTransInfo();
614
float space[3][3], v[3];
616
VECCOPY(space[0], vec);
622
Crossf(space[1], vec, v);
623
Crossf(space[2], vec, space[1]);
626
Mat3CpyMat3(t->con.mtx, space);
627
t->con.mode = (CON_AXIS0|CON_APPLY);
628
getConstraintMatrix(t);
630
/* start copying with an offset of 1, to reserve a spot for the SPACE char */
631
if(text) strncpy(t->con.text+1, text, 48); // 50 in struct
634
t->con.drawExtra = NULL;
635
t->con.applyVec = applyAxisConstraintVec;
636
t->con.applySize = applyAxisConstraintSize;
637
t->con.applyRot = applyAxisConstraintRot;
641
void BIF_setDualAxisConstraint(float vec1[3], float vec2[3], char *text) {
642
TransInfo *t = BIF_GetTransInfo();
645
VECCOPY(space[0], vec1);
646
VECCOPY(space[1], vec2);
647
Crossf(space[2], space[0], space[1]);
650
Mat3CpyMat3(t->con.mtx, space);
651
t->con.mode = (CON_AXIS0|CON_AXIS1|CON_APPLY);
652
getConstraintMatrix(t);
654
/* start copying with an offset of 1, to reserve a spot for the SPACE char */
655
if(text) strncpy(t->con.text+1, text, 48); // 50 in struct
657
t->con.drawExtra = NULL;
658
t->con.applyVec = applyAxisConstraintVec;
659
t->con.applySize = applyAxisConstraintSize;
660
t->con.applyRot = applyAxisConstraintRot;
665
void BIF_drawConstraint(void)
667
TransInfo *t = BIF_GetTransInfo();
668
TransCon *tc = &(t->con);
670
if (!(tc->mode & CON_APPLY))
672
if (t->flag & T_USES_MANIPULATOR)
675
/* nasty exception for Z constraint in camera view */
676
if( (t->flag & T_OBJECT) && G.vd->camera==OBACT && G.vd->persp>1)
683
if (tc->mode & CON_SELECT) {
686
char col2[3] = {255,255,255};
687
getmouseco_areawin(mval);
688
window_to_3d(vec, (short)(mval[0] - t->con.imval[0]), (short)(mval[1] - t->con.imval[1]));
689
VecAddf(vec, vec, tc->center);
691
drawLine(tc->center, tc->mtx[0], 'x', 0);
692
drawLine(tc->center, tc->mtx[1], 'y', 0);
693
drawLine(tc->center, tc->mtx[2], 'z', 0);
697
glDisable(GL_DEPTH_TEST);
699
glBegin(GL_LINE_STRIP);
700
glVertex3fv(tc->center);
704
if(G.zbuf) glEnable(GL_DEPTH_TEST); // warning for global!
707
if (tc->mode & CON_AXIS0) {
708
drawLine(tc->center, tc->mtx[0], 'x', DRAWLIGHT);
710
if (tc->mode & CON_AXIS1) {
711
drawLine(tc->center, tc->mtx[1], 'y', DRAWLIGHT);
713
if (tc->mode & CON_AXIS2) {
714
drawLine(tc->center, tc->mtx[2], 'z', DRAWLIGHT);
719
/* called from drawview.c, as an extra per-window draw option */
720
void BIF_drawPropCircle()
722
TransInfo *t = BIF_GetTransInfo();
724
if (t->flag & T_PROP_EDIT) {
725
float tmat[4][4], imat[4][4];
727
BIF_ThemeColor(TH_GRID);
729
/* if editmode we need to go into object space */
730
if(G.obedit) mymultmatrix(G.obedit->obmat);
733
Mat4Invert(imat, tmat);
735
drawcircball(GL_LINE_LOOP, t->center, t->propsize, imat);
737
/* if editmode we restore */
738
if(G.obedit) myloadmatrix(G.vd->viewmat);
742
int isLockConstraint(TransInfo *t) {
743
int mode = t->con.mode;
745
if ( (mode & (CON_AXIS0|CON_AXIS1)) == (CON_AXIS0|CON_AXIS1))
748
if ( (mode & (CON_AXIS1|CON_AXIS2)) == (CON_AXIS1|CON_AXIS2))
751
if ( (mode & (CON_AXIS0|CON_AXIS2)) == (CON_AXIS0|CON_AXIS2))
757
void initConstraint(TransInfo *t) {
758
if (t->con.mode & CON_APPLY) {
763
void startConstraint(TransInfo *t) {
764
t->con.mode |= CON_APPLY;
766
t->num.idx_max = MIN2(getConstraintSpaceDimension(t) - 1, t->idx_max);
769
void stopConstraint(TransInfo *t) {
770
t->con.mode &= ~(CON_APPLY|CON_SELECT);
772
t->num.idx_max = t->idx_max;
775
void getConstraintMatrix(TransInfo *t)
778
Mat3Inv(t->con.imtx, t->con.mtx);
779
Mat3One(t->con.pmtx);
781
if (!(t->con.mode & CON_AXIS0)) {
784
t->con.pmtx[0][2] = 0.0f;
787
if (!(t->con.mode & CON_AXIS1)) {
790
t->con.pmtx[1][2] = 0.0f;
793
if (!(t->con.mode & CON_AXIS2)) {
796
t->con.pmtx[2][2] = 0.0f;
799
Mat3MulMat3(mat, t->con.pmtx, t->con.imtx);
800
Mat3MulMat3(t->con.pmtx, t->con.mtx, mat);
803
void initSelectConstraint(TransInfo *t, float mtx[3][3])
805
Mat3CpyMat3(t->con.mtx, mtx);
806
t->con.mode |= CON_APPLY;
807
t->con.mode |= CON_SELECT;
808
t->con.mode &= ~CON_LOCAL;
811
t->con.drawExtra = NULL;
812
t->con.applyVec = applyAxisConstraintVec;
813
t->con.applySize = applyAxisConstraintSize;
814
t->con.applyRot = applyAxisConstraintRot;
817
void selectConstraint(TransInfo *t) {
818
if (t->con.mode & CON_SELECT) {
824
void postSelectConstraint(TransInfo *t)
826
if (!(t->con.mode & CON_SELECT))
829
t->con.mode &= ~CON_AXIS0;
830
t->con.mode &= ~CON_AXIS1;
831
t->con.mode &= ~CON_AXIS2;
832
t->con.mode &= ~CON_SELECT;
840
void setNearestAxis(TransInfo *t)
843
float mvec[3], axis[3], proj[3];
848
t->con.mode &= ~CON_AXIS0;
849
t->con.mode &= ~CON_AXIS1;
850
t->con.mode &= ~CON_AXIS2;
852
getmouseco_areawin(coord);
853
mvec[0] = (float)(coord[0] - t->con.imval[0]);
854
mvec[1] = (float)(coord[1] - t->con.imval[1]);
858
/* we need to correct axis length for the current zoomlevel of view,
859
this to prevent projected values to be clipped behind the camera
860
and to overflow the short integers.
861
The formula used is a bit stupid, just a simplification of the substraction
862
of two 2D points 30 pixels apart (that's the last factor in the formula) after
863
projecting them with window_to_3d and then get the length of that vector.
865
zfac= G.vd->persmat[0][3]*t->center[0]+ G.vd->persmat[1][3]*t->center[1]+ G.vd->persmat[2][3]*t->center[2]+ G.vd->persmat[3][3];
866
zfac = VecLength(G.vd->persinv[0]) * 2.0f/curarea->winx * zfac * 30.0f;
868
for (i = 0; i<3; i++) {
869
VECCOPY(axis, t->con.mtx[i]);
872
/* now we can project to get window coordinate */
873
VecAddf(axis, axis, t->con.center);
874
project_int(axis, icoord);
876
axis[0] = (float)(icoord[0] - t->center2d[0]);
877
axis[1] = (float)(icoord[1] - t->center2d[1]);
880
if (Normalise(axis) != 0.0f) {
881
Projf(proj, mvec, axis);
882
VecSubf(axis, mvec, proj);
883
len[i] = Normalise(axis);
886
len[i] = 10000000000.0f;
890
if (len[0] <= len[1] && len[0] <= len[2]) {
891
if (G.qual & LR_SHIFTKEY) {
892
t->con.mode |= (CON_AXIS1|CON_AXIS2);
893
strcpy(t->con.text, " locking global X");
896
t->con.mode |= CON_AXIS0;
897
strcpy(t->con.text, " along global X");
900
else if (len[1] <= len[0] && len[1] <= len[2]) {
901
if (G.qual & LR_SHIFTKEY) {
902
t->con.mode |= (CON_AXIS0|CON_AXIS2);
903
strcpy(t->con.text, " locking global Y");
906
t->con.mode |= CON_AXIS1;
907
strcpy(t->con.text, " along global Y");
910
else if (len[2] <= len[1] && len[2] <= len[0]) {
911
if (G.qual & LR_SHIFTKEY) {
912
t->con.mode |= (CON_AXIS0|CON_AXIS1);
913
strcpy(t->con.text, " locking global Z");
916
t->con.mode |= CON_AXIS2;
917
strcpy(t->con.text, " along global Z");
920
getConstraintMatrix(t);
923
char constraintModeToChar(TransInfo *t) {
924
if ((t->con.mode & CON_APPLY)==0) {
927
switch (t->con.mode & (CON_AXIS0|CON_AXIS1|CON_AXIS2)) {
929
case (CON_AXIS1|CON_AXIS2):
932
case (CON_AXIS0|CON_AXIS2):
935
case (CON_AXIS0|CON_AXIS1):