tornavis/source/blender/editors/transform/transform_mode_mirror.c

227 lines
5.6 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
/** \file
* \ingroup edtransform
*/
#include <stdlib.h>
#include "BLI_math.h"
#include "BLI_math_bits.h"
#include "BLI_string.h"
#include "BKE_armature.h"
#include "BKE_context.h"
#include "ED_screen.h"
#include "UI_interface.h"
#include "BLT_translation.h"
#include "transform.h"
#include "transform_convert.h"
#include "transform_mode.h"
/* -------------------------------------------------------------------- */
/** \name Transform (Mirror)
* \{ */
/**
* Mirrors an object by negating the scale of the object on the mirror axis, reflecting the
* location and adjusting the rotation.
*
* \param axis: Either the axis to mirror on (0 = x, 1 = y, 2 = z) in transform space or -1 for no
* axis mirror.
* \param flip: If true, a mirror on all axis will be performed additionally (point
* reflection).
*/
static void ElementMirror(TransInfo *t, TransDataContainer *tc, TransData *td, int axis, bool flip)
{
if ((t->flag & T_V3D_ALIGN) == 0 && td->ext) {
/* Size checked needed since the 3D cursor only uses rotation fields. */
if (td->ext->size) {
float fsize[] = {1.0, 1.0, 1.0};
if (axis >= 0) {
fsize[axis] = -fsize[axis];
}
if (flip) {
negate_v3(fsize);
}
protectedSizeBits(td->protectflag, fsize);
mul_v3_v3v3(td->ext->size, td->ext->isize, fsize);
constraintSizeLim(t, td);
}
float rmat[3][3];
if (axis >= 0) {
float imat[3][3];
mul_m3_m3m3(rmat, t->spacemtx_inv, td->axismtx);
rmat[axis][0] = -rmat[axis][0];
rmat[axis][1] = -rmat[axis][1];
rmat[axis][2] = -rmat[axis][2];
rmat[0][axis] = -rmat[0][axis];
rmat[1][axis] = -rmat[1][axis];
rmat[2][axis] = -rmat[2][axis];
invert_m3_m3(imat, td->axismtx);
mul_m3_m3m3(rmat, rmat, imat);
mul_m3_m3m3(rmat, t->spacemtx, rmat);
ElementRotation_ex(t, tc, td, rmat, td->center);
if (td->ext->rotAngle) {
*td->ext->rotAngle = -td->ext->irotAngle;
}
}
else {
unit_m3(rmat);
ElementRotation_ex(t, tc, td, rmat, td->center);
if (td->ext->rotAngle) {
*td->ext->rotAngle = td->ext->irotAngle;
}
}
}
if ((td->flag & TD_NO_LOC) == 0) {
float center[3], vec[3];
/* 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);
}
/* 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);
}
if (axis >= 0) {
/* Always do the mirror in global space. */
if (t->flag & T_EDIT) {
mul_m3_v3(td->mtx, vec);
}
reflect_v3_v3v3(vec, vec, t->spacemtx[axis]);
if (t->flag & T_EDIT) {
mul_m3_v3(td->smtx, vec);
}
}
if (flip) {
negate_v3(vec);
}
add_v3_v3(vec, center);
if (t->flag & T_POINTS) {
sub_v3_v3(vec, td->iloc);
}
else {
sub_v3_v3(vec, td->center);
}
if (t->options & (CTX_OBJECT | CTX_POSE_BONE)) {
mul_m3_v3(td->smtx, vec);
}
protectedTransBits(td->protectflag, vec);
if (td->loc) {
add_v3_v3v3(td->loc, td->iloc, vec);
}
constraintTransLim(t, td);
}
}
static void applyMirror(TransInfo *t, const int UNUSED(mval[2]))
{
int i;
char str[UI_MAX_DRAW_STR];
copy_v3_v3(t->values_final, t->values);
/* OPTIMIZATION:
* This still recalculates transformation on mouse move
* while it should only recalculate on constraint change. */
/* if an axis has been selected */
if (t->con.mode & CON_APPLY) {
/* #special_axis is either the constraint plane normal or the constraint axis.
* Assuming that CON_AXIS0 < CON_AXIS1 < CON_AXIS2 and CON_AXIS2 is CON_AXIS0 << 2 */
BLI_assert(CON_AXIS2 == CON_AXIS0 << 2);
int axis_bitmap = (t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2)) / CON_AXIS0;
int special_axis_bitmap = 0;
int special_axis = -1;
int bitmap_len = count_bits_i(axis_bitmap);
if (LIKELY(!ELEM(bitmap_len, 0, 3))) {
special_axis_bitmap = (bitmap_len == 2) ? ~axis_bitmap : axis_bitmap;
special_axis = bitscan_forward_i(special_axis_bitmap);
}
SNPRINTF(str, TIP_("Mirror%s"), t->con.text);
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
TransData *td = tc->data;
for (i = 0; i < tc->data_len; i++, td++) {
if (td->flag & TD_SKIP) {
continue;
}
ElementMirror(t, tc, td, special_axis, bitmap_len >= 2);
}
}
recalcData(t);
ED_area_status_text(t->area, str);
}
else {
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
TransData *td = tc->data;
for (i = 0; i < tc->data_len; i++, td++) {
if (td->flag & TD_SKIP) {
continue;
}
ElementMirror(t, tc, td, -1, false);
}
}
recalcData(t);
if (t->flag & T_2D_EDIT) {
ED_area_status_text(t->area, TIP_("Select a mirror axis (X, Y)"));
}
else {
ED_area_status_text(t->area, TIP_("Select a mirror axis (X, Y, Z)"));
}
}
}
void initMirror(TransInfo *t)
{
t->transform = applyMirror;
initMouseInputMode(t, &t->mouse, INPUT_NONE);
t->flag |= T_NULL_ONE;
}
/** \} */