From e277e8d085037414f34b27f9c1a26cbbf2507c3e Mon Sep 17 00:00:00 2001 From: mano-wii Date: Fri, 14 Feb 2020 08:42:17 -0300 Subject: Cleanup: Split transform.c in multiple files Differential Revision: https://developer.blender.org/D5819 --- source/blender/editors/transform/transform_mode.c | 1100 +++++++++++++++++++++ 1 file changed, 1100 insertions(+) create mode 100644 source/blender/editors/transform/transform_mode.c (limited to 'source/blender/editors/transform/transform_mode.c') diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c new file mode 100644 index 00000000000..2368e0f0dc4 --- /dev/null +++ b/source/blender/editors/transform/transform_mode.c @@ -0,0 +1,1100 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup edtransform + */ + +#include + +#include "DNA_anim_types.h" +#include "DNA_constraint_types.h" +#include "DNA_gpencil_types.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" + +#include "BKE_constraint.h" +#include "BKE_context.h" +#include "BKE_nla.h" + +#include "ED_screen.h" + +#include "UI_interface.h" + +#include "BLT_translation.h" + +#include "transform.h" +#include "transform_snap.h" + +/* Own include. */ +#include "transform_mode.h" + +bool transdata_check_local_center(TransInfo *t, short around) +{ + return ((around == V3D_AROUND_LOCAL_ORIGINS) && + ((t->flag & (T_OBJECT | T_POSE)) || + /* implicit: (t->flag & T_EDIT) */ + (ELEM(t->obedit_type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE, OB_GPENCIL)) || + (t->spacetype == SPACE_GRAPH) || + (t->options & (CTX_MOVIECLIP | CTX_MASK | CTX_PAINT_CURVE)))); +} + +/* ************************** TRANSFORM LOCKS **************************** */ + +void protectedTransBits(short protectflag, float vec[3]) +{ + if (protectflag & OB_LOCK_LOCX) { + vec[0] = 0.0f; + } + if (protectflag & OB_LOCK_LOCY) { + vec[1] = 0.0f; + } + if (protectflag & OB_LOCK_LOCZ) { + vec[2] = 0.0f; + } +} + +/* this function only does the delta rotation */ +static void protectedQuaternionBits(short protectflag, float quat[4], const float oldquat[4]) +{ + /* check that protection flags are set */ + if ((protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) == 0) { + return; + } + + if (protectflag & OB_LOCK_ROT4D) { + /* quaternions getting limited as 4D entities that they are... */ + if (protectflag & OB_LOCK_ROTW) { + quat[0] = oldquat[0]; + } + if (protectflag & OB_LOCK_ROTX) { + quat[1] = oldquat[1]; + } + if (protectflag & OB_LOCK_ROTY) { + quat[2] = oldquat[2]; + } + if (protectflag & OB_LOCK_ROTZ) { + quat[3] = oldquat[3]; + } + } + else { + /* quaternions get limited with euler... (compatibility mode) */ + float eul[3], oldeul[3], nquat[4], noldquat[4]; + float qlen; + + qlen = normalize_qt_qt(nquat, quat); + normalize_qt_qt(noldquat, oldquat); + + quat_to_eul(eul, nquat); + quat_to_eul(oldeul, noldquat); + + if (protectflag & OB_LOCK_ROTX) { + eul[0] = oldeul[0]; + } + if (protectflag & OB_LOCK_ROTY) { + eul[1] = oldeul[1]; + } + if (protectflag & OB_LOCK_ROTZ) { + eul[2] = oldeul[2]; + } + + eul_to_quat(quat, eul); + + /* restore original quat size */ + mul_qt_fl(quat, qlen); + + /* quaternions flip w sign to accumulate rotations correctly */ + if ((nquat[0] < 0.0f && quat[0] > 0.0f) || (nquat[0] > 0.0f && quat[0] < 0.0f)) { + mul_qt_fl(quat, -1.0f); + } + } +} + +static void protectedRotateBits(short protectflag, float eul[3], const float oldeul[3]) +{ + if (protectflag & OB_LOCK_ROTX) { + eul[0] = oldeul[0]; + } + if (protectflag & OB_LOCK_ROTY) { + eul[1] = oldeul[1]; + } + if (protectflag & OB_LOCK_ROTZ) { + eul[2] = oldeul[2]; + } +} + +/* this function only does the delta rotation */ +/* axis-angle is usually internally stored as quats... */ +static void protectedAxisAngleBits( + short protectflag, float axis[3], float *angle, float oldAxis[3], float oldAngle) +{ + /* check that protection flags are set */ + if ((protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) == 0) { + return; + } + + if (protectflag & OB_LOCK_ROT4D) { + /* axis-angle getting limited as 4D entities that they are... */ + if (protectflag & OB_LOCK_ROTW) { + *angle = oldAngle; + } + if (protectflag & OB_LOCK_ROTX) { + axis[0] = oldAxis[0]; + } + if (protectflag & OB_LOCK_ROTY) { + axis[1] = oldAxis[1]; + } + if (protectflag & OB_LOCK_ROTZ) { + axis[2] = oldAxis[2]; + } + } + else { + /* axis-angle get limited with euler... */ + float eul[3], oldeul[3]; + + axis_angle_to_eulO(eul, EULER_ORDER_DEFAULT, axis, *angle); + axis_angle_to_eulO(oldeul, EULER_ORDER_DEFAULT, oldAxis, oldAngle); + + if (protectflag & OB_LOCK_ROTX) { + eul[0] = oldeul[0]; + } + if (protectflag & OB_LOCK_ROTY) { + eul[1] = oldeul[1]; + } + if (protectflag & OB_LOCK_ROTZ) { + eul[2] = oldeul[2]; + } + + eulO_to_axis_angle(axis, angle, eul, EULER_ORDER_DEFAULT); + + /* When converting to axis-angle, + * we need a special exception for the case when there is no axis. */ + if (IS_EQF(axis[0], axis[1]) && IS_EQF(axis[1], axis[2])) { + /* for now, rotate around y-axis then (so that it simply becomes the roll) */ + axis[1] = 1.0f; + } + } +} + +static void protectedSizeBits(short protectflag, float size[3]) +{ + if (protectflag & OB_LOCK_SCALEX) { + size[0] = 1.0f; + } + if (protectflag & OB_LOCK_SCALEY) { + size[1] = 1.0f; + } + if (protectflag & OB_LOCK_SCALEZ) { + size[2] = 1.0f; + } +} + +/* ******************* TRANSFORM LIMITS ********************** */ + +void constraintTransLim(TransInfo *t, TransData *td) +{ + if (td->con) { + const bConstraintTypeInfo *ctiLoc = BKE_constraint_typeinfo_from_type( + CONSTRAINT_TYPE_LOCLIMIT); + const bConstraintTypeInfo *ctiDist = BKE_constraint_typeinfo_from_type( + CONSTRAINT_TYPE_DISTLIMIT); + + bConstraintOb cob = {NULL}; + bConstraint *con; + float ctime = (float)(t->scene->r.cfra); + + /* Make a temporary bConstraintOb for using these limit constraints + * - they only care that cob->matrix is correctly set ;-) + * - current space should be local + */ + unit_m4(cob.matrix); + copy_v3_v3(cob.matrix[3], td->loc); + + /* Evaluate valid constraints */ + for (con = td->con; con; con = con->next) { + const bConstraintTypeInfo *cti = NULL; + ListBase targets = {NULL, NULL}; + + /* only consider constraint if enabled */ + if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) { + continue; + } + if (con->enforce == 0.0f) { + continue; + } + + /* only use it if it's tagged for this purpose (and the right type) */ + if (con->type == CONSTRAINT_TYPE_LOCLIMIT) { + bLocLimitConstraint *data = (bLocLimitConstraint *)con->data; + + if ((data->flag2 & LIMIT_TRANSFORM) == 0) { + continue; + } + cti = ctiLoc; + } + else if (con->type == CONSTRAINT_TYPE_DISTLIMIT) { + bDistLimitConstraint *data = (bDistLimitConstraint *)con->data; + + if ((data->flag & LIMITDIST_TRANSFORM) == 0) { + continue; + } + cti = ctiDist; + } + + if (cti) { + /* do space conversions */ + if (con->ownspace == CONSTRAINT_SPACE_WORLD) { + /* just multiply by td->mtx (this should be ok) */ + mul_m4_m3m4(cob.matrix, td->mtx, cob.matrix); + } + else if (con->ownspace != CONSTRAINT_SPACE_LOCAL) { + /* skip... incompatible spacetype */ + continue; + } + + /* get constraint targets if needed */ + BKE_constraint_targets_for_solving_get(t->depsgraph, con, &cob, &targets, ctime); + + /* do constraint */ + cti->evaluate_constraint(con, &cob, &targets); + + /* convert spaces again */ + if (con->ownspace == CONSTRAINT_SPACE_WORLD) { + /* just multiply by td->smtx (this should be ok) */ + mul_m4_m3m4(cob.matrix, td->smtx, cob.matrix); + } + + /* free targets list */ + BLI_freelistN(&targets); + } + } + + /* copy results from cob->matrix */ + copy_v3_v3(td->loc, cob.matrix[3]); + } +} + +static void constraintob_from_transdata(bConstraintOb *cob, TransData *td) +{ + /* Make a temporary bConstraintOb for use by limit constraints + * - they only care that cob->matrix is correctly set ;-) + * - current space should be local + */ + memset(cob, 0, sizeof(bConstraintOb)); + if (td->ext) { + if (td->ext->rotOrder == ROT_MODE_QUAT) { + /* quats */ + /* objects and bones do normalization first too, otherwise + * we don't necessarily end up with a rotation matrix, and + * then conversion back to quat gives a different result */ + float quat[4]; + normalize_qt_qt(quat, td->ext->quat); + quat_to_mat4(cob->matrix, quat); + } + else if (td->ext->rotOrder == ROT_MODE_AXISANGLE) { + /* axis angle */ + axis_angle_to_mat4(cob->matrix, td->ext->rotAxis, *td->ext->rotAngle); + } + else { + /* eulers */ + eulO_to_mat4(cob->matrix, td->ext->rot, td->ext->rotOrder); + } + } +} + +static void constraintRotLim(TransInfo *UNUSED(t), TransData *td) +{ + if (td->con) { + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_ROTLIMIT); + bConstraintOb cob; + bConstraint *con; + bool do_limit = false; + + /* Evaluate valid constraints */ + for (con = td->con; con; con = con->next) { + /* only consider constraint if enabled */ + if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) { + continue; + } + if (con->enforce == 0.0f) { + continue; + } + + /* we're only interested in Limit-Rotation constraints */ + if (con->type == CONSTRAINT_TYPE_ROTLIMIT) { + bRotLimitConstraint *data = (bRotLimitConstraint *)con->data; + + /* only use it if it's tagged for this purpose */ + if ((data->flag2 & LIMIT_TRANSFORM) == 0) { + continue; + } + + /* skip incompatible spacetypes */ + if (!ELEM(con->ownspace, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL)) { + continue; + } + + /* only do conversion if necessary, to preserve quats and eulers */ + if (do_limit == false) { + constraintob_from_transdata(&cob, td); + do_limit = true; + } + + /* do space conversions */ + if (con->ownspace == CONSTRAINT_SPACE_WORLD) { + /* just multiply by td->mtx (this should be ok) */ + mul_m4_m3m4(cob.matrix, td->mtx, cob.matrix); + } + + /* do constraint */ + cti->evaluate_constraint(con, &cob, NULL); + + /* convert spaces again */ + if (con->ownspace == CONSTRAINT_SPACE_WORLD) { + /* just multiply by td->smtx (this should be ok) */ + mul_m4_m3m4(cob.matrix, td->smtx, cob.matrix); + } + } + } + + if (do_limit) { + /* copy results from cob->matrix */ + if (td->ext->rotOrder == ROT_MODE_QUAT) { + /* quats */ + mat4_to_quat(td->ext->quat, cob.matrix); + } + else if (td->ext->rotOrder == ROT_MODE_AXISANGLE) { + /* axis angle */ + mat4_to_axis_angle(td->ext->rotAxis, td->ext->rotAngle, cob.matrix); + } + else { + /* eulers */ + mat4_to_eulO(td->ext->rot, td->ext->rotOrder, cob.matrix); + } + } + } +} + +static void constraintSizeLim(TransInfo *t, TransData *td) +{ + if (td->con && td->ext) { + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_SIZELIMIT); + bConstraintOb cob = {NULL}; + bConstraint *con; + float size_sign[3], size_abs[3]; + int i; + + /* Make a temporary bConstraintOb for using these limit constraints + * - they only care that cob->matrix is correctly set ;-) + * - current space should be local + */ + if ((td->flag & TD_SINGLESIZE) && !(t->con.mode & CON_APPLY)) { + /* scale val and reset size */ + return; // TODO: fix this case + } + else { + /* Reset val if SINGLESIZE but using a constraint */ + if (td->flag & TD_SINGLESIZE) { + return; + } + + /* separate out sign to apply back later */ + for (i = 0; i < 3; i++) { + size_sign[i] = signf(td->ext->size[i]); + size_abs[i] = fabsf(td->ext->size[i]); + } + + size_to_mat4(cob.matrix, size_abs); + } + + /* Evaluate valid constraints */ + for (con = td->con; con; con = con->next) { + /* only consider constraint if enabled */ + if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) { + continue; + } + if (con->enforce == 0.0f) { + continue; + } + + /* we're only interested in Limit-Scale constraints */ + if (con->type == CONSTRAINT_TYPE_SIZELIMIT) { + bSizeLimitConstraint *data = con->data; + + /* only use it if it's tagged for this purpose */ + if ((data->flag2 & LIMIT_TRANSFORM) == 0) { + continue; + } + + /* do space conversions */ + if (con->ownspace == CONSTRAINT_SPACE_WORLD) { + /* just multiply by td->mtx (this should be ok) */ + mul_m4_m3m4(cob.matrix, td->mtx, cob.matrix); + } + else if (con->ownspace != CONSTRAINT_SPACE_LOCAL) { + /* skip... incompatible spacetype */ + continue; + } + + /* do constraint */ + cti->evaluate_constraint(con, &cob, NULL); + + /* convert spaces again */ + if (con->ownspace == CONSTRAINT_SPACE_WORLD) { + /* just multiply by td->smtx (this should be ok) */ + mul_m4_m3m4(cob.matrix, td->smtx, cob.matrix); + } + } + } + + /* copy results from cob->matrix */ + if ((td->flag & TD_SINGLESIZE) && !(t->con.mode & CON_APPLY)) { + /* scale val and reset size */ + return; // TODO: fix this case + } + else { + /* Reset val if SINGLESIZE but using a constraint */ + if (td->flag & TD_SINGLESIZE) { + return; + } + + /* extrace scale from matrix and apply back sign */ + mat4_to_size(td->ext->size, cob.matrix); + mul_v3_v3(td->ext->size, size_sign); + } + } +} + +/* -------------------------------------------------------------------- */ +/* Transform (Rotaion Utils) */ + +/** \name Transform Rotaion Utils + * \{ */ +/* Used by Transform Rotation and Transform Normal Rotation */ +void headerRotation(TransInfo *t, char str[UI_MAX_DRAW_STR], float final) +{ + size_t ofs = 0; + + if (hasNumInput(&t->num)) { + char c[NUM_STR_REP_LEN]; + + outputNumInput(&(t->num), c, &t->scene->unit); + + ofs += BLI_snprintf( + str + ofs, UI_MAX_DRAW_STR - ofs, TIP_("Rot: %s %s %s"), &c[0], t->con.text, t->proptext); + } + else { + ofs += BLI_snprintf(str + ofs, + UI_MAX_DRAW_STR - ofs, + TIP_("Rot: %.2f%s %s"), + RAD2DEGF(final), + t->con.text, + t->proptext); + } + + if (t->flag & T_PROP_EDIT_ALL) { + ofs += BLI_snprintf( + str + ofs, UI_MAX_DRAW_STR - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); + } +} + +void postInputRotation(TransInfo *t, float values[3]) +{ + float axis_final[3]; + copy_v3_v3(axis_final, t->orient_matrix[t->orient_axis]); + if ((t->con.mode & CON_APPLY) && t->con.applyRot) { + t->con.applyRot(t, NULL, NULL, axis_final, values); + } +} + +/** + * Applies values of rotation to `td->loc` and `td->ext->quat` + * based on a rotation matrix (mat) and a pivot (center). + * + * Protected axis and other transform settings are taken into account. + */ +void ElementRotation_ex(TransInfo *t, + TransDataContainer *tc, + TransData *td, + const float mat[3][3], + const float *center) +{ + float vec[3], totmat[3][3], smat[3][3]; + float eul[3], fmat[3][3], quat[4]; + + if (t->flag & T_POINTS) { + mul_m3_m3m3(totmat, mat, td->mtx); + mul_m3_m3m3(smat, td->smtx, totmat); + + /* apply gpencil falloff */ + if (t->options & CTX_GPENCIL_STROKES) { + bGPDstroke *gps = (bGPDstroke *)td->extra; + float sx = smat[0][0]; + float sy = smat[1][1]; + float sz = smat[2][2]; + + mul_m3_fl(smat, gps->runtime.multi_frame_falloff); + /* fix scale */ + smat[0][0] = sx; + smat[1][1] = sy; + smat[2][2] = sz; + } + + sub_v3_v3v3(vec, td->iloc, center); + mul_m3_v3(smat, vec); + + add_v3_v3v3(td->loc, vec, center); + + sub_v3_v3v3(vec, td->loc, td->iloc); + protectedTransBits(td->protectflag, vec); + add_v3_v3v3(td->loc, td->iloc, vec); + + if (td->flag & TD_USEQUAT) { + mul_m3_series(fmat, td->smtx, mat, td->mtx); + mat3_to_quat(quat, fmat); // Actual transform + + if (td->ext->quat) { + mul_qt_qtqt(td->ext->quat, quat, td->ext->iquat); + + /* is there a reason not to have this here? -jahka */ + protectedQuaternionBits(td->protectflag, td->ext->quat, td->ext->iquat); + } + } + } + /** + * HACK WARNING + * + * This is some VERY ugly special case to deal with pose mode. + * + * The problem is that mtx and smtx include each bone orientation. + * + * That is needed to rotate each bone properly, HOWEVER, to calculate + * the translation component, we only need the actual armature object's + * matrix (and inverse). That is not all though. Once the proper translation + * has been computed, it has to be converted back into the bone's space. + */ + else if (t->flag & T_POSE) { + // Extract and invert armature object matrix + + if ((td->flag & TD_NO_LOC) == 0) { + sub_v3_v3v3(vec, td->center, center); + + mul_m3_v3(tc->mat3, vec); // To Global space + mul_m3_v3(mat, vec); // Applying rotation + mul_m3_v3(tc->imat3, vec); // To Local space + + add_v3_v3(vec, center); + /* vec now is the location where the object has to be */ + + sub_v3_v3v3(vec, vec, td->center); // Translation needed from the initial location + + /* special exception, see TD_PBONE_LOCAL_MTX definition comments */ + if (td->flag & TD_PBONE_LOCAL_MTX_P) { + /* do nothing */ + } + else if (td->flag & TD_PBONE_LOCAL_MTX_C) { + mul_m3_v3(tc->mat3, vec); // To Global space + mul_m3_v3(td->ext->l_smtx, vec); // To Pose space (Local Location) + } + else { + mul_m3_v3(tc->mat3, vec); // To Global space + mul_m3_v3(td->smtx, vec); // To Pose space + } + + protectedTransBits(td->protectflag, vec); + + add_v3_v3v3(td->loc, td->iloc, vec); + + constraintTransLim(t, td); + } + + /* rotation */ + /* MORE HACK: as in some cases the matrix to apply location and rot/scale is not the same, + * and ElementRotation() might be called in Translation context (with align snapping), + * we need to be sure to actually use the *rotation* matrix here... + * So no other way than storing it in some dedicated members of td->ext! */ + if ((t->flag & T_V3D_ALIGN) == 0) { /* align mode doesn't rotate objects itself */ + /* euler or quaternion/axis-angle? */ + if (td->ext->rotOrder == ROT_MODE_QUAT) { + mul_m3_series(fmat, td->ext->r_smtx, mat, td->ext->r_mtx); + + mat3_to_quat(quat, fmat); /* Actual transform */ + + mul_qt_qtqt(td->ext->quat, quat, td->ext->iquat); + /* this function works on end result */ + protectedQuaternionBits(td->protectflag, td->ext->quat, td->ext->iquat); + } + else if (td->ext->rotOrder == ROT_MODE_AXISANGLE) { + /* calculate effect based on quats */ + float iquat[4], tquat[4]; + + axis_angle_to_quat(iquat, td->ext->irotAxis, td->ext->irotAngle); + + mul_m3_series(fmat, td->ext->r_smtx, mat, td->ext->r_mtx); + mat3_to_quat(quat, fmat); /* Actual transform */ + mul_qt_qtqt(tquat, quat, iquat); + + quat_to_axis_angle(td->ext->rotAxis, td->ext->rotAngle, tquat); + + /* this function works on end result */ + protectedAxisAngleBits(td->protectflag, + td->ext->rotAxis, + td->ext->rotAngle, + td->ext->irotAxis, + td->ext->irotAngle); + } + else { + float eulmat[3][3]; + + mul_m3_m3m3(totmat, mat, td->ext->r_mtx); + mul_m3_m3m3(smat, td->ext->r_smtx, totmat); + + /* calculate the total rotatation in eulers */ + copy_v3_v3(eul, td->ext->irot); + eulO_to_mat3(eulmat, eul, td->ext->rotOrder); + + /* mat = transform, obmat = bone rotation */ + mul_m3_m3m3(fmat, smat, eulmat); + + mat3_to_compatible_eulO(eul, td->ext->rot, td->ext->rotOrder, fmat); + + /* and apply (to end result only) */ + protectedRotateBits(td->protectflag, eul, td->ext->irot); + copy_v3_v3(td->ext->rot, eul); + } + + constraintRotLim(t, td); + } + } + else { + if ((td->flag & TD_NO_LOC) == 0) { + /* translation */ + sub_v3_v3v3(vec, td->center, center); + mul_m3_v3(mat, vec); + add_v3_v3(vec, center); + /* vec now is the location where the object has to be */ + sub_v3_v3(vec, td->center); + mul_m3_v3(td->smtx, vec); + + protectedTransBits(td->protectflag, vec); + + add_v3_v3v3(td->loc, td->iloc, vec); + } + + constraintTransLim(t, td); + + /* rotation */ + if ((t->flag & T_V3D_ALIGN) == 0) { // align mode doesn't rotate objects itself + /* euler or quaternion? */ + if ((td->ext->rotOrder == ROT_MODE_QUAT) || (td->flag & TD_USEQUAT)) { + /* can be called for texture space translate for example, then opt out */ + if (td->ext->quat) { + mul_m3_series(fmat, td->smtx, mat, td->mtx); + mat3_to_quat(quat, fmat); // Actual transform + + mul_qt_qtqt(td->ext->quat, quat, td->ext->iquat); + /* this function works on end result */ + protectedQuaternionBits(td->protectflag, td->ext->quat, td->ext->iquat); + } + } + else if (td->ext->rotOrder == ROT_MODE_AXISANGLE) { + /* calculate effect based on quats */ + float iquat[4], tquat[4]; + + axis_angle_to_quat(iquat, td->ext->irotAxis, td->ext->irotAngle); + + mul_m3_series(fmat, td->smtx, mat, td->mtx); + mat3_to_quat(quat, fmat); // Actual transform + mul_qt_qtqt(tquat, quat, iquat); + + quat_to_axis_angle(td->ext->rotAxis, td->ext->rotAngle, tquat); + + /* this function works on end result */ + protectedAxisAngleBits(td->protectflag, + td->ext->rotAxis, + td->ext->rotAngle, + td->ext->irotAxis, + td->ext->irotAngle); + } + else { + float obmat[3][3]; + + mul_m3_m3m3(totmat, mat, td->mtx); + mul_m3_m3m3(smat, td->smtx, totmat); + + /* calculate the total rotatation in eulers */ + add_v3_v3v3(eul, td->ext->irot, td->ext->drot); /* correct for delta rot */ + eulO_to_mat3(obmat, eul, td->ext->rotOrder); + /* mat = transform, obmat = object rotation */ + mul_m3_m3m3(fmat, smat, obmat); + + mat3_to_compatible_eulO(eul, td->ext->rot, td->ext->rotOrder, fmat); + + /* correct back for delta rot */ + sub_v3_v3v3(eul, eul, td->ext->drot); + + /* and apply */ + protectedRotateBits(td->protectflag, eul, td->ext->irot); + copy_v3_v3(td->ext->rot, eul); + } + + constraintRotLim(t, td); + } + } +} + +void ElementRotation( + TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3], const short around) +{ + const float *center; + + /* local constraint shouldn't alter center */ + if (transdata_check_local_center(t, around)) { + center = td->center; + } + else { + center = tc->center_local; + } + + ElementRotation_ex(t, tc, td, mat, center); +} +/** \} */ + +/* -------------------------------------------------------------------- */ +/* Transform (Resize Utils) */ + +/** \name Transform Resize Utils + * \{ */ +void headerResize(TransInfo *t, const float vec[3], char str[UI_MAX_DRAW_STR]) +{ + char tvec[NUM_STR_REP_LEN * 3]; + size_t ofs = 0; + if (hasNumInput(&t->num)) { + outputNumInput(&(t->num), tvec, &t->scene->unit); + } + else { + BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", vec[0]); + BLI_snprintf(&tvec[NUM_STR_REP_LEN], NUM_STR_REP_LEN, "%.4f", vec[1]); + BLI_snprintf(&tvec[NUM_STR_REP_LEN * 2], NUM_STR_REP_LEN, "%.4f", vec[2]); + } + + if (t->con.mode & CON_APPLY) { + switch (t->num.idx_max) { + case 0: + ofs += BLI_snprintf(str + ofs, + UI_MAX_DRAW_STR - ofs, + TIP_("Scale: %s%s %s"), + &tvec[0], + t->con.text, + t->proptext); + break; + case 1: + ofs += BLI_snprintf(str + ofs, + UI_MAX_DRAW_STR - ofs, + TIP_("Scale: %s : %s%s %s"), + &tvec[0], + &tvec[NUM_STR_REP_LEN], + t->con.text, + t->proptext); + break; + case 2: + ofs += BLI_snprintf(str + ofs, + UI_MAX_DRAW_STR - ofs, + TIP_("Scale: %s : %s : %s%s %s"), + &tvec[0], + &tvec[NUM_STR_REP_LEN], + &tvec[NUM_STR_REP_LEN * 2], + t->con.text, + t->proptext); + break; + } + } + else { + if (t->flag & T_2D_EDIT) { + ofs += BLI_snprintf(str + ofs, + UI_MAX_DRAW_STR - ofs, + TIP_("Scale X: %s Y: %s%s %s"), + &tvec[0], + &tvec[NUM_STR_REP_LEN], + t->con.text, + t->proptext); + } + else { + ofs += BLI_snprintf(str + ofs, + UI_MAX_DRAW_STR - ofs, + TIP_("Scale X: %s Y: %s Z: %s%s %s"), + &tvec[0], + &tvec[NUM_STR_REP_LEN], + &tvec[NUM_STR_REP_LEN * 2], + t->con.text, + t->proptext); + } + } + + if (t->flag & T_PROP_EDIT_ALL) { + ofs += BLI_snprintf( + str + ofs, UI_MAX_DRAW_STR - ofs, TIP_(" Proportional size: %.2f"), t->prop_size); + } +} + +/** + * \a smat is reference matrix only. + * + * \note this is a tricky area, before making changes see: T29633, T42444 + */ +static void TransMat3ToSize(float mat[3][3], float smat[3][3], float size[3]) +{ + float rmat[3][3]; + + mat3_to_rot_size(rmat, size, mat); + + /* first tried with dotproduct... but the sign flip is crucial */ + if (dot_v3v3(rmat[0], smat[0]) < 0.0f) { + size[0] = -size[0]; + } + if (dot_v3v3(rmat[1], smat[1]) < 0.0f) { + size[1] = -size[1]; + } + if (dot_v3v3(rmat[2], smat[2]) < 0.0f) { + size[2] = -size[2]; + } +} + +void ElementResize(TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3]) +{ + float tmat[3][3], smat[3][3], center[3]; + float vec[3]; + + if (t->flag & T_EDIT) { + mul_m3_m3m3(smat, mat, td->mtx); + mul_m3_m3m3(tmat, td->smtx, smat); + } + else { + copy_m3_m3(tmat, mat); + } + + if (t->con.applySize) { + t->con.applySize(t, tc, td, tmat); + } + + /* local constraint shouldn't alter center */ + if (transdata_check_local_center(t, t->around)) { + copy_v3_v3(center, td->center); + } + else if (t->options & CTX_MOVIECLIP) { + if (td->flag & TD_INDIVIDUAL_SCALE) { + copy_v3_v3(center, td->center); + } + else { + copy_v3_v3(center, tc->center_local); + } + } + else { + copy_v3_v3(center, tc->center_local); + } + + /* Size checked needed since the 3D cursor only uses rotation fields. */ + if (td->ext && td->ext->size) { + float fsize[3]; + + if ((t->options & CTX_SCULPT) || t->flag & (T_OBJECT | T_TEXTURE | T_POSE)) { + float obsizemat[3][3]; + /* Reorient the size mat to fit the oriented object. */ + mul_m3_m3m3(obsizemat, tmat, td->axismtx); + /* print_m3("obsizemat", obsizemat); */ + TransMat3ToSize(obsizemat, td->axismtx, fsize); + /* print_v3("fsize", fsize); */ + } + else { + mat3_to_size(fsize, tmat); + } + + protectedSizeBits(td->protectflag, fsize); + + if ((t->flag & T_V3D_ALIGN) == 0) { /* align mode doesn't resize objects itself */ + if ((td->flag & TD_SINGLESIZE) && !(t->con.mode & CON_APPLY)) { + /* scale val and reset size */ + *td->val = td->ival * (1 + (fsize[0] - 1) * td->factor); + + td->ext->size[0] = td->ext->isize[0]; + td->ext->size[1] = td->ext->isize[1]; + td->ext->size[2] = td->ext->isize[2]; + } + else { + /* Reset val if SINGLESIZE but using a constraint */ + if (td->flag & TD_SINGLESIZE) { + *td->val = td->ival; + } + + td->ext->size[0] = td->ext->isize[0] * (1 + (fsize[0] - 1) * td->factor); + td->ext->size[1] = td->ext->isize[1] * (1 + (fsize[1] - 1) * td->factor); + td->ext->size[2] = td->ext->isize[2] * (1 + (fsize[2] - 1) * td->factor); + } + } + + constraintSizeLim(t, td); + } + + /* For individual element center, Editmode need to use iloc */ + if (t->flag & T_POINTS) { + sub_v3_v3v3(vec, td->iloc, center); + } + else { + sub_v3_v3v3(vec, td->center, center); + } + + mul_m3_v3(tmat, vec); + + add_v3_v3(vec, center); + if (t->flag & T_POINTS) { + sub_v3_v3(vec, td->iloc); + } + else { + sub_v3_v3(vec, td->center); + } + + /* grease pencil falloff */ + if (t->options & CTX_GPENCIL_STROKES) { + bGPDstroke *gps = (bGPDstroke *)td->extra; + mul_v3_fl(vec, td->factor * gps->runtime.multi_frame_falloff); + + /* scale stroke thickness */ + if (td->val) { + snapGridIncrement(t, t->values_final); + applyNumInput(&t->num, t->values_final); + + float ratio = t->values_final[0]; + *td->val = td->ival * ratio * gps->runtime.multi_frame_falloff; + CLAMP_MIN(*td->val, 0.001f); + } + } + else { + mul_v3_fl(vec, td->factor); + } + + if (t->flag & (T_OBJECT | T_POSE)) { + mul_m3_v3(td->smtx, vec); + } + + protectedTransBits(td->protectflag, vec); + if (td->loc) { + add_v3_v3v3(td->loc, td->iloc, vec); + } + + constraintTransLim(t, td); +} +/** \} */ + +/* -------------------------------------------------------------------- */ +/* Transform (Frame Utils) */ + +/** \name Transform Frame Utils + * \{ */ + +/* This function returns the snapping 'mode' for Animation Editors only + * We cannot use the standard snapping due to NLA-strip scaling complexities. + */ +// XXX these modifier checks should be keymappable +short getAnimEdit_SnapMode(TransInfo *t) +{ + short autosnap = SACTSNAP_OFF; + + if (t->spacetype == SPACE_ACTION) { + SpaceAction *saction = (SpaceAction *)t->sa->spacedata.first; + + if (saction) { + autosnap = saction->autosnap; + } + } + else if (t->spacetype == SPACE_GRAPH) { + SpaceGraph *sipo = (SpaceGraph *)t->sa->spacedata.first; + + if (sipo) { + autosnap = sipo->autosnap; + } + } + else if (t->spacetype == SPACE_NLA) { + SpaceNla *snla = (SpaceNla *)t->sa->spacedata.first; + + if (snla) { + autosnap = snla->autosnap; + } + } + else { + autosnap = SACTSNAP_OFF; + } + + /* toggle autosnap on/off + * - when toggling on, prefer nearest frame over 1.0 frame increments + */ + if (t->modifiers & MOD_SNAP_INVERT) { + if (autosnap) { + autosnap = SACTSNAP_OFF; + } + else { + autosnap = SACTSNAP_FRAME; + } + } + + return autosnap; +} + +/* This function is used by Animation Editor specific transform functions to do + * the Snap Keyframe to Nearest Frame/Marker + */ +void doAnimEdit_SnapFrame( + TransInfo *t, TransData *td, TransData2D *td2d, AnimData *adt, short autosnap) +{ + if (ELEM(autosnap, SACTSNAP_FRAME, SACTSNAP_SECOND, SACTSNAP_MARKER)) { + float val; + + /* convert frame to nla-action time (if needed) */ + if (adt) { + val = BKE_nla_tweakedit_remap(adt, *(td->val), NLATIME_CONVERT_MAP); + } + else { + val = *(td->val); + } + + snapFrameTransform(t, autosnap, true, val, &val); + + /* convert frame out of nla-action time */ + if (adt) { + *(td->val) = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP); + } + else { + *(td->val) = val; + } + } + + /* If the handles are to be moved too + * (as side-effect of keyframes moving, to keep the general effect) + * offset them by the same amount so that the general angles are maintained + * (i.e. won't change while handles are free-to-roam and keyframes are snap-locked). + */ + if ((td->flag & TD_MOVEHANDLE1) && td2d->h1) { + td2d->h1[0] = td2d->ih1[0] + *td->val - td->ival; + } + if ((td->flag & TD_MOVEHANDLE2) && td2d->h2) { + td2d->h2[0] = td2d->ih2[0] + *td->val - td->ival; + } +} +/** \} */ -- cgit v1.2.3