Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormano-wii <germano.costa@ig.com.br>2020-02-14 14:42:17 +0300
committermano-wii <germano.costa@ig.com.br>2020-02-14 14:42:59 +0300
commite277e8d085037414f34b27f9c1a26cbbf2507c3e (patch)
treecb37b0a9332a23e09a6717d68894f481542f3d67 /source/blender/editors/transform/transform_mode.c
parent897f943ca0c93a46bdb767e4c47c4bddfa7149c2 (diff)
Cleanup: Split transform.c in multiple files
Differential Revision: https://developer.blender.org/D5819
Diffstat (limited to 'source/blender/editors/transform/transform_mode.c')
-rw-r--r--source/blender/editors/transform/transform_mode.c1100
1 files changed, 1100 insertions, 0 deletions
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 <stdlib.h>
+
+#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;
+ }
+}
+/** \} */