9786 lines
		
	
	
		
			266 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			9786 lines
		
	
	
		
			266 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * 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 <stdio.h>
 | 
						|
#include <string.h>
 | 
						|
#include <math.h>
 | 
						|
#include <float.h>
 | 
						|
 | 
						|
#include "MEM_guardedalloc.h"
 | 
						|
 | 
						|
#include "DNA_anim_types.h"
 | 
						|
#include "DNA_armature_types.h"
 | 
						|
#include "DNA_constraint_types.h"
 | 
						|
#include "DNA_mask_types.h"
 | 
						|
#include "DNA_mesh_types.h"
 | 
						|
#include "DNA_movieclip_types.h"
 | 
						|
#include "DNA_scene_types.h" /* PET modes */
 | 
						|
#include "DNA_workspace_types.h"
 | 
						|
#include "DNA_gpencil_types.h"
 | 
						|
 | 
						|
#include "BLI_alloca.h"
 | 
						|
#include "BLI_utildefines.h"
 | 
						|
#include "BLI_math.h"
 | 
						|
#include "BLI_rect.h"
 | 
						|
#include "BLI_listbase.h"
 | 
						|
#include "BLI_string.h"
 | 
						|
#include "BLI_ghash.h"
 | 
						|
#include "BLI_utildefines_stack.h"
 | 
						|
#include "BLI_memarena.h"
 | 
						|
 | 
						|
#include "BKE_nla.h"
 | 
						|
#include "BKE_editmesh.h"
 | 
						|
#include "BKE_editmesh_bvh.h"
 | 
						|
#include "BKE_context.h"
 | 
						|
#include "BKE_constraint.h"
 | 
						|
#include "BKE_particle.h"
 | 
						|
#include "BKE_unit.h"
 | 
						|
#include "BKE_scene.h"
 | 
						|
#include "BKE_mask.h"
 | 
						|
#include "BKE_mesh.h"
 | 
						|
#include "BKE_report.h"
 | 
						|
#include "BKE_workspace.h"
 | 
						|
 | 
						|
#include "DEG_depsgraph.h"
 | 
						|
 | 
						|
#include "GPU_immediate.h"
 | 
						|
#include "GPU_immediate_util.h"
 | 
						|
#include "GPU_matrix.h"
 | 
						|
#include "GPU_state.h"
 | 
						|
 | 
						|
#include "ED_image.h"
 | 
						|
#include "ED_keyframing.h"
 | 
						|
#include "ED_screen.h"
 | 
						|
#include "ED_space_api.h"
 | 
						|
#include "ED_markers.h"
 | 
						|
#include "ED_view3d.h"
 | 
						|
#include "ED_mesh.h"
 | 
						|
#include "ED_clip.h"
 | 
						|
#include "ED_node.h"
 | 
						|
#include "ED_gpencil.h"
 | 
						|
 | 
						|
#include "WM_types.h"
 | 
						|
#include "WM_api.h"
 | 
						|
 | 
						|
#include "UI_view2d.h"
 | 
						|
#include "UI_interface.h"
 | 
						|
#include "UI_interface_icons.h"
 | 
						|
#include "UI_resources.h"
 | 
						|
 | 
						|
#include "RNA_access.h"
 | 
						|
#include "RNA_define.h"
 | 
						|
 | 
						|
#include "BLF_api.h"
 | 
						|
#include "BLT_translation.h"
 | 
						|
 | 
						|
#include "transform.h"
 | 
						|
 | 
						|
/* Disabling, since when you type you know what you are doing,
 | 
						|
 * and being able to set it to zero is handy. */
 | 
						|
// #define USE_NUM_NO_ZERO
 | 
						|
 | 
						|
static void drawTransformApply(const struct bContext *C, ARegion *ar, void *arg);
 | 
						|
static void doEdgeSlide(TransInfo *t, float perc);
 | 
						|
static void doVertSlide(TransInfo *t, float perc);
 | 
						|
 | 
						|
static void drawEdgeSlide(TransInfo *t);
 | 
						|
static void drawVertSlide(TransInfo *t);
 | 
						|
static void postInputRotation(TransInfo *t, float values[3]);
 | 
						|
 | 
						|
static void ElementRotation(
 | 
						|
    TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3], const short around);
 | 
						|
static void initSnapSpatial(TransInfo *t, float r_snap[3]);
 | 
						|
 | 
						|
static void storeCustomLNorValue(TransDataContainer *t, BMesh *bm);
 | 
						|
 | 
						|
/* Transform Callbacks */
 | 
						|
static void initBend(TransInfo *t);
 | 
						|
static eRedrawFlag handleEventBend(TransInfo *t, const struct wmEvent *event);
 | 
						|
static void Bend(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initShear(TransInfo *t);
 | 
						|
static eRedrawFlag handleEventShear(TransInfo *t, const struct wmEvent *event);
 | 
						|
static void applyShear(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initResize(TransInfo *t);
 | 
						|
static void applyResize(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initSkinResize(TransInfo *t);
 | 
						|
static void applySkinResize(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initTranslation(TransInfo *t);
 | 
						|
static void applyTranslation(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initToSphere(TransInfo *t);
 | 
						|
static void applyToSphere(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initRotation(TransInfo *t);
 | 
						|
static void applyRotation(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initNormalRotation(TransInfo *t);
 | 
						|
static void applyNormalRotation(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initShrinkFatten(TransInfo *t);
 | 
						|
static void applyShrinkFatten(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initTilt(TransInfo *t);
 | 
						|
static void applyTilt(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initCurveShrinkFatten(TransInfo *t);
 | 
						|
static void applyCurveShrinkFatten(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initMaskShrinkFatten(TransInfo *t);
 | 
						|
static void applyMaskShrinkFatten(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initGPShrinkFatten(TransInfo *t);
 | 
						|
static void applyGPShrinkFatten(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initTrackball(TransInfo *t);
 | 
						|
static void applyTrackball(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initPushPull(TransInfo *t);
 | 
						|
static void applyPushPull(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initBevelWeight(TransInfo *t);
 | 
						|
static void applyBevelWeight(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initCrease(TransInfo *t);
 | 
						|
static void applyCrease(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initBoneSize(TransInfo *t);
 | 
						|
static void applyBoneSize(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initBoneEnvelope(TransInfo *t);
 | 
						|
static void applyBoneEnvelope(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initBoneRoll(TransInfo *t);
 | 
						|
static void applyBoneRoll(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initEdgeSlide_ex(
 | 
						|
    TransInfo *t, bool use_double_side, bool use_even, bool flipped, bool use_clamp);
 | 
						|
static void initEdgeSlide(TransInfo *t);
 | 
						|
static eRedrawFlag handleEventEdgeSlide(TransInfo *t, const struct wmEvent *event);
 | 
						|
static void applyEdgeSlide(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initVertSlide_ex(TransInfo *t, bool use_even, bool flipped, bool use_clamp);
 | 
						|
static void initVertSlide(TransInfo *t);
 | 
						|
static eRedrawFlag handleEventVertSlide(TransInfo *t, const struct wmEvent *event);
 | 
						|
static void applyVertSlide(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initTimeTranslate(TransInfo *t);
 | 
						|
static void applyTimeTranslate(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initTimeSlide(TransInfo *t);
 | 
						|
static void applyTimeSlide(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initTimeScale(TransInfo *t);
 | 
						|
static void applyTimeScale(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initBakeTime(TransInfo *t);
 | 
						|
static void applyBakeTime(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initMirror(TransInfo *t);
 | 
						|
static void applyMirror(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initAlign(TransInfo *t);
 | 
						|
static void applyAlign(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initSeqSlide(TransInfo *t);
 | 
						|
static void applySeqSlide(TransInfo *t, const int mval[2]);
 | 
						|
 | 
						|
static void initGPOpacity(TransInfo *t);
 | 
						|
static void applyGPOpacity(TransInfo *t, const int mval[2]);
 | 
						|
/* end transform callbacks */
 | 
						|
 | 
						|
static 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))));
 | 
						|
}
 | 
						|
 | 
						|
bool transdata_check_local_islands(TransInfo *t, short around)
 | 
						|
{
 | 
						|
  return ((around == V3D_AROUND_LOCAL_ORIGINS) && ((ELEM(t->obedit_type, OB_MESH))));
 | 
						|
}
 | 
						|
 | 
						|
/* ************************** SPACE DEPENDENT CODE **************************** */
 | 
						|
 | 
						|
void setTransformViewMatrices(TransInfo *t)
 | 
						|
{
 | 
						|
  if (t->spacetype == SPACE_VIEW3D && t->ar && t->ar->regiontype == RGN_TYPE_WINDOW) {
 | 
						|
    RegionView3D *rv3d = t->ar->regiondata;
 | 
						|
 | 
						|
    copy_m4_m4(t->viewmat, rv3d->viewmat);
 | 
						|
    copy_m4_m4(t->viewinv, rv3d->viewinv);
 | 
						|
    copy_m4_m4(t->persmat, rv3d->persmat);
 | 
						|
    copy_m4_m4(t->persinv, rv3d->persinv);
 | 
						|
    t->persp = rv3d->persp;
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    unit_m4(t->viewmat);
 | 
						|
    unit_m4(t->viewinv);
 | 
						|
    unit_m4(t->persmat);
 | 
						|
    unit_m4(t->persinv);
 | 
						|
    t->persp = RV3D_ORTHO;
 | 
						|
  }
 | 
						|
 | 
						|
  calculateCenter2D(t);
 | 
						|
  calculateCenterLocal(t, t->center_global);
 | 
						|
}
 | 
						|
 | 
						|
void setTransformViewAspect(TransInfo *t, float r_aspect[3])
 | 
						|
{
 | 
						|
  copy_v3_fl(r_aspect, 1.0f);
 | 
						|
 | 
						|
  if (t->spacetype == SPACE_IMAGE) {
 | 
						|
    SpaceImage *sima = t->sa->spacedata.first;
 | 
						|
 | 
						|
    if (t->options & CTX_MASK) {
 | 
						|
      ED_space_image_get_aspect(sima, &r_aspect[0], &r_aspect[1]);
 | 
						|
    }
 | 
						|
    else if (t->options & CTX_PAINT_CURVE) {
 | 
						|
      /* pass */
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      ED_space_image_get_uv_aspect(sima, &r_aspect[0], &r_aspect[1]);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_CLIP) {
 | 
						|
    SpaceClip *sclip = t->sa->spacedata.first;
 | 
						|
 | 
						|
    if (t->options & CTX_MOVIECLIP) {
 | 
						|
      ED_space_clip_get_aspect_dimension_aware(sclip, &r_aspect[0], &r_aspect[1]);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      ED_space_clip_get_aspect(sclip, &r_aspect[0], &r_aspect[1]);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_GRAPH) {
 | 
						|
    /* depemds on context of usage */
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void convertViewVec2D(View2D *v2d, float r_vec[3], int dx, int dy)
 | 
						|
{
 | 
						|
  float divx = BLI_rcti_size_x(&v2d->mask);
 | 
						|
  float divy = BLI_rcti_size_y(&v2d->mask);
 | 
						|
 | 
						|
  r_vec[0] = BLI_rctf_size_x(&v2d->cur) * dx / divx;
 | 
						|
  r_vec[1] = BLI_rctf_size_y(&v2d->cur) * dy / divy;
 | 
						|
  r_vec[2] = 0.0f;
 | 
						|
}
 | 
						|
 | 
						|
static void convertViewVec2D_mask(View2D *v2d, float r_vec[3], int dx, int dy)
 | 
						|
{
 | 
						|
  float divx = BLI_rcti_size_x(&v2d->mask);
 | 
						|
  float divy = BLI_rcti_size_y(&v2d->mask);
 | 
						|
 | 
						|
  float mulx = BLI_rctf_size_x(&v2d->cur);
 | 
						|
  float muly = BLI_rctf_size_y(&v2d->cur);
 | 
						|
 | 
						|
  /* difference with convertViewVec2D */
 | 
						|
  /* clamp w/h, mask only */
 | 
						|
  if (mulx / divx < muly / divy) {
 | 
						|
    divy = divx;
 | 
						|
    muly = mulx;
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    divx = divy;
 | 
						|
    mulx = muly;
 | 
						|
  }
 | 
						|
  /* end difference */
 | 
						|
 | 
						|
  r_vec[0] = mulx * dx / divx;
 | 
						|
  r_vec[1] = muly * dy / divy;
 | 
						|
  r_vec[2] = 0.0f;
 | 
						|
}
 | 
						|
 | 
						|
void convertViewVec(TransInfo *t, float r_vec[3], double dx, double dy)
 | 
						|
{
 | 
						|
  if ((t->spacetype == SPACE_VIEW3D) && (t->ar->regiontype == RGN_TYPE_WINDOW)) {
 | 
						|
    if (t->options & CTX_PAINT_CURVE) {
 | 
						|
      r_vec[0] = dx;
 | 
						|
      r_vec[1] = dy;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      const float mval_f[2] = {(float)dx, (float)dy};
 | 
						|
      ED_view3d_win_to_delta(t->ar, mval_f, r_vec, t->zfac);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_IMAGE) {
 | 
						|
    if (t->options & CTX_MASK) {
 | 
						|
      convertViewVec2D_mask(t->view, r_vec, dx, dy);
 | 
						|
    }
 | 
						|
    else if (t->options & CTX_PAINT_CURVE) {
 | 
						|
      r_vec[0] = dx;
 | 
						|
      r_vec[1] = dy;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      convertViewVec2D(t->view, r_vec, dx, dy);
 | 
						|
    }
 | 
						|
 | 
						|
    r_vec[0] *= t->aspect[0];
 | 
						|
    r_vec[1] *= t->aspect[1];
 | 
						|
  }
 | 
						|
  else if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_NLA)) {
 | 
						|
    convertViewVec2D(t->view, r_vec, dx, dy);
 | 
						|
  }
 | 
						|
  else if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) {
 | 
						|
    convertViewVec2D(&t->ar->v2d, r_vec, dx, dy);
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_CLIP) {
 | 
						|
    if (t->options & CTX_MASK) {
 | 
						|
      convertViewVec2D_mask(t->view, r_vec, dx, dy);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      convertViewVec2D(t->view, r_vec, dx, dy);
 | 
						|
    }
 | 
						|
 | 
						|
    r_vec[0] *= t->aspect[0];
 | 
						|
    r_vec[1] *= t->aspect[1];
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    printf("%s: called in an invalid context\n", __func__);
 | 
						|
    zero_v3(r_vec);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DProjTest flag)
 | 
						|
{
 | 
						|
  if (t->spacetype == SPACE_VIEW3D) {
 | 
						|
    if (t->ar->regiontype == RGN_TYPE_WINDOW) {
 | 
						|
      if (ED_view3d_project_int_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) {
 | 
						|
        /* this is what was done in 2.64, perhaps we can be smarter? */
 | 
						|
        adr[0] = (int)2140000000.0f;
 | 
						|
        adr[1] = (int)2140000000.0f;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_IMAGE) {
 | 
						|
    SpaceImage *sima = t->sa->spacedata.first;
 | 
						|
 | 
						|
    if (t->options & CTX_MASK) {
 | 
						|
      float v[2];
 | 
						|
 | 
						|
      v[0] = vec[0] / t->aspect[0];
 | 
						|
      v[1] = vec[1] / t->aspect[1];
 | 
						|
 | 
						|
      BKE_mask_coord_to_image(sima->image, &sima->iuser, v, v);
 | 
						|
 | 
						|
      ED_image_point_pos__reverse(sima, t->ar, v, v);
 | 
						|
 | 
						|
      adr[0] = v[0];
 | 
						|
      adr[1] = v[1];
 | 
						|
    }
 | 
						|
    else if (t->options & CTX_PAINT_CURVE) {
 | 
						|
      adr[0] = vec[0];
 | 
						|
      adr[1] = vec[1];
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      float v[2];
 | 
						|
 | 
						|
      v[0] = vec[0] / t->aspect[0];
 | 
						|
      v[1] = vec[1] / t->aspect[1];
 | 
						|
 | 
						|
      UI_view2d_view_to_region(t->view, v[0], v[1], &adr[0], &adr[1]);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_ACTION) {
 | 
						|
    int out[2] = {0, 0};
 | 
						|
#if 0
 | 
						|
    SpaceAction *sact = t->sa->spacedata.first;
 | 
						|
 | 
						|
    if (sact->flag & SACTION_DRAWTIME) {
 | 
						|
      //vec[0] = vec[0]/((t->scene->r.frs_sec / t->scene->r.frs_sec_base));
 | 
						|
      /* same as below */
 | 
						|
      UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
 | 
						|
    }
 | 
						|
    else
 | 
						|
#endif
 | 
						|
    {
 | 
						|
      UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
 | 
						|
    }
 | 
						|
 | 
						|
    adr[0] = out[0];
 | 
						|
    adr[1] = out[1];
 | 
						|
  }
 | 
						|
  else if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_NLA)) {
 | 
						|
    int out[2] = {0, 0};
 | 
						|
 | 
						|
    UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
 | 
						|
    adr[0] = out[0];
 | 
						|
    adr[1] = out[1];
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_SEQ) { /* XXX not tested yet, but should work */
 | 
						|
    int out[2] = {0, 0};
 | 
						|
 | 
						|
    UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
 | 
						|
    adr[0] = out[0];
 | 
						|
    adr[1] = out[1];
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_CLIP) {
 | 
						|
    SpaceClip *sc = t->sa->spacedata.first;
 | 
						|
 | 
						|
    if (t->options & CTX_MASK) {
 | 
						|
      MovieClip *clip = ED_space_clip_get_clip(sc);
 | 
						|
 | 
						|
      if (clip) {
 | 
						|
        float v[2];
 | 
						|
 | 
						|
        v[0] = vec[0] / t->aspect[0];
 | 
						|
        v[1] = vec[1] / t->aspect[1];
 | 
						|
 | 
						|
        BKE_mask_coord_to_movieclip(sc->clip, &sc->user, v, v);
 | 
						|
 | 
						|
        ED_clip_point_stable_pos__reverse(sc, t->ar, v, v);
 | 
						|
 | 
						|
        adr[0] = v[0];
 | 
						|
        adr[1] = v[1];
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        adr[0] = 0;
 | 
						|
        adr[1] = 0;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else if (t->options & CTX_MOVIECLIP) {
 | 
						|
      float v[2];
 | 
						|
 | 
						|
      v[0] = vec[0] / t->aspect[0];
 | 
						|
      v[1] = vec[1] / t->aspect[1];
 | 
						|
 | 
						|
      UI_view2d_view_to_region(t->view, v[0], v[1], &adr[0], &adr[1]);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      BLI_assert(0);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_NODE) {
 | 
						|
    UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &adr[0], &adr[1]);
 | 
						|
  }
 | 
						|
}
 | 
						|
void projectIntView(TransInfo *t, const float vec[3], int adr[2])
 | 
						|
{
 | 
						|
  projectIntViewEx(t, vec, adr, V3D_PROJ_TEST_NOP);
 | 
						|
}
 | 
						|
 | 
						|
void projectFloatViewEx(TransInfo *t, const float vec[3], float adr[2], const eV3DProjTest flag)
 | 
						|
{
 | 
						|
  switch (t->spacetype) {
 | 
						|
    case SPACE_VIEW3D: {
 | 
						|
      if (t->options & CTX_PAINT_CURVE) {
 | 
						|
        adr[0] = vec[0];
 | 
						|
        adr[1] = vec[1];
 | 
						|
      }
 | 
						|
      else if (t->ar->regiontype == RGN_TYPE_WINDOW) {
 | 
						|
        /* allow points behind the view [#33643] */
 | 
						|
        if (ED_view3d_project_float_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) {
 | 
						|
          /* XXX, 2.64 and prior did this, weak! */
 | 
						|
          adr[0] = t->ar->winx / 2.0f;
 | 
						|
          adr[1] = t->ar->winy / 2.0f;
 | 
						|
        }
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    default: {
 | 
						|
      int a[2] = {0, 0};
 | 
						|
      projectIntView(t, vec, a);
 | 
						|
      adr[0] = a[0];
 | 
						|
      adr[1] = a[1];
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
void projectFloatView(TransInfo *t, const float vec[3], float adr[2])
 | 
						|
{
 | 
						|
  projectFloatViewEx(t, vec, adr, V3D_PROJ_TEST_NOP);
 | 
						|
}
 | 
						|
 | 
						|
void applyAspectRatio(TransInfo *t, float vec[2])
 | 
						|
{
 | 
						|
  if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION) &&
 | 
						|
      !(t->options & CTX_PAINT_CURVE)) {
 | 
						|
    SpaceImage *sima = t->sa->spacedata.first;
 | 
						|
 | 
						|
    if ((sima->flag & SI_COORDFLOATS) == 0) {
 | 
						|
      int width, height;
 | 
						|
      ED_space_image_get_size(sima, &width, &height);
 | 
						|
 | 
						|
      vec[0] *= width;
 | 
						|
      vec[1] *= height;
 | 
						|
    }
 | 
						|
 | 
						|
    vec[0] /= t->aspect[0];
 | 
						|
    vec[1] /= t->aspect[1];
 | 
						|
  }
 | 
						|
  else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) {
 | 
						|
    if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
 | 
						|
      vec[0] /= t->aspect[0];
 | 
						|
      vec[1] /= t->aspect[1];
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void removeAspectRatio(TransInfo *t, float vec[2])
 | 
						|
{
 | 
						|
  if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION)) {
 | 
						|
    SpaceImage *sima = t->sa->spacedata.first;
 | 
						|
 | 
						|
    if ((sima->flag & SI_COORDFLOATS) == 0) {
 | 
						|
      int width, height;
 | 
						|
      ED_space_image_get_size(sima, &width, &height);
 | 
						|
 | 
						|
      vec[0] /= width;
 | 
						|
      vec[1] /= height;
 | 
						|
    }
 | 
						|
 | 
						|
    vec[0] *= t->aspect[0];
 | 
						|
    vec[1] *= t->aspect[1];
 | 
						|
  }
 | 
						|
  else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) {
 | 
						|
    if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
 | 
						|
      vec[0] *= t->aspect[0];
 | 
						|
      vec[1] *= t->aspect[1];
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void viewRedrawForce(const bContext *C, TransInfo *t)
 | 
						|
{
 | 
						|
  if (t->options & CTX_GPENCIL_STROKES) {
 | 
						|
    bGPdata *gpd = ED_gpencil_data_get_active(C);
 | 
						|
    if (gpd) {
 | 
						|
      DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
 | 
						|
    }
 | 
						|
    WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_VIEW3D) {
 | 
						|
    if (t->options & CTX_PAINT_CURVE) {
 | 
						|
      wmWindow *window = CTX_wm_window(C);
 | 
						|
      WM_paint_cursor_tag_redraw(window, t->ar);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      /* Do we need more refined tags? */
 | 
						|
      if (t->flag & T_POSE) {
 | 
						|
        WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
 | 
						|
      }
 | 
						|
 | 
						|
      /* For real-time animation record - send notifiers recognized by animation editors */
 | 
						|
      // XXX: is this notifier a lame duck?
 | 
						|
      if ((t->animtimer) && IS_AUTOKEY_ON(t->scene)) {
 | 
						|
        WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_ACTION) {
 | 
						|
    // SpaceAction *saction = (SpaceAction *)t->sa->spacedata.first;
 | 
						|
    WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_GRAPH) {
 | 
						|
    // SpaceGraph *sipo = (SpaceGraph *)t->sa->spacedata.first;
 | 
						|
    WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_NLA) {
 | 
						|
    WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL);
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_NODE) {
 | 
						|
    // ED_area_tag_redraw(t->sa);
 | 
						|
    WM_event_add_notifier(C, NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_SEQ) {
 | 
						|
    WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, NULL);
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_IMAGE) {
 | 
						|
    if (t->options & CTX_MASK) {
 | 
						|
      Mask *mask = CTX_data_edit_mask(C);
 | 
						|
 | 
						|
      WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
 | 
						|
    }
 | 
						|
    else if (t->options & CTX_PAINT_CURVE) {
 | 
						|
      wmWindow *window = CTX_wm_window(C);
 | 
						|
      WM_paint_cursor_tag_redraw(window, t->ar);
 | 
						|
    }
 | 
						|
    else if (t->flag & T_CURSOR) {
 | 
						|
      ED_area_tag_redraw(t->sa);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      // XXX how to deal with lock?
 | 
						|
      SpaceImage *sima = (SpaceImage *)t->sa->spacedata.first;
 | 
						|
      if (sima->lock) {
 | 
						|
        WM_event_add_notifier(C, NC_GEOM | ND_DATA, OBEDIT_FROM_VIEW_LAYER(t->view_layer)->data);
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        ED_area_tag_redraw(t->sa);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_CLIP) {
 | 
						|
    SpaceClip *sc = (SpaceClip *)t->sa->spacedata.first;
 | 
						|
 | 
						|
    if (ED_space_clip_check_show_trackedit(sc)) {
 | 
						|
      MovieClip *clip = ED_space_clip_get_clip(sc);
 | 
						|
 | 
						|
      /* objects could be parented to tracking data, so send this for viewport refresh */
 | 
						|
      WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
 | 
						|
 | 
						|
      WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
 | 
						|
    }
 | 
						|
    else if (ED_space_clip_check_show_maskedit(sc)) {
 | 
						|
      Mask *mask = CTX_data_edit_mask(C);
 | 
						|
 | 
						|
      WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void viewRedrawPost(bContext *C, TransInfo *t)
 | 
						|
{
 | 
						|
  ED_area_status_text(t->sa, NULL);
 | 
						|
 | 
						|
  if (t->spacetype == SPACE_VIEW3D) {
 | 
						|
    /* if autokeying is enabled, send notifiers that keyframes were added */
 | 
						|
    if (IS_AUTOKEY_ON(t->scene)) {
 | 
						|
      WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
 | 
						|
    }
 | 
						|
 | 
						|
    /* redraw UV editor */
 | 
						|
    if (ELEM(t->mode, TFM_VERT_SLIDE, TFM_EDGE_SLIDE) &&
 | 
						|
        (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) {
 | 
						|
      WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
 | 
						|
    }
 | 
						|
 | 
						|
    /* XXX temp, first hack to get auto-render in compositor work (ton) */
 | 
						|
    WM_event_add_notifier(C, NC_SCENE | ND_TRANSFORM_DONE, CTX_data_scene(C));
 | 
						|
  }
 | 
						|
 | 
						|
#if 0  // TRANSFORM_FIX_ME
 | 
						|
  if (t->spacetype == SPACE_VIEW3D) {
 | 
						|
    allqueue(REDRAWBUTSOBJECT, 0);
 | 
						|
    allqueue(REDRAWVIEW3D, 0);
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_IMAGE) {
 | 
						|
    allqueue(REDRAWIMAGE, 0);
 | 
						|
    allqueue(REDRAWVIEW3D, 0);
 | 
						|
  }
 | 
						|
  else if (ELEM(t->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_GRAPH)) {
 | 
						|
    allqueue(REDRAWVIEW3D, 0);
 | 
						|
    allqueue(REDRAWACTION, 0);
 | 
						|
    allqueue(REDRAWNLA, 0);
 | 
						|
    allqueue(REDRAWIPO, 0);
 | 
						|
    allqueue(REDRAWTIME, 0);
 | 
						|
    allqueue(REDRAWBUTSOBJECT, 0);
 | 
						|
  }
 | 
						|
 | 
						|
  scrarea_queue_headredraw(curarea);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
/* ************************** TRANSFORMATIONS **************************** */
 | 
						|
 | 
						|
static void view_editmove(unsigned short UNUSED(event))
 | 
						|
{
 | 
						|
#if 0  // TRANSFORM_FIX_ME
 | 
						|
  int refresh = 0;
 | 
						|
  /* Regular:   Zoom in */
 | 
						|
  /* Shift:     Scroll up */
 | 
						|
  /* Ctrl:      Scroll right */
 | 
						|
  /* Alt-Shift: Rotate up */
 | 
						|
  /* Alt-Ctrl:  Rotate right */
 | 
						|
 | 
						|
  /* only work in 3D window for now
 | 
						|
   * In the end, will have to send to event to a 2D window handler instead
 | 
						|
   */
 | 
						|
  if (Trans.flag & T_2D_EDIT) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  switch (event) {
 | 
						|
    case WHEELUPMOUSE:
 | 
						|
      if (G.qual & LR_SHIFTKEY) {
 | 
						|
        if (G.qual & LR_ALTKEY) {
 | 
						|
          G.qual &= ~LR_SHIFTKEY;
 | 
						|
          persptoetsen(PAD2);
 | 
						|
          G.qual |= LR_SHIFTKEY;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          persptoetsen(PAD2);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else if (G.qual & LR_CTRLKEY) {
 | 
						|
        if (G.qual & LR_ALTKEY) {
 | 
						|
          G.qual &= ~LR_CTRLKEY;
 | 
						|
          persptoetsen(PAD4);
 | 
						|
          G.qual |= LR_CTRLKEY;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          persptoetsen(PAD4);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else if (U.uiflag & USER_WHEELZOOMDIR) {
 | 
						|
        persptoetsen(PADMINUS);
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        persptoetsen(PADPLUSKEY);
 | 
						|
      }
 | 
						|
 | 
						|
      refresh = 1;
 | 
						|
      break;
 | 
						|
    case WHEELDOWNMOUSE:
 | 
						|
      if (G.qual & LR_SHIFTKEY) {
 | 
						|
        if (G.qual & LR_ALTKEY) {
 | 
						|
          G.qual &= ~LR_SHIFTKEY;
 | 
						|
          persptoetsen(PAD8);
 | 
						|
          G.qual |= LR_SHIFTKEY;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          persptoetsen(PAD8);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else if (G.qual & LR_CTRLKEY) {
 | 
						|
        if (G.qual & LR_ALTKEY) {
 | 
						|
          G.qual &= ~LR_CTRLKEY;
 | 
						|
          persptoetsen(PAD6);
 | 
						|
          G.qual |= LR_CTRLKEY;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          persptoetsen(PAD6);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else if (U.uiflag & USER_WHEELZOOMDIR) {
 | 
						|
        persptoetsen(PADPLUSKEY);
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        persptoetsen(PADMINUS);
 | 
						|
      }
 | 
						|
 | 
						|
      refresh = 1;
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  if (refresh) {
 | 
						|
    setTransformViewMatrices(&Trans);
 | 
						|
  }
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
/* ************************************************* */
 | 
						|
 | 
						|
/* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
 | 
						|
enum {
 | 
						|
  TFM_MODAL_CANCEL = 1,
 | 
						|
  TFM_MODAL_CONFIRM = 2,
 | 
						|
  TFM_MODAL_TRANSLATE = 3,
 | 
						|
  TFM_MODAL_ROTATE = 4,
 | 
						|
  TFM_MODAL_RESIZE = 5,
 | 
						|
  TFM_MODAL_SNAP_INV_ON = 6,
 | 
						|
  TFM_MODAL_SNAP_INV_OFF = 7,
 | 
						|
  TFM_MODAL_SNAP_TOGGLE = 8,
 | 
						|
  TFM_MODAL_AXIS_X = 9,
 | 
						|
  TFM_MODAL_AXIS_Y = 10,
 | 
						|
  TFM_MODAL_AXIS_Z = 11,
 | 
						|
  TFM_MODAL_PLANE_X = 12,
 | 
						|
  TFM_MODAL_PLANE_Y = 13,
 | 
						|
  TFM_MODAL_PLANE_Z = 14,
 | 
						|
  TFM_MODAL_CONS_OFF = 15,
 | 
						|
  TFM_MODAL_ADD_SNAP = 16,
 | 
						|
  TFM_MODAL_REMOVE_SNAP = 17,
 | 
						|
 | 
						|
  /* 18 and 19 used by numinput, defined in transform.h */
 | 
						|
 | 
						|
  TFM_MODAL_PROPSIZE_UP = 20,
 | 
						|
  TFM_MODAL_PROPSIZE_DOWN = 21,
 | 
						|
  TFM_MODAL_AUTOIK_LEN_INC = 22,
 | 
						|
  TFM_MODAL_AUTOIK_LEN_DEC = 23,
 | 
						|
 | 
						|
  TFM_MODAL_EDGESLIDE_UP = 24,
 | 
						|
  TFM_MODAL_EDGESLIDE_DOWN = 25,
 | 
						|
 | 
						|
  /* for analog input, like trackpad */
 | 
						|
  TFM_MODAL_PROPSIZE = 26,
 | 
						|
  /* node editor insert offset (aka auto-offset) direction toggle */
 | 
						|
  TFM_MODAL_INSERTOFS_TOGGLE_DIR = 27,
 | 
						|
};
 | 
						|
 | 
						|
static bool transform_modal_item_poll(const wmOperator *op, int value)
 | 
						|
{
 | 
						|
  const TransInfo *t = op->customdata;
 | 
						|
  switch (value) {
 | 
						|
    case TFM_MODAL_CANCEL: {
 | 
						|
      if ((t->flag & T_RELEASE_CONFIRM) && ISMOUSE(t->launch_event)) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case TFM_MODAL_PROPSIZE:
 | 
						|
    case TFM_MODAL_PROPSIZE_UP:
 | 
						|
    case TFM_MODAL_PROPSIZE_DOWN: {
 | 
						|
      if ((t->flag & T_PROP_EDIT) == 0) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case TFM_MODAL_ADD_SNAP:
 | 
						|
    case TFM_MODAL_REMOVE_SNAP: {
 | 
						|
      if (t->spacetype != SPACE_VIEW3D) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      else if (t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      else if (!validSnap(t)) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case TFM_MODAL_AXIS_X:
 | 
						|
    case TFM_MODAL_AXIS_Y:
 | 
						|
    case TFM_MODAL_AXIS_Z:
 | 
						|
    case TFM_MODAL_PLANE_X:
 | 
						|
    case TFM_MODAL_PLANE_Y:
 | 
						|
    case TFM_MODAL_PLANE_Z: {
 | 
						|
      if (t->flag & T_NO_CONSTRAINT) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      if (!ELEM(value, TFM_MODAL_AXIS_X, TFM_MODAL_AXIS_Y)) {
 | 
						|
        if (t->flag & T_2D_EDIT) {
 | 
						|
          return false;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case TFM_MODAL_CONS_OFF: {
 | 
						|
      if ((t->con.mode & CON_APPLY) == 0) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case TFM_MODAL_EDGESLIDE_UP:
 | 
						|
    case TFM_MODAL_EDGESLIDE_DOWN: {
 | 
						|
      if (t->mode != TFM_EDGE_SLIDE) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case TFM_MODAL_INSERTOFS_TOGGLE_DIR: {
 | 
						|
      if (t->spacetype != SPACE_NODE) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case TFM_MODAL_AUTOIK_LEN_INC:
 | 
						|
    case TFM_MODAL_AUTOIK_LEN_DEC: {
 | 
						|
      if ((t->flag & T_AUTOIK) == 0) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
/* called in transform_ops.c, on each regeneration of keymaps */
 | 
						|
wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf)
 | 
						|
{
 | 
						|
  static const EnumPropertyItem modal_items[] = {
 | 
						|
      {TFM_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
 | 
						|
      {TFM_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
 | 
						|
      {TFM_MODAL_AXIS_X, "AXIS_X", 0, "X axis", ""},
 | 
						|
      {TFM_MODAL_AXIS_Y, "AXIS_Y", 0, "Y axis", ""},
 | 
						|
      {TFM_MODAL_AXIS_Z, "AXIS_Z", 0, "Z axis", ""},
 | 
						|
      {TFM_MODAL_PLANE_X, "PLANE_X", 0, "X plane", ""},
 | 
						|
      {TFM_MODAL_PLANE_Y, "PLANE_Y", 0, "Y plane", ""},
 | 
						|
      {TFM_MODAL_PLANE_Z, "PLANE_Z", 0, "Z plane", ""},
 | 
						|
      {TFM_MODAL_CONS_OFF, "CONS_OFF", 0, "Clear Constraints", ""},
 | 
						|
      {TFM_MODAL_SNAP_INV_ON, "SNAP_INV_ON", 0, "Snap Invert", ""},
 | 
						|
      {TFM_MODAL_SNAP_INV_OFF, "SNAP_INV_OFF", 0, "Snap Invert (Off)", ""},
 | 
						|
      {TFM_MODAL_SNAP_TOGGLE, "SNAP_TOGGLE", 0, "Snap Toggle", ""},
 | 
						|
      {TFM_MODAL_ADD_SNAP, "ADD_SNAP", 0, "Add Snap Point", ""},
 | 
						|
      {TFM_MODAL_REMOVE_SNAP, "REMOVE_SNAP", 0, "Remove Last Snap Point", ""},
 | 
						|
      {NUM_MODAL_INCREMENT_UP, "INCREMENT_UP", 0, "Numinput Increment Up", ""},
 | 
						|
      {NUM_MODAL_INCREMENT_DOWN, "INCREMENT_DOWN", 0, "Numinput Increment Down", ""},
 | 
						|
      {TFM_MODAL_PROPSIZE_UP, "PROPORTIONAL_SIZE_UP", 0, "Increase Proportional Influence", ""},
 | 
						|
      {TFM_MODAL_PROPSIZE_DOWN,
 | 
						|
       "PROPORTIONAL_SIZE_DOWN",
 | 
						|
       0,
 | 
						|
       "Decrease Proportional Influence",
 | 
						|
       ""},
 | 
						|
      {TFM_MODAL_AUTOIK_LEN_INC, "AUTOIK_CHAIN_LEN_UP", 0, "Increase Max AutoIK Chain Length", ""},
 | 
						|
      {TFM_MODAL_AUTOIK_LEN_DEC,
 | 
						|
       "AUTOIK_CHAIN_LEN_DOWN",
 | 
						|
       0,
 | 
						|
       "Decrease Max AutoIK Chain Length",
 | 
						|
       ""},
 | 
						|
      {TFM_MODAL_EDGESLIDE_UP, "EDGESLIDE_EDGE_NEXT", 0, "Select next Edge Slide Edge", ""},
 | 
						|
      {TFM_MODAL_EDGESLIDE_DOWN, "EDGESLIDE_PREV_NEXT", 0, "Select previous Edge Slide Edge", ""},
 | 
						|
      {TFM_MODAL_PROPSIZE, "PROPORTIONAL_SIZE", 0, "Adjust Proportional Influence", ""},
 | 
						|
      {TFM_MODAL_INSERTOFS_TOGGLE_DIR,
 | 
						|
       "INSERTOFS_TOGGLE_DIR",
 | 
						|
       0,
 | 
						|
       "Toggle Direction for Node Auto-offset",
 | 
						|
       ""},
 | 
						|
      {TFM_MODAL_TRANSLATE, "TRANSLATE", 0, "Move", ""},
 | 
						|
      {TFM_MODAL_ROTATE, "ROTATE", 0, "Rotate", ""},
 | 
						|
      {TFM_MODAL_RESIZE, "RESIZE", 0, "Resize", ""},
 | 
						|
      {0, NULL, 0, NULL, NULL},
 | 
						|
  };
 | 
						|
 | 
						|
  wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "Transform Modal Map");
 | 
						|
 | 
						|
  keymap = WM_modalkeymap_add(keyconf, "Transform Modal Map", modal_items);
 | 
						|
  keymap->poll_modal_item = transform_modal_item_poll;
 | 
						|
 | 
						|
  return keymap;
 | 
						|
}
 | 
						|
 | 
						|
static void transform_event_xyz_constraint(TransInfo *t, short key_type, char cmode, bool is_plane)
 | 
						|
{
 | 
						|
  if (!(t->flag & T_NO_CONSTRAINT)) {
 | 
						|
    int constraint_axis, constraint_plane;
 | 
						|
    const bool edit_2d = (t->flag & T_2D_EDIT) != 0;
 | 
						|
    const char *msg1 = "", *msg2 = "", *msg3 = "";
 | 
						|
    char axis;
 | 
						|
 | 
						|
    /* Initialize */
 | 
						|
    switch (key_type) {
 | 
						|
      case XKEY:
 | 
						|
        msg1 = IFACE_("along X");
 | 
						|
        msg2 = IFACE_("along %s X");
 | 
						|
        msg3 = IFACE_("locking %s X");
 | 
						|
        axis = 'X';
 | 
						|
        constraint_axis = CON_AXIS0;
 | 
						|
        break;
 | 
						|
      case YKEY:
 | 
						|
        msg1 = IFACE_("along Y");
 | 
						|
        msg2 = IFACE_("along %s Y");
 | 
						|
        msg3 = IFACE_("locking %s Y");
 | 
						|
        axis = 'Y';
 | 
						|
        constraint_axis = CON_AXIS1;
 | 
						|
        break;
 | 
						|
      case ZKEY:
 | 
						|
        msg1 = IFACE_("along Z");
 | 
						|
        msg2 = IFACE_("along %s Z");
 | 
						|
        msg3 = IFACE_("locking %s Z");
 | 
						|
        axis = 'Z';
 | 
						|
        constraint_axis = CON_AXIS2;
 | 
						|
        break;
 | 
						|
      default:
 | 
						|
        /* Invalid key */
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    constraint_plane = ((CON_AXIS0 | CON_AXIS1 | CON_AXIS2) & (~constraint_axis));
 | 
						|
 | 
						|
    if (edit_2d && (key_type != ZKEY)) {
 | 
						|
      if (cmode == axis) {
 | 
						|
        stopConstraint(t);
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        setUserConstraint(t, V3D_ORIENT_GLOBAL, constraint_axis, msg1);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else if (!edit_2d) {
 | 
						|
      if (cmode != axis) {
 | 
						|
        /* First press, constraint to an axis. */
 | 
						|
        t->orientation.index = 0;
 | 
						|
        const short *orientation_ptr = t->orientation.types[t->orientation.index];
 | 
						|
        const short orientation = orientation_ptr ? *orientation_ptr : V3D_ORIENT_GLOBAL;
 | 
						|
        if (is_plane == false) {
 | 
						|
          setUserConstraint(t, orientation, constraint_axis, msg2);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          setUserConstraint(t, orientation, constraint_plane, msg3);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        /* Successive presses on existing axis, cycle orientation modes. */
 | 
						|
        t->orientation.index = (t->orientation.index + 1) % ARRAY_SIZE(t->orientation.types);
 | 
						|
 | 
						|
        if (t->orientation.index == 0) {
 | 
						|
          stopConstraint(t);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          const short *orientation_ptr = t->orientation.types[t->orientation.index];
 | 
						|
          const short orientation = orientation_ptr ? *orientation_ptr : V3D_ORIENT_GLOBAL;
 | 
						|
          if (is_plane == false) {
 | 
						|
            setUserConstraint(t, orientation, constraint_axis, msg2);
 | 
						|
          }
 | 
						|
          else {
 | 
						|
            setUserConstraint(t, orientation, constraint_plane, msg3);
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
    t->redraw |= TREDRAW_HARD;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
int transformEvent(TransInfo *t, const wmEvent *event)
 | 
						|
{
 | 
						|
  char cmode = constraintModeToChar(t);
 | 
						|
  bool handled = false;
 | 
						|
  const int modifiers_prev = t->modifiers;
 | 
						|
 | 
						|
  t->redraw |= handleMouseInput(t, &t->mouse, event);
 | 
						|
 | 
						|
  /* Handle modal numinput events first, if already activated. */
 | 
						|
  if (((event->val == KM_PRESS) || (event->type == EVT_MODAL_MAP)) && hasNumInput(&t->num) &&
 | 
						|
      handleNumInput(t->context, &(t->num), event)) {
 | 
						|
    t->redraw |= TREDRAW_HARD;
 | 
						|
    handled = true;
 | 
						|
  }
 | 
						|
  else if (event->type == MOUSEMOVE) {
 | 
						|
    if (t->modifiers & MOD_CONSTRAINT_SELECT) {
 | 
						|
      t->con.mode |= CON_SELECT;
 | 
						|
    }
 | 
						|
 | 
						|
    copy_v2_v2_int(t->mval, event->mval);
 | 
						|
 | 
						|
    /* Use this for soft redraw. Might cause flicker in object mode */
 | 
						|
    // t->redraw |= TREDRAW_SOFT;
 | 
						|
    t->redraw |= TREDRAW_HARD;
 | 
						|
 | 
						|
    if (t->state == TRANS_STARTING) {
 | 
						|
      t->state = TRANS_RUNNING;
 | 
						|
    }
 | 
						|
 | 
						|
    applyMouseInput(t, &t->mouse, t->mval, t->values);
 | 
						|
 | 
						|
    // Snapping mouse move events
 | 
						|
    t->redraw |= handleSnapping(t, event);
 | 
						|
    handled = true;
 | 
						|
  }
 | 
						|
  /* handle modal keymap first */
 | 
						|
  else if (event->type == EVT_MODAL_MAP) {
 | 
						|
    switch (event->val) {
 | 
						|
      case TFM_MODAL_CANCEL:
 | 
						|
        t->state = TRANS_CANCEL;
 | 
						|
        handled = true;
 | 
						|
        break;
 | 
						|
      case TFM_MODAL_CONFIRM:
 | 
						|
        t->state = TRANS_CONFIRM;
 | 
						|
        handled = true;
 | 
						|
        break;
 | 
						|
      case TFM_MODAL_TRANSLATE:
 | 
						|
        /* only switch when... */
 | 
						|
        if (ELEM(t->mode,
 | 
						|
                 TFM_ROTATION,
 | 
						|
                 TFM_RESIZE,
 | 
						|
                 TFM_TRACKBALL,
 | 
						|
                 TFM_EDGE_SLIDE,
 | 
						|
                 TFM_VERT_SLIDE)) {
 | 
						|
          restoreTransObjects(t);
 | 
						|
          resetTransModal(t);
 | 
						|
          resetTransRestrictions(t);
 | 
						|
          initTranslation(t);
 | 
						|
          initSnapping(t, NULL);  // need to reinit after mode change
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          WM_event_add_mousemove(t->context);
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        else if (t->mode == TFM_SEQ_SLIDE) {
 | 
						|
          t->flag ^= T_ALT_TRANSFORM;
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          if (t->obedit_type == OB_MESH) {
 | 
						|
            if ((t->mode == TFM_TRANSLATION) && (t->spacetype == SPACE_VIEW3D)) {
 | 
						|
              restoreTransObjects(t);
 | 
						|
              resetTransModal(t);
 | 
						|
              resetTransRestrictions(t);
 | 
						|
 | 
						|
              /* first try edge slide */
 | 
						|
              initEdgeSlide(t);
 | 
						|
              /* if that fails, do vertex slide */
 | 
						|
              if (t->state == TRANS_CANCEL) {
 | 
						|
                resetTransModal(t);
 | 
						|
                t->state = TRANS_STARTING;
 | 
						|
                initVertSlide(t);
 | 
						|
              }
 | 
						|
              /* vert slide can fail on unconnected vertices (rare but possible) */
 | 
						|
              if (t->state == TRANS_CANCEL) {
 | 
						|
                resetTransModal(t);
 | 
						|
                t->mode = TFM_TRANSLATION;
 | 
						|
                t->state = TRANS_STARTING;
 | 
						|
                restoreTransObjects(t);
 | 
						|
                resetTransRestrictions(t);
 | 
						|
                initTranslation(t);
 | 
						|
              }
 | 
						|
              initSnapping(t, NULL);  // need to reinit after mode change
 | 
						|
              t->redraw |= TREDRAW_HARD;
 | 
						|
              handled = true;
 | 
						|
              WM_event_add_mousemove(t->context);
 | 
						|
            }
 | 
						|
          }
 | 
						|
          else if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
 | 
						|
            if (t->mode == TFM_TRANSLATION) {
 | 
						|
              restoreTransObjects(t);
 | 
						|
 | 
						|
              t->flag ^= T_ALT_TRANSFORM;
 | 
						|
              t->redraw |= TREDRAW_HARD;
 | 
						|
              handled = true;
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case TFM_MODAL_ROTATE:
 | 
						|
        /* only switch when... */
 | 
						|
        if (!(t->options & CTX_TEXTURE) && !(t->options & (CTX_MOVIECLIP | CTX_MASK))) {
 | 
						|
          if (ELEM(t->mode,
 | 
						|
                   TFM_ROTATION,
 | 
						|
                   TFM_RESIZE,
 | 
						|
                   TFM_TRACKBALL,
 | 
						|
                   TFM_TRANSLATION,
 | 
						|
                   TFM_EDGE_SLIDE,
 | 
						|
                   TFM_VERT_SLIDE)) {
 | 
						|
            restoreTransObjects(t);
 | 
						|
            resetTransModal(t);
 | 
						|
            resetTransRestrictions(t);
 | 
						|
 | 
						|
            if (t->mode == TFM_ROTATION) {
 | 
						|
              initTrackball(t);
 | 
						|
            }
 | 
						|
            else {
 | 
						|
              initRotation(t);
 | 
						|
            }
 | 
						|
            initSnapping(t, NULL);  // need to reinit after mode change
 | 
						|
            t->redraw |= TREDRAW_HARD;
 | 
						|
            handled = true;
 | 
						|
          }
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case TFM_MODAL_RESIZE:
 | 
						|
        /* only switch when... */
 | 
						|
        if (ELEM(t->mode,
 | 
						|
                 TFM_ROTATION,
 | 
						|
                 TFM_TRANSLATION,
 | 
						|
                 TFM_TRACKBALL,
 | 
						|
                 TFM_EDGE_SLIDE,
 | 
						|
                 TFM_VERT_SLIDE)) {
 | 
						|
 | 
						|
          /* Scale isn't normally very useful after extrude along normals, see T39756 */
 | 
						|
          if ((t->con.mode & CON_APPLY) && (t->con.orientation == V3D_ORIENT_NORMAL)) {
 | 
						|
            stopConstraint(t);
 | 
						|
          }
 | 
						|
 | 
						|
          restoreTransObjects(t);
 | 
						|
          resetTransModal(t);
 | 
						|
          resetTransRestrictions(t);
 | 
						|
          initResize(t);
 | 
						|
          initSnapping(t, NULL);  // need to reinit after mode change
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        else if (t->mode == TFM_SHRINKFATTEN) {
 | 
						|
          t->flag ^= T_ALT_TRANSFORM;
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        else if (t->mode == TFM_RESIZE) {
 | 
						|
          if (t->options & CTX_MOVIECLIP) {
 | 
						|
            restoreTransObjects(t);
 | 
						|
 | 
						|
            t->flag ^= T_ALT_TRANSFORM;
 | 
						|
            t->redraw |= TREDRAW_HARD;
 | 
						|
            handled = true;
 | 
						|
          }
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
      case TFM_MODAL_SNAP_INV_ON:
 | 
						|
        t->modifiers |= MOD_SNAP_INVERT;
 | 
						|
        t->redraw |= TREDRAW_HARD;
 | 
						|
        handled = true;
 | 
						|
        break;
 | 
						|
      case TFM_MODAL_SNAP_INV_OFF:
 | 
						|
        t->modifiers &= ~MOD_SNAP_INVERT;
 | 
						|
        t->redraw |= TREDRAW_HARD;
 | 
						|
        handled = true;
 | 
						|
        break;
 | 
						|
      case TFM_MODAL_SNAP_TOGGLE:
 | 
						|
        t->modifiers ^= MOD_SNAP;
 | 
						|
        t->redraw |= TREDRAW_HARD;
 | 
						|
        handled = true;
 | 
						|
        break;
 | 
						|
      case TFM_MODAL_AXIS_X:
 | 
						|
        if (!(t->flag & T_NO_CONSTRAINT)) {
 | 
						|
          transform_event_xyz_constraint(t, XKEY, cmode, false);
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case TFM_MODAL_AXIS_Y:
 | 
						|
        if ((t->flag & T_NO_CONSTRAINT) == 0) {
 | 
						|
          transform_event_xyz_constraint(t, YKEY, cmode, false);
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case TFM_MODAL_AXIS_Z:
 | 
						|
        if ((t->flag & (T_NO_CONSTRAINT)) == 0) {
 | 
						|
          transform_event_xyz_constraint(t, ZKEY, cmode, false);
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case TFM_MODAL_PLANE_X:
 | 
						|
        if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
 | 
						|
          transform_event_xyz_constraint(t, XKEY, cmode, true);
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case TFM_MODAL_PLANE_Y:
 | 
						|
        if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
 | 
						|
          transform_event_xyz_constraint(t, YKEY, cmode, true);
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case TFM_MODAL_PLANE_Z:
 | 
						|
        if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
 | 
						|
          transform_event_xyz_constraint(t, ZKEY, cmode, true);
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case TFM_MODAL_CONS_OFF:
 | 
						|
        if ((t->flag & T_NO_CONSTRAINT) == 0) {
 | 
						|
          stopConstraint(t);
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case TFM_MODAL_ADD_SNAP:
 | 
						|
        addSnapPoint(t);
 | 
						|
        t->redraw |= TREDRAW_HARD;
 | 
						|
        handled = true;
 | 
						|
        break;
 | 
						|
      case TFM_MODAL_REMOVE_SNAP:
 | 
						|
        removeSnapPoint(t);
 | 
						|
        t->redraw |= TREDRAW_HARD;
 | 
						|
        handled = true;
 | 
						|
        break;
 | 
						|
      case TFM_MODAL_PROPSIZE:
 | 
						|
        /* MOUSEPAN usage... */
 | 
						|
        if (t->flag & T_PROP_EDIT) {
 | 
						|
          float fac = 1.0f + 0.005f * (event->y - event->prevy);
 | 
						|
          t->prop_size *= fac;
 | 
						|
          if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) {
 | 
						|
            t->prop_size = max_ff(min_ff(t->prop_size, ((View3D *)t->view)->clip_end),
 | 
						|
                                  T_PROP_SIZE_MIN);
 | 
						|
          }
 | 
						|
          else {
 | 
						|
            t->prop_size = max_ff(min_ff(t->prop_size, T_PROP_SIZE_MAX), T_PROP_SIZE_MIN);
 | 
						|
          }
 | 
						|
          calculatePropRatio(t);
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case TFM_MODAL_PROPSIZE_UP:
 | 
						|
        if (t->flag & T_PROP_EDIT) {
 | 
						|
          t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
 | 
						|
          if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) {
 | 
						|
            t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->clip_end);
 | 
						|
          }
 | 
						|
          else {
 | 
						|
            t->prop_size = min_ff(t->prop_size, T_PROP_SIZE_MAX);
 | 
						|
          }
 | 
						|
          calculatePropRatio(t);
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case TFM_MODAL_PROPSIZE_DOWN:
 | 
						|
        if (t->flag & T_PROP_EDIT) {
 | 
						|
          t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
 | 
						|
          t->prop_size = max_ff(t->prop_size, T_PROP_SIZE_MIN);
 | 
						|
          calculatePropRatio(t);
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case TFM_MODAL_AUTOIK_LEN_INC:
 | 
						|
        if (t->flag & T_AUTOIK) {
 | 
						|
          transform_autoik_update(t, 1);
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case TFM_MODAL_AUTOIK_LEN_DEC:
 | 
						|
        if (t->flag & T_AUTOIK) {
 | 
						|
          transform_autoik_update(t, -1);
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case TFM_MODAL_INSERTOFS_TOGGLE_DIR:
 | 
						|
        if (t->spacetype == SPACE_NODE) {
 | 
						|
          SpaceNode *snode = (SpaceNode *)t->sa->spacedata.first;
 | 
						|
 | 
						|
          BLI_assert(t->sa->spacetype == t->spacetype);
 | 
						|
 | 
						|
          if (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_RIGHT) {
 | 
						|
            snode->insert_ofs_dir = SNODE_INSERTOFS_DIR_LEFT;
 | 
						|
          }
 | 
						|
          else if (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_LEFT) {
 | 
						|
            snode->insert_ofs_dir = SNODE_INSERTOFS_DIR_RIGHT;
 | 
						|
          }
 | 
						|
          else {
 | 
						|
            BLI_assert(0);
 | 
						|
          }
 | 
						|
 | 
						|
          t->redraw |= TREDRAW_SOFT;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      /* Those two are only handled in transform's own handler, see T44634! */
 | 
						|
      case TFM_MODAL_EDGESLIDE_UP:
 | 
						|
      case TFM_MODAL_EDGESLIDE_DOWN:
 | 
						|
      default:
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  /* else do non-mapped events */
 | 
						|
  else if (event->val == KM_PRESS) {
 | 
						|
    switch (event->type) {
 | 
						|
      case RIGHTMOUSE:
 | 
						|
        t->state = TRANS_CANCEL;
 | 
						|
        handled = true;
 | 
						|
        break;
 | 
						|
      /* enforce redraw of transform when modifiers are used */
 | 
						|
      case LEFTSHIFTKEY:
 | 
						|
      case RIGHTSHIFTKEY:
 | 
						|
        t->modifiers |= MOD_CONSTRAINT_PLANE;
 | 
						|
        t->redraw |= TREDRAW_HARD;
 | 
						|
        handled = true;
 | 
						|
        break;
 | 
						|
 | 
						|
      case SPACEKEY:
 | 
						|
        t->state = TRANS_CONFIRM;
 | 
						|
        handled = true;
 | 
						|
        break;
 | 
						|
 | 
						|
      case MIDDLEMOUSE:
 | 
						|
        if ((t->flag & T_NO_CONSTRAINT) == 0) {
 | 
						|
          /* exception for switching to dolly, or trackball, in camera view */
 | 
						|
          if (t->flag & T_CAMERA) {
 | 
						|
            if (t->mode == TFM_TRANSLATION) {
 | 
						|
              setLocalConstraint(t, (CON_AXIS2), IFACE_("along local Z"));
 | 
						|
            }
 | 
						|
            else if (t->mode == TFM_ROTATION) {
 | 
						|
              restoreTransObjects(t);
 | 
						|
              initTrackball(t);
 | 
						|
            }
 | 
						|
          }
 | 
						|
          else {
 | 
						|
            t->modifiers |= MOD_CONSTRAINT_SELECT;
 | 
						|
            if (t->con.mode & CON_APPLY) {
 | 
						|
              stopConstraint(t);
 | 
						|
            }
 | 
						|
            else {
 | 
						|
              if (event->shift) {
 | 
						|
                /* bit hackish... but it prevents mmb select to print the
 | 
						|
                 * orientation from menu */
 | 
						|
                float mati[3][3];
 | 
						|
                strcpy(t->spacename, "global");
 | 
						|
                unit_m3(mati);
 | 
						|
                initSelectConstraint(t, mati);
 | 
						|
              }
 | 
						|
              else {
 | 
						|
                initSelectConstraint(t, t->spacemtx);
 | 
						|
              }
 | 
						|
              postSelectConstraint(t);
 | 
						|
            }
 | 
						|
          }
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case ESCKEY:
 | 
						|
        t->state = TRANS_CANCEL;
 | 
						|
        handled = true;
 | 
						|
        break;
 | 
						|
      case PADENTER:
 | 
						|
      case RETKEY:
 | 
						|
        t->state = TRANS_CONFIRM;
 | 
						|
        handled = true;
 | 
						|
        break;
 | 
						|
      case GKEY:
 | 
						|
        /* only switch when... */
 | 
						|
        if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL)) {
 | 
						|
          restoreTransObjects(t);
 | 
						|
          resetTransModal(t);
 | 
						|
          resetTransRestrictions(t);
 | 
						|
          initTranslation(t);
 | 
						|
          initSnapping(t, NULL);  // need to reinit after mode change
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case SKEY:
 | 
						|
        /* only switch when... */
 | 
						|
        if (ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL)) {
 | 
						|
          restoreTransObjects(t);
 | 
						|
          resetTransModal(t);
 | 
						|
          resetTransRestrictions(t);
 | 
						|
          initResize(t);
 | 
						|
          initSnapping(t, NULL);  // need to reinit after mode change
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case RKEY:
 | 
						|
        /* only switch when... */
 | 
						|
        if (!(t->options & CTX_TEXTURE)) {
 | 
						|
          if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION)) {
 | 
						|
            restoreTransObjects(t);
 | 
						|
            resetTransModal(t);
 | 
						|
            resetTransRestrictions(t);
 | 
						|
 | 
						|
            if (t->mode == TFM_ROTATION) {
 | 
						|
              initTrackball(t);
 | 
						|
            }
 | 
						|
            else {
 | 
						|
              initRotation(t);
 | 
						|
            }
 | 
						|
            initSnapping(t, NULL);  // need to reinit after mode change
 | 
						|
            t->redraw |= TREDRAW_HARD;
 | 
						|
            handled = true;
 | 
						|
          }
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case CKEY:
 | 
						|
        if (event->alt) {
 | 
						|
          if (!(t->options & CTX_NO_PET)) {
 | 
						|
            t->flag ^= T_PROP_CONNECTED;
 | 
						|
            sort_trans_data_dist(t);
 | 
						|
            calculatePropRatio(t);
 | 
						|
            t->redraw = TREDRAW_HARD;
 | 
						|
            handled = true;
 | 
						|
          }
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case OKEY:
 | 
						|
        if (t->flag & T_PROP_EDIT && event->shift) {
 | 
						|
          t->prop_mode = (t->prop_mode + 1) % PROP_MODE_MAX;
 | 
						|
          calculatePropRatio(t);
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case PADPLUSKEY:
 | 
						|
        if (event->alt && t->flag & T_PROP_EDIT) {
 | 
						|
          t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
 | 
						|
          if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) {
 | 
						|
            t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->clip_end);
 | 
						|
          }
 | 
						|
          calculatePropRatio(t);
 | 
						|
          t->redraw = TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case PAGEUPKEY:
 | 
						|
      case WHEELDOWNMOUSE:
 | 
						|
        if (t->flag & T_AUTOIK) {
 | 
						|
          transform_autoik_update(t, 1);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          view_editmove(event->type);
 | 
						|
        }
 | 
						|
        t->redraw = TREDRAW_HARD;
 | 
						|
        handled = true;
 | 
						|
        break;
 | 
						|
      case PADMINUS:
 | 
						|
        if (event->alt && t->flag & T_PROP_EDIT) {
 | 
						|
          t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
 | 
						|
          calculatePropRatio(t);
 | 
						|
          t->redraw = TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case PAGEDOWNKEY:
 | 
						|
      case WHEELUPMOUSE:
 | 
						|
        if (t->flag & T_AUTOIK) {
 | 
						|
          transform_autoik_update(t, -1);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          view_editmove(event->type);
 | 
						|
        }
 | 
						|
        t->redraw = TREDRAW_HARD;
 | 
						|
        handled = true;
 | 
						|
        break;
 | 
						|
      case LEFTALTKEY:
 | 
						|
      case RIGHTALTKEY:
 | 
						|
        if (ELEM(t->spacetype, SPACE_SEQ, SPACE_VIEW3D)) {
 | 
						|
          t->flag |= T_ALT_TRANSFORM;
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case NKEY:
 | 
						|
        if (ELEM(t->mode, TFM_ROTATION)) {
 | 
						|
          if ((t->flag & T_EDIT) && t->obedit_type == OB_MESH) {
 | 
						|
            restoreTransObjects(t);
 | 
						|
            resetTransModal(t);
 | 
						|
            resetTransRestrictions(t);
 | 
						|
            initNormalRotation(t);
 | 
						|
            t->redraw = TREDRAW_HARD;
 | 
						|
            handled = true;
 | 
						|
          }
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      default:
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Snapping key events */
 | 
						|
    t->redraw |= handleSnapping(t, event);
 | 
						|
  }
 | 
						|
  else if (event->val == KM_RELEASE) {
 | 
						|
    switch (event->type) {
 | 
						|
      case LEFTSHIFTKEY:
 | 
						|
      case RIGHTSHIFTKEY:
 | 
						|
        t->modifiers &= ~MOD_CONSTRAINT_PLANE;
 | 
						|
        t->redraw |= TREDRAW_HARD;
 | 
						|
        handled = true;
 | 
						|
        break;
 | 
						|
 | 
						|
      case MIDDLEMOUSE:
 | 
						|
        if ((t->flag & T_NO_CONSTRAINT) == 0) {
 | 
						|
          t->modifiers &= ~MOD_CONSTRAINT_SELECT;
 | 
						|
          postSelectConstraint(t);
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      case LEFTALTKEY:
 | 
						|
      case RIGHTALTKEY:
 | 
						|
        if (ELEM(t->spacetype, SPACE_SEQ, SPACE_VIEW3D)) {
 | 
						|
          t->flag &= ~T_ALT_TRANSFORM;
 | 
						|
          t->redraw |= TREDRAW_HARD;
 | 
						|
          handled = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      default:
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    /* confirm transform if launch key is released after mouse move */
 | 
						|
    if (t->flag & T_RELEASE_CONFIRM) {
 | 
						|
      /* XXX Keyrepeat bug in Xorg messes this up, will test when fixed */
 | 
						|
      if ((event->type == t->launch_event) && ISMOUSE(t->launch_event)) {
 | 
						|
        t->state = TRANS_CONFIRM;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* if we change snap options, get the unsnapped values back */
 | 
						|
  if ((t->modifiers & (MOD_SNAP | MOD_SNAP_INVERT)) !=
 | 
						|
      (modifiers_prev & (MOD_SNAP | MOD_SNAP_INVERT))) {
 | 
						|
    applyMouseInput(t, &t->mouse, t->mval, t->values);
 | 
						|
  }
 | 
						|
 | 
						|
  /* Per transform event, if present */
 | 
						|
  if (t->handleEvent && (!handled ||
 | 
						|
                         /* Needed for vertex slide, see [#38756] */
 | 
						|
                         (event->type == MOUSEMOVE))) {
 | 
						|
    t->redraw |= t->handleEvent(t, event);
 | 
						|
  }
 | 
						|
 | 
						|
  /* Try to init modal numinput now, if possible. */
 | 
						|
  if (!(handled || t->redraw) && ((event->val == KM_PRESS) || (event->type == EVT_MODAL_MAP)) &&
 | 
						|
      handleNumInput(t->context, &(t->num), event)) {
 | 
						|
    t->redraw |= TREDRAW_HARD;
 | 
						|
    handled = true;
 | 
						|
  }
 | 
						|
 | 
						|
  if (t->redraw && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
 | 
						|
    WM_window_status_area_tag_redraw(CTX_wm_window(t->context));
 | 
						|
  }
 | 
						|
 | 
						|
  if (handled || t->redraw) {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    return OPERATOR_PASS_THROUGH;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool calculateTransformCenter(bContext *C, int centerMode, float cent3d[3], float cent2d[2])
 | 
						|
{
 | 
						|
  TransInfo *t = MEM_callocN(sizeof(TransInfo), "TransInfo data");
 | 
						|
  bool success;
 | 
						|
 | 
						|
  t->context = C;
 | 
						|
 | 
						|
  t->state = TRANS_RUNNING;
 | 
						|
 | 
						|
  /* avoid calculating PET */
 | 
						|
  t->options = CTX_NO_PET;
 | 
						|
 | 
						|
  t->mode = TFM_DUMMY;
 | 
						|
 | 
						|
  initTransInfo(C, t, NULL, NULL);
 | 
						|
 | 
						|
  /* avoid doing connectivity lookups (when V3D_AROUND_LOCAL_ORIGINS is set) */
 | 
						|
  t->around = V3D_AROUND_CENTER_BOUNDS;
 | 
						|
 | 
						|
  createTransData(C, t);  // make TransData structs from selection
 | 
						|
 | 
						|
  t->around = centerMode;  // override userdefined mode
 | 
						|
 | 
						|
  if (t->data_len_all == 0) {
 | 
						|
    success = false;
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    success = true;
 | 
						|
 | 
						|
    calculateCenter(t);
 | 
						|
 | 
						|
    if (cent2d) {
 | 
						|
      copy_v2_v2(cent2d, t->center2d);
 | 
						|
    }
 | 
						|
 | 
						|
    if (cent3d) {
 | 
						|
      // Copy center from constraint center. Transform center can be local
 | 
						|
      copy_v3_v3(cent3d, t->center_global);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* aftertrans does insert keyframes, and clears base flags; doesn't read transdata */
 | 
						|
  special_aftertrans_update(C, t);
 | 
						|
 | 
						|
  postTrans(C, t);
 | 
						|
 | 
						|
  MEM_freeN(t);
 | 
						|
 | 
						|
  return success;
 | 
						|
}
 | 
						|
 | 
						|
typedef enum {
 | 
						|
  UP,
 | 
						|
  DOWN,
 | 
						|
  LEFT,
 | 
						|
  RIGHT,
 | 
						|
} ArrowDirection;
 | 
						|
 | 
						|
#define POS_INDEX 0
 | 
						|
/* NOTE: this --^ is a bit hackish, but simplifies GPUVertFormat usage among functions
 | 
						|
 * private to this file  - merwin
 | 
						|
 */
 | 
						|
 | 
						|
static void drawArrow(ArrowDirection d, short offset, short length, short size)
 | 
						|
{
 | 
						|
  immBegin(GPU_PRIM_LINES, 6);
 | 
						|
 | 
						|
  switch (d) {
 | 
						|
    case LEFT:
 | 
						|
      offset = -offset;
 | 
						|
      length = -length;
 | 
						|
      size = -size;
 | 
						|
      ATTR_FALLTHROUGH;
 | 
						|
    case RIGHT:
 | 
						|
      immVertex2f(POS_INDEX, offset, 0);
 | 
						|
      immVertex2f(POS_INDEX, offset + length, 0);
 | 
						|
      immVertex2f(POS_INDEX, offset + length, 0);
 | 
						|
      immVertex2f(POS_INDEX, offset + length - size, -size);
 | 
						|
      immVertex2f(POS_INDEX, offset + length, 0);
 | 
						|
      immVertex2f(POS_INDEX, offset + length - size, size);
 | 
						|
      break;
 | 
						|
 | 
						|
    case DOWN:
 | 
						|
      offset = -offset;
 | 
						|
      length = -length;
 | 
						|
      size = -size;
 | 
						|
      ATTR_FALLTHROUGH;
 | 
						|
    case UP:
 | 
						|
      immVertex2f(POS_INDEX, 0, offset);
 | 
						|
      immVertex2f(POS_INDEX, 0, offset + length);
 | 
						|
      immVertex2f(POS_INDEX, 0, offset + length);
 | 
						|
      immVertex2f(POS_INDEX, -size, offset + length - size);
 | 
						|
      immVertex2f(POS_INDEX, 0, offset + length);
 | 
						|
      immVertex2f(POS_INDEX, size, offset + length - size);
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  immEnd();
 | 
						|
}
 | 
						|
 | 
						|
static void drawArrowHead(ArrowDirection d, short size)
 | 
						|
{
 | 
						|
  immBegin(GPU_PRIM_LINES, 4);
 | 
						|
 | 
						|
  switch (d) {
 | 
						|
    case LEFT:
 | 
						|
      size = -size;
 | 
						|
      ATTR_FALLTHROUGH;
 | 
						|
    case RIGHT:
 | 
						|
      immVertex2f(POS_INDEX, 0, 0);
 | 
						|
      immVertex2f(POS_INDEX, -size, -size);
 | 
						|
      immVertex2f(POS_INDEX, 0, 0);
 | 
						|
      immVertex2f(POS_INDEX, -size, size);
 | 
						|
      break;
 | 
						|
 | 
						|
    case DOWN:
 | 
						|
      size = -size;
 | 
						|
      ATTR_FALLTHROUGH;
 | 
						|
    case UP:
 | 
						|
      immVertex2f(POS_INDEX, 0, 0);
 | 
						|
      immVertex2f(POS_INDEX, -size, -size);
 | 
						|
      immVertex2f(POS_INDEX, 0, 0);
 | 
						|
      immVertex2f(POS_INDEX, size, -size);
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  immEnd();
 | 
						|
}
 | 
						|
 | 
						|
static void drawArc(float size, float angle_start, float angle_end, int segments)
 | 
						|
{
 | 
						|
  float delta = (angle_end - angle_start) / segments;
 | 
						|
  float angle;
 | 
						|
  int a;
 | 
						|
 | 
						|
  immBegin(GPU_PRIM_LINE_STRIP, segments + 1);
 | 
						|
 | 
						|
  for (angle = angle_start, a = 0; a < segments; angle += delta, a++) {
 | 
						|
    immVertex2f(POS_INDEX, cosf(angle) * size, sinf(angle) * size);
 | 
						|
  }
 | 
						|
  immVertex2f(POS_INDEX, cosf(angle_end) * size, sinf(angle_end) * size);
 | 
						|
 | 
						|
  immEnd();
 | 
						|
}
 | 
						|
 | 
						|
static bool helpline_poll(bContext *C)
 | 
						|
{
 | 
						|
  ARegion *ar = CTX_wm_region(C);
 | 
						|
 | 
						|
  if (ar && ar->regiontype == RGN_TYPE_WINDOW) {
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void drawHelpline(bContext *UNUSED(C), int x, int y, void *customdata)
 | 
						|
{
 | 
						|
  TransInfo *t = (TransInfo *)customdata;
 | 
						|
 | 
						|
  if (t->helpline != HLP_NONE) {
 | 
						|
    float cent[2];
 | 
						|
    float mval[3] = {
 | 
						|
        x,
 | 
						|
        y,
 | 
						|
        0.0f,
 | 
						|
    };
 | 
						|
    float tmval[2] = {
 | 
						|
        (float)t->mval[0],
 | 
						|
        (float)t->mval[1],
 | 
						|
    };
 | 
						|
 | 
						|
    projectFloatViewEx(t, t->center_global, cent, V3D_PROJ_TEST_CLIP_ZERO);
 | 
						|
    /* Offset the values for the area region. */
 | 
						|
    const float offset[2] = {
 | 
						|
        t->ar->winrct.xmin,
 | 
						|
        t->ar->winrct.ymin,
 | 
						|
    };
 | 
						|
 | 
						|
    for (int i = 0; i < 2; i++) {
 | 
						|
      cent[i] += offset[i];
 | 
						|
      tmval[i] += offset[i];
 | 
						|
    }
 | 
						|
 | 
						|
    GPU_matrix_push();
 | 
						|
 | 
						|
    /* Dashed lines first. */
 | 
						|
    if (ELEM(t->helpline, HLP_SPRING, HLP_ANGLE)) {
 | 
						|
      const uint shdr_pos = GPU_vertformat_attr_add(
 | 
						|
          immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
 | 
						|
 | 
						|
      UNUSED_VARS_NDEBUG(shdr_pos); /* silence warning */
 | 
						|
      BLI_assert(shdr_pos == POS_INDEX);
 | 
						|
 | 
						|
      GPU_line_width(1.0f);
 | 
						|
 | 
						|
      immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
 | 
						|
 | 
						|
      float viewport_size[4];
 | 
						|
      GPU_viewport_size_get_f(viewport_size);
 | 
						|
      immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
 | 
						|
 | 
						|
      immUniform1i("colors_len", 0); /* "simple" mode */
 | 
						|
      immUniformThemeColor(TH_VIEW_OVERLAY);
 | 
						|
      immUniform1f("dash_width", 6.0f);
 | 
						|
      immUniform1f("dash_factor", 0.5f);
 | 
						|
 | 
						|
      immBegin(GPU_PRIM_LINES, 2);
 | 
						|
      immVertex2fv(POS_INDEX, cent);
 | 
						|
      immVertex2f(POS_INDEX, tmval[0], tmval[1]);
 | 
						|
      immEnd();
 | 
						|
 | 
						|
      immUnbindProgram();
 | 
						|
    }
 | 
						|
 | 
						|
    /* And now, solid lines. */
 | 
						|
    uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
 | 
						|
    UNUSED_VARS_NDEBUG(pos); /* silence warning */
 | 
						|
    BLI_assert(pos == POS_INDEX);
 | 
						|
    immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
 | 
						|
 | 
						|
    switch (t->helpline) {
 | 
						|
      case HLP_SPRING:
 | 
						|
        immUniformThemeColor(TH_VIEW_OVERLAY);
 | 
						|
 | 
						|
        GPU_matrix_translate_3fv(mval);
 | 
						|
        GPU_matrix_rotate_axis(-RAD2DEGF(atan2f(cent[0] - tmval[0], cent[1] - tmval[1])), 'Z');
 | 
						|
 | 
						|
        GPU_line_width(3.0f);
 | 
						|
        drawArrow(UP, 5, 10, 5);
 | 
						|
        drawArrow(DOWN, 5, 10, 5);
 | 
						|
        break;
 | 
						|
      case HLP_HARROW:
 | 
						|
        immUniformThemeColor(TH_VIEW_OVERLAY);
 | 
						|
        GPU_matrix_translate_3fv(mval);
 | 
						|
 | 
						|
        GPU_line_width(3.0f);
 | 
						|
        drawArrow(RIGHT, 5, 10, 5);
 | 
						|
        drawArrow(LEFT, 5, 10, 5);
 | 
						|
        break;
 | 
						|
      case HLP_VARROW:
 | 
						|
        immUniformThemeColor(TH_VIEW_OVERLAY);
 | 
						|
 | 
						|
        GPU_matrix_translate_3fv(mval);
 | 
						|
 | 
						|
        GPU_line_width(3.0f);
 | 
						|
        drawArrow(UP, 5, 10, 5);
 | 
						|
        drawArrow(DOWN, 5, 10, 5);
 | 
						|
        break;
 | 
						|
      case HLP_CARROW: {
 | 
						|
        /* Draw arrow based on direction defined by custom-points. */
 | 
						|
        immUniformThemeColor(TH_VIEW_OVERLAY);
 | 
						|
 | 
						|
        GPU_matrix_translate_3fv(mval);
 | 
						|
 | 
						|
        GPU_line_width(3.0f);
 | 
						|
 | 
						|
        const int *data = t->mouse.data;
 | 
						|
        const float dx = data[2] - data[0], dy = data[3] - data[1];
 | 
						|
        const float angle = -atan2f(dx, dy);
 | 
						|
 | 
						|
        GPU_matrix_push();
 | 
						|
 | 
						|
        GPU_matrix_rotate_axis(RAD2DEGF(angle), 'Z');
 | 
						|
 | 
						|
        drawArrow(UP, 5, 10, 5);
 | 
						|
        drawArrow(DOWN, 5, 10, 5);
 | 
						|
 | 
						|
        GPU_matrix_pop();
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      case HLP_ANGLE: {
 | 
						|
        float dx = tmval[0] - cent[0], dy = tmval[1] - cent[1];
 | 
						|
        float angle = atan2f(dy, dx);
 | 
						|
        float dist = hypotf(dx, dy);
 | 
						|
        float delta_angle = min_ff(15.0f / dist, (float)M_PI / 4.0f);
 | 
						|
        float spacing_angle = min_ff(5.0f / dist, (float)M_PI / 12.0f);
 | 
						|
 | 
						|
        immUniformThemeColor(TH_VIEW_OVERLAY);
 | 
						|
 | 
						|
        GPU_matrix_translate_3f(cent[0] - tmval[0] + mval[0], cent[1] - tmval[1] + mval[1], 0);
 | 
						|
 | 
						|
        GPU_line_width(3.0f);
 | 
						|
        drawArc(dist, angle - delta_angle, angle - spacing_angle, 10);
 | 
						|
        drawArc(dist, angle + spacing_angle, angle + delta_angle, 10);
 | 
						|
 | 
						|
        GPU_matrix_push();
 | 
						|
 | 
						|
        GPU_matrix_translate_3f(
 | 
						|
            cosf(angle - delta_angle) * dist, sinf(angle - delta_angle) * dist, 0);
 | 
						|
        GPU_matrix_rotate_axis(RAD2DEGF(angle - delta_angle), 'Z');
 | 
						|
 | 
						|
        drawArrowHead(DOWN, 5);
 | 
						|
 | 
						|
        GPU_matrix_pop();
 | 
						|
 | 
						|
        GPU_matrix_translate_3f(
 | 
						|
            cosf(angle + delta_angle) * dist, sinf(angle + delta_angle) * dist, 0);
 | 
						|
        GPU_matrix_rotate_axis(RAD2DEGF(angle + delta_angle), 'Z');
 | 
						|
 | 
						|
        drawArrowHead(UP, 5);
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      case HLP_TRACKBALL: {
 | 
						|
        unsigned char col[3], col2[3];
 | 
						|
        UI_GetThemeColor3ubv(TH_GRID, col);
 | 
						|
 | 
						|
        GPU_matrix_translate_3fv(mval);
 | 
						|
 | 
						|
        GPU_line_width(3.0f);
 | 
						|
 | 
						|
        UI_make_axis_color(col, col2, 'X');
 | 
						|
        immUniformColor3ubv(col2);
 | 
						|
 | 
						|
        drawArrow(RIGHT, 5, 10, 5);
 | 
						|
        drawArrow(LEFT, 5, 10, 5);
 | 
						|
 | 
						|
        UI_make_axis_color(col, col2, 'Y');
 | 
						|
        immUniformColor3ubv(col2);
 | 
						|
 | 
						|
        drawArrow(UP, 5, 10, 5);
 | 
						|
        drawArrow(DOWN, 5, 10, 5);
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    immUnbindProgram();
 | 
						|
    GPU_matrix_pop();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static bool transinfo_show_overlay(const struct bContext *C, TransInfo *t, ARegion *ar)
 | 
						|
{
 | 
						|
  /* Don't show overlays when not the active view and when overlay is disabled: T57139 */
 | 
						|
  bool ok = false;
 | 
						|
  if (ar == t->ar) {
 | 
						|
    ok = true;
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    ScrArea *sa = CTX_wm_area(C);
 | 
						|
    if (sa->spacetype == SPACE_VIEW3D) {
 | 
						|
      View3D *v3d = sa->spacedata.first;
 | 
						|
      if ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) {
 | 
						|
        ok = true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return ok;
 | 
						|
}
 | 
						|
 | 
						|
static void drawTransformView(const struct bContext *C, ARegion *ar, void *arg)
 | 
						|
{
 | 
						|
  TransInfo *t = arg;
 | 
						|
 | 
						|
  if (!transinfo_show_overlay(C, t, ar)) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  GPU_line_width(1.0f);
 | 
						|
 | 
						|
  drawConstraint(t);
 | 
						|
  drawPropCircle(C, t);
 | 
						|
  drawSnapping(C, t);
 | 
						|
 | 
						|
  if (ar == t->ar) {
 | 
						|
    /* edge slide, vert slide */
 | 
						|
    drawEdgeSlide(t);
 | 
						|
    drawVertSlide(t);
 | 
						|
 | 
						|
    /* Rotation */
 | 
						|
    drawDial3d(t);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* just draw a little warning message in the top-right corner of the viewport
 | 
						|
 * to warn that autokeying is enabled */
 | 
						|
static void drawAutoKeyWarning(TransInfo *UNUSED(t), ARegion *ar)
 | 
						|
{
 | 
						|
  rcti rect;
 | 
						|
  const char *printable = IFACE_("Auto Keying On");
 | 
						|
  float printable_size[2];
 | 
						|
  int xco, yco;
 | 
						|
 | 
						|
  ED_region_visible_rect(ar, &rect);
 | 
						|
 | 
						|
  const int font_id = BLF_default();
 | 
						|
  BLF_width_and_height(
 | 
						|
      font_id, printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]);
 | 
						|
 | 
						|
  xco = (rect.xmax - U.widget_unit) - (int)printable_size[0];
 | 
						|
  yco = (rect.ymax - U.widget_unit);
 | 
						|
 | 
						|
  /* warning text (to clarify meaning of overlays)
 | 
						|
   * - original color was red to match the icon, but that clashes badly with a less nasty border
 | 
						|
   */
 | 
						|
  unsigned char color[3];
 | 
						|
  UI_GetThemeColorShade3ubv(TH_TEXT_HI, -50, color);
 | 
						|
  BLF_color3ubv(font_id, color);
 | 
						|
#ifdef WITH_INTERNATIONAL
 | 
						|
  BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX);
 | 
						|
#else
 | 
						|
  BLF_draw_default_ascii(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX);
 | 
						|
#endif
 | 
						|
 | 
						|
  /* autokey recording icon... */
 | 
						|
  GPU_blend_set_func_separate(
 | 
						|
      GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
 | 
						|
  GPU_blend(true);
 | 
						|
 | 
						|
  xco -= U.widget_unit;
 | 
						|
  yco -= (int)printable_size[1] / 2;
 | 
						|
 | 
						|
  UI_icon_draw(xco, yco, ICON_REC);
 | 
						|
 | 
						|
  GPU_blend(false);
 | 
						|
}
 | 
						|
 | 
						|
static void drawTransformPixel(const struct bContext *C, ARegion *ar, void *arg)
 | 
						|
{
 | 
						|
  TransInfo *t = arg;
 | 
						|
 | 
						|
  if (!transinfo_show_overlay(C, t, ar)) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (ar == t->ar) {
 | 
						|
    Scene *scene = t->scene;
 | 
						|
    ViewLayer *view_layer = t->view_layer;
 | 
						|
    Object *ob = OBACT(view_layer);
 | 
						|
 | 
						|
    /* draw auto-key-framing hint in the corner
 | 
						|
     * - only draw if enabled (advanced users may be distracted/annoyed),
 | 
						|
     *   for objects that will be autokeyframed (no point otherwise),
 | 
						|
     *   AND only for the active region (as showing all is too overwhelming)
 | 
						|
     */
 | 
						|
    if ((U.autokey_flag & AUTOKEY_FLAG_NOWARNING) == 0) {
 | 
						|
      if (ar == t->ar) {
 | 
						|
        if (t->flag & (T_OBJECT | T_POSE)) {
 | 
						|
          if (ob && autokeyframe_cfra_can_key(scene, &ob->id)) {
 | 
						|
            drawAutoKeyWarning(t, ar);
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * \see #initTransform which reads values from the operator.
 | 
						|
 */
 | 
						|
void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
 | 
						|
{
 | 
						|
  ToolSettings *ts = CTX_data_tool_settings(C);
 | 
						|
  int proportional = 0;
 | 
						|
  PropertyRNA *prop;
 | 
						|
 | 
						|
  // Save back mode in case we're in the generic operator
 | 
						|
  if ((prop = RNA_struct_find_property(op->ptr, "mode"))) {
 | 
						|
    RNA_property_enum_set(op->ptr, prop, t->mode);
 | 
						|
  }
 | 
						|
 | 
						|
  if ((prop = RNA_struct_find_property(op->ptr, "value"))) {
 | 
						|
    float values[4];
 | 
						|
 | 
						|
    copy_v4_v4(values, (t->flag & T_AUTOVALUES) ? t->auto_values : t->values);
 | 
						|
 | 
						|
    if (RNA_property_array_check(prop)) {
 | 
						|
      RNA_property_float_set_array(op->ptr, prop, values);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      RNA_property_float_set(op->ptr, prop, values[0]);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (t->flag & T_PROP_EDIT_ALL) {
 | 
						|
    if (t->flag & T_PROP_EDIT) {
 | 
						|
      proportional |= PROP_EDIT_USE;
 | 
						|
    }
 | 
						|
    if (t->flag & T_PROP_CONNECTED) {
 | 
						|
      proportional |= PROP_EDIT_CONNECTED;
 | 
						|
    }
 | 
						|
    if (t->flag & T_PROP_PROJECTED) {
 | 
						|
      proportional |= PROP_EDIT_PROJECTED;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // If modal, save settings back in scene if not set as operator argument
 | 
						|
  if ((t->flag & T_MODAL) || (op->flag & OP_IS_REPEAT)) {
 | 
						|
    /* save settings if not set in operator */
 | 
						|
 | 
						|
    /* skip saving proportional edit if it was not actually used */
 | 
						|
    if (!(t->options & CTX_NO_PET)) {
 | 
						|
      if ((prop = RNA_struct_find_property(op->ptr, "use_proportional_edit")) &&
 | 
						|
          !RNA_property_is_set(op->ptr, prop)) {
 | 
						|
        if (t->spacetype == SPACE_GRAPH) {
 | 
						|
          ts->proportional_fcurve = proportional;
 | 
						|
        }
 | 
						|
        else if (t->spacetype == SPACE_ACTION) {
 | 
						|
          ts->proportional_action = proportional;
 | 
						|
        }
 | 
						|
        else if (t->obedit_type != -1) {
 | 
						|
          ts->proportional_edit = proportional;
 | 
						|
        }
 | 
						|
        else if (t->options & CTX_MASK) {
 | 
						|
          ts->proportional_mask = proportional != 0;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          ts->proportional_objects = proportional != 0;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      if ((prop = RNA_struct_find_property(op->ptr, "proportional_size"))) {
 | 
						|
        ts->proportional_size = RNA_property_is_set(op->ptr, prop) ?
 | 
						|
                                    RNA_property_float_get(op->ptr, prop) :
 | 
						|
                                    t->prop_size;
 | 
						|
      }
 | 
						|
 | 
						|
      if ((prop = RNA_struct_find_property(op->ptr, "proportional_edit_falloff")) &&
 | 
						|
          !RNA_property_is_set(op->ptr, prop)) {
 | 
						|
        ts->prop_mode = t->prop_mode;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    /* do we check for parameter? */
 | 
						|
    if (transformModeUseSnap(t)) {
 | 
						|
      if (t->modifiers & MOD_SNAP) {
 | 
						|
        ts->snap_flag |= SCE_SNAP;
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        ts->snap_flag &= ~SCE_SNAP;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (t->spacetype == SPACE_VIEW3D) {
 | 
						|
      if ((prop = RNA_struct_find_property(op->ptr, "orient_type")) &&
 | 
						|
          !RNA_property_is_set(op->ptr, prop) &&
 | 
						|
          (t->orientation.user != V3D_ORIENT_CUSTOM_MATRIX)) {
 | 
						|
        TransformOrientationSlot *orient_slot = &t->scene->orientation_slots[SCE_ORIENT_DEFAULT];
 | 
						|
        orient_slot->type = t->orientation.user;
 | 
						|
        BLI_assert(((orient_slot->index_custom == -1) && (t->orientation.custom == NULL)) ||
 | 
						|
                   (BKE_scene_transform_orientation_get_index(t->scene, t->orientation.custom) ==
 | 
						|
                    orient_slot->index_custom));
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if ((prop = RNA_struct_find_property(op->ptr, "use_proportional_edit"))) {
 | 
						|
    RNA_property_boolean_set(op->ptr, prop, proportional & PROP_EDIT_USE);
 | 
						|
    RNA_boolean_set(op->ptr, "use_proportional_connected", proportional & PROP_EDIT_CONNECTED);
 | 
						|
    RNA_boolean_set(op->ptr, "use_proportional_projected", proportional & PROP_EDIT_PROJECTED);
 | 
						|
    RNA_enum_set(op->ptr, "proportional_edit_falloff", t->prop_mode);
 | 
						|
    RNA_float_set(op->ptr, "proportional_size", t->prop_size);
 | 
						|
  }
 | 
						|
 | 
						|
  if ((prop = RNA_struct_find_property(op->ptr, "mirror"))) {
 | 
						|
    RNA_property_boolean_set(op->ptr, prop, (t->flag & T_NO_MIRROR) == 0);
 | 
						|
  }
 | 
						|
 | 
						|
  /* Orientation used for redo. */
 | 
						|
  const bool use_orient_axis = (t->orient_matrix_is_set &&
 | 
						|
                                (RNA_struct_find_property(op->ptr, "orient_axis") != NULL));
 | 
						|
  short orientation;
 | 
						|
  if (t->con.mode & CON_APPLY) {
 | 
						|
    orientation = t->con.orientation;
 | 
						|
    if (orientation == V3D_ORIENT_CUSTOM) {
 | 
						|
      const int orientation_index_custom = BKE_scene_transform_orientation_get_index(
 | 
						|
          t->scene, t->orientation.custom);
 | 
						|
      /* Maybe we need a t->con.custom_orientation?
 | 
						|
       * Seems like it would always match t->orientation.custom. */
 | 
						|
      orientation = V3D_ORIENT_CUSTOM + orientation_index_custom;
 | 
						|
      BLI_assert(orientation >= V3D_ORIENT_CUSTOM);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if ((t->orientation.user == V3D_ORIENT_CUSTOM_MATRIX) &&
 | 
						|
           (prop = RNA_struct_find_property(op->ptr, "orient_matrix_type"))) {
 | 
						|
    orientation = RNA_property_enum_get(op->ptr, prop);
 | 
						|
  }
 | 
						|
  else if (use_orient_axis) {
 | 
						|
    /* We're not using an orientation, use the fallback. */
 | 
						|
    orientation = t->orientation.unset;
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    orientation = V3D_ORIENT_GLOBAL;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((prop = RNA_struct_find_property(op->ptr, "orient_axis"))) {
 | 
						|
    if (t->flag & T_MODAL) {
 | 
						|
      if (t->con.mode & CON_APPLY) {
 | 
						|
        int orient_axis = constraintModeToIndex(t);
 | 
						|
        if (orient_axis != -1) {
 | 
						|
          RNA_property_enum_set(op->ptr, prop, orient_axis);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        RNA_property_enum_set(op->ptr, prop, t->orient_axis);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if ((prop = RNA_struct_find_property(op->ptr, "orient_axis_ortho"))) {
 | 
						|
    if (t->flag & T_MODAL) {
 | 
						|
      RNA_property_enum_set(op->ptr, prop, t->orient_axis_ortho);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if ((prop = RNA_struct_find_property(op->ptr, "orient_matrix"))) {
 | 
						|
    if (t->flag & T_MODAL) {
 | 
						|
      if (orientation != V3D_ORIENT_CUSTOM_MATRIX) {
 | 
						|
        if (t->flag & T_MODAL) {
 | 
						|
          RNA_enum_set(op->ptr, "orient_matrix_type", orientation);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      if (t->con.mode & CON_APPLY) {
 | 
						|
        RNA_float_set_array(op->ptr, "orient_matrix", &t->con.mtx[0][0]);
 | 
						|
      }
 | 
						|
      else if (use_orient_axis) {
 | 
						|
        RNA_float_set_array(op->ptr, "orient_matrix", &t->orient_matrix[0][0]);
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        RNA_float_set_array(op->ptr, "orient_matrix", &t->spacemtx[0][0]);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if ((prop = RNA_struct_find_property(op->ptr, "orient_type"))) {
 | 
						|
    /* constraint orientation can be global, even if user selects something else
 | 
						|
     * so use the orientation in the constraint if set */
 | 
						|
 | 
						|
    /* Use 'orient_matrix' instead. */
 | 
						|
    if (t->flag & T_MODAL) {
 | 
						|
      if (orientation != V3D_ORIENT_CUSTOM_MATRIX) {
 | 
						|
        RNA_property_enum_set(op->ptr, prop, orientation);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis"))) {
 | 
						|
    bool constraint_axis[3] = {false, false, false};
 | 
						|
    if (t->flag & T_MODAL) {
 | 
						|
      /* Only set if needed, so we can hide in the UI when nothing is set.
 | 
						|
       * See 'transform_poll_property'. */
 | 
						|
      if (t->con.mode & CON_APPLY) {
 | 
						|
        if (t->con.mode & CON_AXIS0) {
 | 
						|
          constraint_axis[0] = true;
 | 
						|
        }
 | 
						|
        if (t->con.mode & CON_AXIS1) {
 | 
						|
          constraint_axis[1] = true;
 | 
						|
        }
 | 
						|
        if (t->con.mode & CON_AXIS2) {
 | 
						|
          constraint_axis[2] = true;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      if (ELEM(true, UNPACK3(constraint_axis))) {
 | 
						|
        RNA_property_boolean_set_array(op->ptr, prop, constraint_axis);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  {
 | 
						|
    const char *prop_id = NULL;
 | 
						|
    bool prop_state = true;
 | 
						|
    if (t->mode == TFM_SHRINKFATTEN) {
 | 
						|
      prop_id = "use_even_offset";
 | 
						|
      prop_state = false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (prop_id && (prop = RNA_struct_find_property(op->ptr, prop_id))) {
 | 
						|
      RNA_property_boolean_set(op->ptr, prop, ((t->flag & T_ALT_TRANSFORM) == 0) == prop_state);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if ((prop = RNA_struct_find_property(op->ptr, "correct_uv"))) {
 | 
						|
    RNA_property_boolean_set(
 | 
						|
        op->ptr, prop, (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) != 0);
 | 
						|
  }
 | 
						|
 | 
						|
  if (t->mode == TFM_SHEAR) {
 | 
						|
    prop = RNA_struct_find_property(op->ptr, "shear_axis");
 | 
						|
    t->custom.mode.data = POINTER_FROM_INT(RNA_property_enum_get(op->ptr, prop));
 | 
						|
    RNA_property_enum_set(op->ptr, prop, POINTER_AS_INT(t->custom.mode.data));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * \note  caller needs to free 't' on a 0 return
 | 
						|
 * \warning  \a event might be NULL (when tweaking from redo panel)
 | 
						|
 * \see #saveTransform which writes these values back.
 | 
						|
 */
 | 
						|
bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *event, int mode)
 | 
						|
{
 | 
						|
  int options = 0;
 | 
						|
  PropertyRNA *prop;
 | 
						|
 | 
						|
  t->context = C;
 | 
						|
 | 
						|
  /* added initialize, for external calls to set stuff in TransInfo, like undo string */
 | 
						|
 | 
						|
  t->state = TRANS_STARTING;
 | 
						|
 | 
						|
  if ((prop = RNA_struct_find_property(op->ptr, "cursor_transform")) &&
 | 
						|
      RNA_property_is_set(op->ptr, prop)) {
 | 
						|
    if (RNA_property_boolean_get(op->ptr, prop)) {
 | 
						|
      options |= CTX_CURSOR;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if ((prop = RNA_struct_find_property(op->ptr, "texture_space")) &&
 | 
						|
      RNA_property_is_set(op->ptr, prop)) {
 | 
						|
    if (RNA_property_boolean_get(op->ptr, prop)) {
 | 
						|
      options |= CTX_TEXTURE;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if ((prop = RNA_struct_find_property(op->ptr, "gpencil_strokes")) &&
 | 
						|
      RNA_property_is_set(op->ptr, prop)) {
 | 
						|
    if (RNA_property_boolean_get(op->ptr, prop)) {
 | 
						|
      options |= CTX_GPENCIL_STROKES;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  t->options = options;
 | 
						|
 | 
						|
  t->mode = mode;
 | 
						|
 | 
						|
  /* Needed to translate tweak events to mouse buttons. */
 | 
						|
  t->launch_event = event ? WM_userdef_event_type_from_keymap_type(event->type) : -1;
 | 
						|
 | 
						|
  /* XXX Remove this when wm_operator_call_internal doesn't use window->eventstate
 | 
						|
   * (which can have type = 0) */
 | 
						|
  /* For gizmo only, so assume LEFTMOUSE. */
 | 
						|
  if (t->launch_event == 0) {
 | 
						|
    t->launch_event = LEFTMOUSE;
 | 
						|
  }
 | 
						|
 | 
						|
  unit_m3(t->spacemtx);
 | 
						|
 | 
						|
  initTransInfo(C, t, op, event);
 | 
						|
  initTransformOrientation(C, t);
 | 
						|
 | 
						|
  if (t->spacetype == SPACE_VIEW3D) {
 | 
						|
    t->draw_handle_apply = ED_region_draw_cb_activate(
 | 
						|
        t->ar->type, drawTransformApply, t, REGION_DRAW_PRE_VIEW);
 | 
						|
    t->draw_handle_view = ED_region_draw_cb_activate(
 | 
						|
        t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
 | 
						|
    t->draw_handle_pixel = ED_region_draw_cb_activate(
 | 
						|
        t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
 | 
						|
    t->draw_handle_cursor = WM_paint_cursor_activate(
 | 
						|
        CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, helpline_poll, drawHelpline, t);
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_IMAGE) {
 | 
						|
    t->draw_handle_view = ED_region_draw_cb_activate(
 | 
						|
        t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
 | 
						|
    t->draw_handle_cursor = WM_paint_cursor_activate(
 | 
						|
        CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, helpline_poll, drawHelpline, t);
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_CLIP) {
 | 
						|
    t->draw_handle_view = ED_region_draw_cb_activate(
 | 
						|
        t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
 | 
						|
    t->draw_handle_cursor = WM_paint_cursor_activate(
 | 
						|
        CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, helpline_poll, drawHelpline, t);
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_NODE) {
 | 
						|
    t->draw_handle_view = ED_region_draw_cb_activate(
 | 
						|
        t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
 | 
						|
    t->draw_handle_cursor = WM_paint_cursor_activate(
 | 
						|
        CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, helpline_poll, drawHelpline, t);
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_GRAPH) {
 | 
						|
    t->draw_handle_view = ED_region_draw_cb_activate(
 | 
						|
        t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
 | 
						|
    t->draw_handle_cursor = WM_paint_cursor_activate(
 | 
						|
        CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, helpline_poll, drawHelpline, t);
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_ACTION) {
 | 
						|
    t->draw_handle_view = ED_region_draw_cb_activate(
 | 
						|
        t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
 | 
						|
    t->draw_handle_cursor = WM_paint_cursor_activate(
 | 
						|
        CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, helpline_poll, drawHelpline, t);
 | 
						|
  }
 | 
						|
 | 
						|
  createTransData(C, t);  // make TransData structs from selection
 | 
						|
 | 
						|
  if (t->data_len_all == 0) {
 | 
						|
    postTrans(C, t);
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  if (event) {
 | 
						|
    /* keymap for shortcut header prints */
 | 
						|
    t->keymap = WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
 | 
						|
 | 
						|
    /* Stupid code to have Ctrl-Click on gizmo work ok.
 | 
						|
     *
 | 
						|
     * Do this only for translation/rotation/resize because only these
 | 
						|
     * modes are available from gizmo and doing such check could
 | 
						|
     * lead to keymap conflicts for other modes (see #31584)
 | 
						|
     */
 | 
						|
    if (ELEM(mode, TFM_TRANSLATION, TFM_ROTATION, TFM_RESIZE)) {
 | 
						|
      wmKeyMapItem *kmi;
 | 
						|
 | 
						|
      for (kmi = t->keymap->items.first; kmi; kmi = kmi->next) {
 | 
						|
        if (kmi->flag & KMI_INACTIVE) {
 | 
						|
          continue;
 | 
						|
        }
 | 
						|
 | 
						|
        if (kmi->propvalue == TFM_MODAL_SNAP_INV_ON && kmi->val == KM_PRESS) {
 | 
						|
          if ((ELEM(kmi->type, LEFTCTRLKEY, RIGHTCTRLKEY) && event->ctrl) ||
 | 
						|
              (ELEM(kmi->type, LEFTSHIFTKEY, RIGHTSHIFTKEY) && event->shift) ||
 | 
						|
              (ELEM(kmi->type, LEFTALTKEY, RIGHTALTKEY) && event->alt) ||
 | 
						|
              ((kmi->type == OSKEY) && event->oskey)) {
 | 
						|
            t->modifiers |= MOD_SNAP_INVERT;
 | 
						|
          }
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  initSnapping(t, op);  // Initialize snapping data AFTER mode flags
 | 
						|
 | 
						|
  initSnapSpatial(t, t->snap_spatial);
 | 
						|
 | 
						|
  /* EVIL! posemode code can switch translation to rotate when 1 bone is selected.
 | 
						|
   * will be removed (ton) */
 | 
						|
 | 
						|
  /* EVIL2: we gave as argument also texture space context bit... was cleared */
 | 
						|
 | 
						|
  /* EVIL3: extend mode for animation editors also switches modes...
 | 
						|
   * but is best way to avoid duplicate code */
 | 
						|
  mode = t->mode;
 | 
						|
 | 
						|
  calculatePropRatio(t);
 | 
						|
  calculateCenter(t);
 | 
						|
 | 
						|
  /* Overwrite initial values if operator supplied a non-null vector.
 | 
						|
   *
 | 
						|
   * Run before init functions so 'values_modal_offset' can be applied on mouse input.
 | 
						|
   */
 | 
						|
  BLI_assert(is_zero_v4(t->values_modal_offset));
 | 
						|
  if ((prop = RNA_struct_find_property(op->ptr, "value")) && RNA_property_is_set(op->ptr, prop)) {
 | 
						|
    float values[4] = {0}; /* in case value isn't length 4, avoid uninitialized memory  */
 | 
						|
 | 
						|
    if (RNA_property_array_check(prop)) {
 | 
						|
      RNA_float_get_array(op->ptr, "value", values);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      values[0] = RNA_float_get(op->ptr, "value");
 | 
						|
    }
 | 
						|
 | 
						|
    copy_v4_v4(t->values, values);
 | 
						|
 | 
						|
    if (t->flag & T_MODAL) {
 | 
						|
      copy_v4_v4(t->values_modal_offset, values);
 | 
						|
      t->redraw = TREDRAW_HARD;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      copy_v4_v4(t->auto_values, values);
 | 
						|
      t->flag |= T_AUTOVALUES;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (event) {
 | 
						|
    /* Initialize accurate transform to settings requested by keymap. */
 | 
						|
    bool use_accurate = false;
 | 
						|
    if ((prop = RNA_struct_find_property(op->ptr, "use_accurate")) &&
 | 
						|
        RNA_property_is_set(op->ptr, prop)) {
 | 
						|
      if (RNA_property_boolean_get(op->ptr, prop)) {
 | 
						|
        use_accurate = true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    initMouseInput(t, &t->mouse, t->center2d, event->mval, use_accurate);
 | 
						|
  }
 | 
						|
 | 
						|
  switch (mode) {
 | 
						|
    case TFM_TRANSLATION:
 | 
						|
      initTranslation(t);
 | 
						|
      break;
 | 
						|
    case TFM_ROTATION:
 | 
						|
      initRotation(t);
 | 
						|
      break;
 | 
						|
    case TFM_RESIZE:
 | 
						|
      initResize(t);
 | 
						|
      break;
 | 
						|
    case TFM_SKIN_RESIZE:
 | 
						|
      initSkinResize(t);
 | 
						|
      break;
 | 
						|
    case TFM_TOSPHERE:
 | 
						|
      initToSphere(t);
 | 
						|
      break;
 | 
						|
    case TFM_SHEAR:
 | 
						|
      prop = RNA_struct_find_property(op->ptr, "shear_axis");
 | 
						|
      t->custom.mode.data = POINTER_FROM_INT(RNA_property_enum_get(op->ptr, prop));
 | 
						|
      initShear(t);
 | 
						|
      break;
 | 
						|
    case TFM_BEND:
 | 
						|
      initBend(t);
 | 
						|
      break;
 | 
						|
    case TFM_SHRINKFATTEN:
 | 
						|
      initShrinkFatten(t);
 | 
						|
      break;
 | 
						|
    case TFM_TILT:
 | 
						|
      initTilt(t);
 | 
						|
      break;
 | 
						|
    case TFM_CURVE_SHRINKFATTEN:
 | 
						|
      initCurveShrinkFatten(t);
 | 
						|
      break;
 | 
						|
    case TFM_MASK_SHRINKFATTEN:
 | 
						|
      initMaskShrinkFatten(t);
 | 
						|
      break;
 | 
						|
    case TFM_GPENCIL_SHRINKFATTEN:
 | 
						|
      initGPShrinkFatten(t);
 | 
						|
      break;
 | 
						|
    case TFM_TRACKBALL:
 | 
						|
      initTrackball(t);
 | 
						|
      break;
 | 
						|
    case TFM_PUSHPULL:
 | 
						|
      initPushPull(t);
 | 
						|
      break;
 | 
						|
    case TFM_CREASE:
 | 
						|
      initCrease(t);
 | 
						|
      break;
 | 
						|
    case TFM_BONESIZE: { /* used for both B-Bone width (bonesize) as for deform-dist (envelope) */
 | 
						|
      /* Note: we have to pick one, use the active object. */
 | 
						|
      TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_OK(t);
 | 
						|
      bArmature *arm = tc->poseobj->data;
 | 
						|
      if (arm->drawtype == ARM_ENVELOPE) {
 | 
						|
        initBoneEnvelope(t);
 | 
						|
        t->mode = TFM_BONE_ENVELOPE_DIST;
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        initBoneSize(t);
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case TFM_BONE_ENVELOPE:
 | 
						|
      initBoneEnvelope(t);
 | 
						|
      break;
 | 
						|
    case TFM_BONE_ENVELOPE_DIST:
 | 
						|
      initBoneEnvelope(t);
 | 
						|
      t->mode = TFM_BONE_ENVELOPE_DIST;
 | 
						|
      break;
 | 
						|
    case TFM_EDGE_SLIDE:
 | 
						|
    case TFM_VERT_SLIDE: {
 | 
						|
      const bool use_even = (op ? RNA_boolean_get(op->ptr, "use_even") : false);
 | 
						|
      const bool flipped = (op ? RNA_boolean_get(op->ptr, "flipped") : false);
 | 
						|
      const bool use_clamp = (op ? RNA_boolean_get(op->ptr, "use_clamp") : true);
 | 
						|
      if (mode == TFM_EDGE_SLIDE) {
 | 
						|
        const bool use_double_side = (op ? !RNA_boolean_get(op->ptr, "single_side") : true);
 | 
						|
        initEdgeSlide_ex(t, use_double_side, use_even, flipped, use_clamp);
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        initVertSlide_ex(t, use_even, flipped, use_clamp);
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    case TFM_BONE_ROLL:
 | 
						|
      initBoneRoll(t);
 | 
						|
      break;
 | 
						|
    case TFM_TIME_TRANSLATE:
 | 
						|
      initTimeTranslate(t);
 | 
						|
      break;
 | 
						|
    case TFM_TIME_SLIDE:
 | 
						|
      initTimeSlide(t);
 | 
						|
      break;
 | 
						|
    case TFM_TIME_SCALE:
 | 
						|
      initTimeScale(t);
 | 
						|
      break;
 | 
						|
    case TFM_TIME_DUPLICATE:
 | 
						|
      /* same as TFM_TIME_EXTEND, but we need the mode info for later
 | 
						|
       * so that duplicate-culling will work properly
 | 
						|
       */
 | 
						|
      if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_NLA)) {
 | 
						|
        initTranslation(t);
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        initTimeTranslate(t);
 | 
						|
      }
 | 
						|
      t->mode = mode;
 | 
						|
      break;
 | 
						|
    case TFM_TIME_EXTEND:
 | 
						|
      /* now that transdata has been made, do like for TFM_TIME_TRANSLATE (for most Animation
 | 
						|
       * Editors because they have only 1D transforms for time values) or TFM_TRANSLATION
 | 
						|
       * (for Graph/NLA Editors only since they uses 'standard' transforms to get 2D movement)
 | 
						|
       * depending on which editor this was called from
 | 
						|
       */
 | 
						|
      if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_NLA)) {
 | 
						|
        initTranslation(t);
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        initTimeTranslate(t);
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case TFM_BAKE_TIME:
 | 
						|
      initBakeTime(t);
 | 
						|
      break;
 | 
						|
    case TFM_MIRROR:
 | 
						|
      initMirror(t);
 | 
						|
      break;
 | 
						|
    case TFM_BWEIGHT:
 | 
						|
      initBevelWeight(t);
 | 
						|
      break;
 | 
						|
    case TFM_ALIGN:
 | 
						|
      initAlign(t);
 | 
						|
      break;
 | 
						|
    case TFM_SEQ_SLIDE:
 | 
						|
      initSeqSlide(t);
 | 
						|
      break;
 | 
						|
    case TFM_NORMAL_ROTATION:
 | 
						|
      initNormalRotation(t);
 | 
						|
      break;
 | 
						|
    case TFM_GPENCIL_OPACITY:
 | 
						|
      initGPOpacity(t);
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  if (t->state == TRANS_CANCEL) {
 | 
						|
    postTrans(C, t);
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Transformation axis from operator */
 | 
						|
  if ((prop = RNA_struct_find_property(op->ptr, "orient_axis")) &&
 | 
						|
      RNA_property_is_set(op->ptr, prop)) {
 | 
						|
    t->orient_axis = RNA_property_enum_get(op->ptr, prop);
 | 
						|
  }
 | 
						|
  if ((prop = RNA_struct_find_property(op->ptr, "orient_axis_ortho")) &&
 | 
						|
      RNA_property_is_set(op->ptr, prop)) {
 | 
						|
    t->orient_axis_ortho = RNA_property_enum_get(op->ptr, prop);
 | 
						|
  }
 | 
						|
 | 
						|
  /* Constraint init from operator */
 | 
						|
  if ((t->flag & T_MODAL) ||
 | 
						|
      /* For mirror operator the constraint axes are effectively the values. */
 | 
						|
      (RNA_struct_find_property(op->ptr, "value") == NULL)) {
 | 
						|
    if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis")) &&
 | 
						|
        RNA_property_is_set(op->ptr, prop)) {
 | 
						|
      bool constraint_axis[3];
 | 
						|
 | 
						|
      RNA_property_boolean_get_array(op->ptr, prop, constraint_axis);
 | 
						|
 | 
						|
      if (constraint_axis[0] || constraint_axis[1] || constraint_axis[2]) {
 | 
						|
        t->con.mode |= CON_APPLY;
 | 
						|
 | 
						|
        if (constraint_axis[0]) {
 | 
						|
          t->con.mode |= CON_AXIS0;
 | 
						|
        }
 | 
						|
        if (constraint_axis[1]) {
 | 
						|
          t->con.mode |= CON_AXIS1;
 | 
						|
        }
 | 
						|
        if (constraint_axis[2]) {
 | 
						|
          t->con.mode |= CON_AXIS2;
 | 
						|
        }
 | 
						|
 | 
						|
        setUserConstraint(t, t->orientation.user, t->con.mode, "%s");
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    /* So we can adjust in non global orientation. */
 | 
						|
    if (t->orientation.user != V3D_ORIENT_GLOBAL) {
 | 
						|
      t->con.mode |= CON_APPLY | CON_AXIS0 | CON_AXIS1 | CON_AXIS2;
 | 
						|
      setUserConstraint(t, t->orientation.user, t->con.mode, "%s");
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* Don't write into the values when non-modal because they are already set from operator redo
 | 
						|
   * values. */
 | 
						|
  if (t->flag & T_MODAL) {
 | 
						|
    /* Setup the mouse input with initial values. */
 | 
						|
    applyMouseInput(t, &t->mouse, t->mouse.imval, t->values);
 | 
						|
  }
 | 
						|
 | 
						|
  if ((prop = RNA_struct_find_property(op->ptr, "preserve_clnor"))) {
 | 
						|
    if ((t->flag & T_EDIT) && t->obedit_type == OB_MESH) {
 | 
						|
 | 
						|
      FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
        if ((((Mesh *)(tc->obedit->data))->flag & ME_AUTOSMOOTH)) {
 | 
						|
          BMEditMesh *em = NULL;  // BKE_editmesh_from_object(t->obedit);
 | 
						|
          bool do_skip = false;
 | 
						|
 | 
						|
          /* Currently only used for two of three most frequent transform ops,
 | 
						|
           * can include more ops.
 | 
						|
           * Note that scaling cannot be included here,
 | 
						|
           * non-uniform scaling will affect normals. */
 | 
						|
          if (ELEM(t->mode, TFM_TRANSLATION, TFM_ROTATION)) {
 | 
						|
            if (em->bm->totvertsel == em->bm->totvert) {
 | 
						|
              /* No need to invalidate if whole mesh is selected. */
 | 
						|
              do_skip = true;
 | 
						|
            }
 | 
						|
          }
 | 
						|
 | 
						|
          if (t->flag & T_MODAL) {
 | 
						|
            RNA_property_boolean_set(op->ptr, prop, false);
 | 
						|
          }
 | 
						|
          else if (!do_skip) {
 | 
						|
            const bool preserve_clnor = RNA_property_boolean_get(op->ptr, prop);
 | 
						|
            if (preserve_clnor) {
 | 
						|
              BKE_editmesh_lnorspace_update(em);
 | 
						|
              t->flag |= T_CLNOR_REBUILD;
 | 
						|
            }
 | 
						|
            BM_lnorspace_invalidate(em->bm, true);
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  t->context = NULL;
 | 
						|
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
void transformApply(bContext *C, TransInfo *t)
 | 
						|
{
 | 
						|
  t->context = C;
 | 
						|
 | 
						|
  if ((t->redraw & TREDRAW_HARD) || (t->draw_handle_apply == NULL && (t->redraw & TREDRAW_SOFT))) {
 | 
						|
    selectConstraint(t);
 | 
						|
    if (t->transform) {
 | 
						|
      t->transform(t, t->mval);  // calls recalcData()
 | 
						|
      viewRedrawForce(C, t);
 | 
						|
    }
 | 
						|
    t->redraw = TREDRAW_NOTHING;
 | 
						|
  }
 | 
						|
  else if (t->redraw & TREDRAW_SOFT) {
 | 
						|
    viewRedrawForce(C, t);
 | 
						|
  }
 | 
						|
 | 
						|
  /* If auto confirm is on, break after one pass */
 | 
						|
  if (t->options & CTX_AUTOCONFIRM) {
 | 
						|
    t->state = TRANS_CONFIRM;
 | 
						|
  }
 | 
						|
 | 
						|
  t->context = NULL;
 | 
						|
}
 | 
						|
 | 
						|
static void drawTransformApply(const bContext *C, ARegion *UNUSED(ar), void *arg)
 | 
						|
{
 | 
						|
  TransInfo *t = arg;
 | 
						|
 | 
						|
  if (t->redraw & TREDRAW_SOFT) {
 | 
						|
    t->redraw |= TREDRAW_HARD;
 | 
						|
    transformApply((bContext *)C, t);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
int transformEnd(bContext *C, TransInfo *t)
 | 
						|
{
 | 
						|
  int exit_code = OPERATOR_RUNNING_MODAL;
 | 
						|
 | 
						|
  t->context = C;
 | 
						|
 | 
						|
  if (t->state != TRANS_STARTING && t->state != TRANS_RUNNING) {
 | 
						|
    /* handle restoring objects */
 | 
						|
    if (t->state == TRANS_CANCEL) {
 | 
						|
      /* exception, edge slide transformed UVs too */
 | 
						|
      if (t->mode == TFM_EDGE_SLIDE) {
 | 
						|
        doEdgeSlide(t, 0.0f);
 | 
						|
      }
 | 
						|
      else if (t->mode == TFM_VERT_SLIDE) {
 | 
						|
        doVertSlide(t, 0.0f);
 | 
						|
      }
 | 
						|
 | 
						|
      exit_code = OPERATOR_CANCELLED;
 | 
						|
      restoreTransObjects(t);  // calls recalcData()
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      if (t->flag & T_CLNOR_REBUILD) {
 | 
						|
        FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
          BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
 | 
						|
          BM_lnorspace_rebuild(em->bm, true);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      exit_code = OPERATOR_FINISHED;
 | 
						|
    }
 | 
						|
 | 
						|
    /* aftertrans does insert keyframes, and clears base flags; doesn't read transdata */
 | 
						|
    special_aftertrans_update(C, t);
 | 
						|
 | 
						|
    /* free data */
 | 
						|
    postTrans(C, t);
 | 
						|
 | 
						|
    /* send events out for redraws */
 | 
						|
    viewRedrawPost(C, t);
 | 
						|
 | 
						|
    viewRedrawForce(C, t);
 | 
						|
  }
 | 
						|
 | 
						|
  t->context = NULL;
 | 
						|
 | 
						|
  return exit_code;
 | 
						|
}
 | 
						|
 | 
						|
/* ************************** TRANSFORM LOCKS **************************** */
 | 
						|
 | 
						|
static 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;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* 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);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* ******************* TRANSFORM LIMITS ********************** */
 | 
						|
 | 
						|
static 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 = con->data;
 | 
						|
 | 
						|
        if ((data->flag2 & LIMIT_TRANSFORM) == 0) {
 | 
						|
          continue;
 | 
						|
        }
 | 
						|
        cti = ctiLoc;
 | 
						|
      }
 | 
						|
      else if (con->type == CONSTRAINT_TYPE_DISTLIMIT) {
 | 
						|
        bDistLimitConstraint *data = 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 = 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 (Bend) */
 | 
						|
 | 
						|
/** \name Transform Bend
 | 
						|
 * \{ */
 | 
						|
 | 
						|
struct BendCustomData {
 | 
						|
  /* All values are in global space. */
 | 
						|
  float warp_sta[3];
 | 
						|
  float warp_end[3];
 | 
						|
 | 
						|
  float warp_nor[3];
 | 
						|
  float warp_tan[3];
 | 
						|
 | 
						|
  /* for applying the mouse distance */
 | 
						|
  float warp_init_dist;
 | 
						|
};
 | 
						|
 | 
						|
static void initBend(TransInfo *t)
 | 
						|
{
 | 
						|
  const float mval_fl[2] = {UNPACK2(t->mval)};
 | 
						|
  const float *curs;
 | 
						|
  float tvec[3];
 | 
						|
  struct BendCustomData *data;
 | 
						|
 | 
						|
  t->mode = TFM_BEND;
 | 
						|
  t->transform = Bend;
 | 
						|
  t->handleEvent = handleEventBend;
 | 
						|
 | 
						|
  setInputPostFct(&t->mouse, postInputRotation);
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_ANGLE_SPRING);
 | 
						|
 | 
						|
  t->idx_max = 1;
 | 
						|
  t->num.idx_max = 1;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = SNAP_INCREMENTAL_ANGLE;
 | 
						|
  t->snap[2] = t->snap[1] * 0.2;
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_use_radians = (t->scene->unit.system_rotation == USER_UNIT_ROT_RADIANS);
 | 
						|
  t->num.unit_type[0] = B_UNIT_ROTATION;
 | 
						|
  t->num.unit_type[1] = B_UNIT_LENGTH;
 | 
						|
 | 
						|
  t->flag |= T_NO_CONSTRAINT;
 | 
						|
 | 
						|
  // copy_v3_v3(t->center, ED_view3d_cursor3d_get(t->scene, t->view));
 | 
						|
  if ((t->flag & T_OVERRIDE_CENTER) == 0) {
 | 
						|
    calculateCenterCursor(t, t->center_global);
 | 
						|
  }
 | 
						|
  calculateCenterLocal(t, t->center_global);
 | 
						|
 | 
						|
  t->val = 0.0f;
 | 
						|
 | 
						|
  data = MEM_callocN(sizeof(*data), __func__);
 | 
						|
 | 
						|
  curs = t->scene->cursor.location;
 | 
						|
  copy_v3_v3(data->warp_sta, curs);
 | 
						|
  ED_view3d_win_to_3d(t->sa->spacedata.first, t->ar, curs, mval_fl, data->warp_end);
 | 
						|
 | 
						|
  copy_v3_v3(data->warp_nor, t->viewinv[2]);
 | 
						|
  normalize_v3(data->warp_nor);
 | 
						|
 | 
						|
  /* tangent */
 | 
						|
  sub_v3_v3v3(tvec, data->warp_end, data->warp_sta);
 | 
						|
  cross_v3_v3v3(data->warp_tan, tvec, data->warp_nor);
 | 
						|
  normalize_v3(data->warp_tan);
 | 
						|
 | 
						|
  data->warp_init_dist = len_v3v3(data->warp_end, data->warp_sta);
 | 
						|
 | 
						|
  t->custom.mode.data = data;
 | 
						|
  t->custom.mode.use_free = true;
 | 
						|
}
 | 
						|
 | 
						|
static eRedrawFlag handleEventBend(TransInfo *UNUSED(t), const wmEvent *event)
 | 
						|
{
 | 
						|
  eRedrawFlag status = TREDRAW_NOTHING;
 | 
						|
 | 
						|
  if (event->type == MIDDLEMOUSE && event->val == KM_PRESS) {
 | 
						|
    status = TREDRAW_HARD;
 | 
						|
  }
 | 
						|
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
static void Bend(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  float vec[3];
 | 
						|
  float pivot_global[3];
 | 
						|
  float warp_end_radius_global[3];
 | 
						|
  int i;
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
  const struct BendCustomData *data = t->custom.mode.data;
 | 
						|
  const bool is_clamp = (t->flag & T_ALT_TRANSFORM) == 0;
 | 
						|
 | 
						|
  union {
 | 
						|
    struct {
 | 
						|
      float angle, scale;
 | 
						|
    };
 | 
						|
    float vector[2];
 | 
						|
  } values;
 | 
						|
 | 
						|
  /* amount of radians for bend */
 | 
						|
  copy_v2_v2(values.vector, t->values);
 | 
						|
 | 
						|
#if 0
 | 
						|
  snapGrid(t, angle_rad);
 | 
						|
#else
 | 
						|
  /* hrmf, snapping radius is using 'angle' steps, need to convert to something else
 | 
						|
   * this isnt essential but nicer to give reasonable snapping values for radius */
 | 
						|
  if (t->tsnap.mode & SCE_SNAP_MODE_INCREMENT) {
 | 
						|
    const float radius_snap = 0.1f;
 | 
						|
    const float snap_hack = (t->snap[1] * data->warp_init_dist) / radius_snap;
 | 
						|
    values.scale *= snap_hack;
 | 
						|
    snapGridIncrement(t, values.vector);
 | 
						|
    values.scale /= snap_hack;
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  if (applyNumInput(&t->num, values.vector)) {
 | 
						|
    values.scale = values.scale / data->warp_init_dist;
 | 
						|
  }
 | 
						|
 | 
						|
  copy_v2_v2(t->values, values.vector);
 | 
						|
 | 
						|
  /* header print for NumInput */
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    char c[NUM_STR_REP_LEN * 2];
 | 
						|
 | 
						|
    outputNumInput(&(t->num), c, &t->scene->unit);
 | 
						|
 | 
						|
    BLI_snprintf(str,
 | 
						|
                 sizeof(str),
 | 
						|
                 IFACE_("Bend Angle: %s Radius: %s Alt, Clamp %s"),
 | 
						|
                 &c[0],
 | 
						|
                 &c[NUM_STR_REP_LEN],
 | 
						|
                 WM_bool_as_string(is_clamp));
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    /* default header print */
 | 
						|
    BLI_snprintf(str,
 | 
						|
                 sizeof(str),
 | 
						|
                 IFACE_("Bend Angle: %.3f Radius: %.4f, Alt, Clamp %s"),
 | 
						|
                 RAD2DEGF(values.angle),
 | 
						|
                 values.scale * data->warp_init_dist,
 | 
						|
                 WM_bool_as_string(is_clamp));
 | 
						|
  }
 | 
						|
 | 
						|
  values.angle *= -1.0f;
 | 
						|
  values.scale *= data->warp_init_dist;
 | 
						|
 | 
						|
  /* calc 'data->warp_end' from 'data->warp_end_init' */
 | 
						|
  copy_v3_v3(warp_end_radius_global, data->warp_end);
 | 
						|
  dist_ensure_v3_v3fl(warp_end_radius_global, data->warp_sta, values.scale);
 | 
						|
  /* done */
 | 
						|
 | 
						|
  /* calculate pivot */
 | 
						|
  copy_v3_v3(pivot_global, data->warp_sta);
 | 
						|
  if (values.angle > 0.0f) {
 | 
						|
    madd_v3_v3fl(pivot_global,
 | 
						|
                 data->warp_tan,
 | 
						|
                 -values.scale * shell_angle_to_dist((float)M_PI_2 - values.angle));
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    madd_v3_v3fl(pivot_global,
 | 
						|
                 data->warp_tan,
 | 
						|
                 +values.scale * shell_angle_to_dist((float)M_PI_2 + values.angle));
 | 
						|
  }
 | 
						|
 | 
						|
  /* TODO(campbell): xform, compensate object center. */
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
 | 
						|
    float warp_sta_local[3];
 | 
						|
    float warp_end_local[3];
 | 
						|
    float warp_end_radius_local[3];
 | 
						|
    float pivot_local[3];
 | 
						|
 | 
						|
    if (tc->use_local_mat) {
 | 
						|
      sub_v3_v3v3(warp_sta_local, data->warp_sta, tc->mat[3]);
 | 
						|
      sub_v3_v3v3(warp_end_local, data->warp_end, tc->mat[3]);
 | 
						|
      sub_v3_v3v3(warp_end_radius_local, warp_end_radius_global, tc->mat[3]);
 | 
						|
      sub_v3_v3v3(pivot_local, pivot_global, tc->mat[3]);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      copy_v3_v3(warp_sta_local, data->warp_sta);
 | 
						|
      copy_v3_v3(warp_end_local, data->warp_end);
 | 
						|
      copy_v3_v3(warp_end_radius_local, warp_end_radius_global);
 | 
						|
      copy_v3_v3(pivot_local, pivot_global);
 | 
						|
    }
 | 
						|
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      float mat[3][3];
 | 
						|
      float delta[3];
 | 
						|
      float fac, fac_scaled;
 | 
						|
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      if (UNLIKELY(values.angle == 0.0f)) {
 | 
						|
        copy_v3_v3(td->loc, td->iloc);
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      copy_v3_v3(vec, td->iloc);
 | 
						|
      mul_m3_v3(td->mtx, vec);
 | 
						|
 | 
						|
      fac = line_point_factor_v3(vec, warp_sta_local, warp_end_radius_local);
 | 
						|
      if (is_clamp) {
 | 
						|
        CLAMP(fac, 0.0f, 1.0f);
 | 
						|
      }
 | 
						|
 | 
						|
      if (t->options & CTX_GPENCIL_STROKES) {
 | 
						|
        /* grease pencil multiframe falloff */
 | 
						|
        bGPDstroke *gps = (bGPDstroke *)td->extra;
 | 
						|
        if (gps != NULL) {
 | 
						|
          fac_scaled = fac * td->factor * gps->runtime.multi_frame_falloff;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          fac_scaled = fac * td->factor;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        fac_scaled = fac * td->factor;
 | 
						|
      }
 | 
						|
 | 
						|
      axis_angle_normalized_to_mat3(mat, data->warp_nor, values.angle * fac_scaled);
 | 
						|
      interp_v3_v3v3(delta, warp_sta_local, warp_end_radius_local, fac_scaled);
 | 
						|
      sub_v3_v3(delta, warp_sta_local);
 | 
						|
 | 
						|
      /* delta is subtracted, rotation adds back this offset */
 | 
						|
      sub_v3_v3(vec, delta);
 | 
						|
 | 
						|
      sub_v3_v3(vec, pivot_local);
 | 
						|
      mul_m3_v3(mat, vec);
 | 
						|
      add_v3_v3(vec, pivot_local);
 | 
						|
 | 
						|
      mul_m3_v3(td->smtx, vec);
 | 
						|
 | 
						|
      /* rotation */
 | 
						|
      if ((t->flag & T_POINTS) == 0) {
 | 
						|
        ElementRotation(t, tc, td, mat, V3D_AROUND_LOCAL_ORIGINS);
 | 
						|
      }
 | 
						|
 | 
						|
      /* location */
 | 
						|
      copy_v3_v3(td->loc, vec);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Shear) */
 | 
						|
 | 
						|
/** \name Transform Shear
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initShear_mouseInputMode(TransInfo *t)
 | 
						|
{
 | 
						|
  float dir[3];
 | 
						|
 | 
						|
  if (t->custom.mode.data == NULL) {
 | 
						|
    copy_v3_v3(dir, t->orient_matrix[t->orient_axis_ortho]);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    cross_v3_v3v3(dir, t->orient_matrix[t->orient_axis_ortho], t->orient_matrix[t->orient_axis]);
 | 
						|
  }
 | 
						|
 | 
						|
  /* Without this, half the gizmo handles move in the opposite direction. */
 | 
						|
  if ((t->orient_axis_ortho + 1) % 3 != t->orient_axis) {
 | 
						|
    negate_v3(dir);
 | 
						|
  }
 | 
						|
 | 
						|
  mul_mat3_m4_v3(t->viewmat, dir);
 | 
						|
  if (normalize_v2(dir) == 0.0f) {
 | 
						|
    dir[0] = 1.0f;
 | 
						|
  }
 | 
						|
  setCustomPointsFromDirection(t, &t->mouse, dir);
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_CUSTOM_RATIO);
 | 
						|
}
 | 
						|
 | 
						|
static void initShear(TransInfo *t)
 | 
						|
{
 | 
						|
  t->mode = TFM_SHEAR;
 | 
						|
  t->transform = applyShear;
 | 
						|
  t->handleEvent = handleEventShear;
 | 
						|
 | 
						|
  if (t->orient_axis == t->orient_axis_ortho) {
 | 
						|
    t->orient_axis = 2;
 | 
						|
    t->orient_axis_ortho = 1;
 | 
						|
  }
 | 
						|
 | 
						|
  initShear_mouseInputMode(t);
 | 
						|
 | 
						|
  t->idx_max = 0;
 | 
						|
  t->num.idx_max = 0;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = 0.1f;
 | 
						|
  t->snap[2] = t->snap[1] * 0.1f;
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_type[0] = B_UNIT_NONE; /* Don't think we have any unit here? */
 | 
						|
 | 
						|
  t->flag |= T_NO_CONSTRAINT;
 | 
						|
}
 | 
						|
 | 
						|
static eRedrawFlag handleEventShear(TransInfo *t, const wmEvent *event)
 | 
						|
{
 | 
						|
  eRedrawFlag status = TREDRAW_NOTHING;
 | 
						|
 | 
						|
  if (event->type == MIDDLEMOUSE && event->val == KM_PRESS) {
 | 
						|
    /* Use custom.mode.data pointer to signal Shear direction */
 | 
						|
    if (t->custom.mode.data == NULL) {
 | 
						|
      t->custom.mode.data = (void *)1;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      t->custom.mode.data = NULL;
 | 
						|
    }
 | 
						|
    initShear_mouseInputMode(t);
 | 
						|
 | 
						|
    status = TREDRAW_HARD;
 | 
						|
  }
 | 
						|
  else if (event->type == XKEY && event->val == KM_PRESS) {
 | 
						|
    t->custom.mode.data = NULL;
 | 
						|
    initShear_mouseInputMode(t);
 | 
						|
 | 
						|
    status = TREDRAW_HARD;
 | 
						|
  }
 | 
						|
  else if (event->type == YKEY && event->val == KM_PRESS) {
 | 
						|
    t->custom.mode.data = (void *)1;
 | 
						|
    initShear_mouseInputMode(t);
 | 
						|
 | 
						|
    status = TREDRAW_HARD;
 | 
						|
  }
 | 
						|
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
static void applyShear(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  float vec[3];
 | 
						|
  float smat[3][3], tmat[3][3], totmat[3][3], axismat[3][3], axismat_inv[3][3];
 | 
						|
  float value;
 | 
						|
  int i;
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
  const bool is_local_center = transdata_check_local_center(t, t->around);
 | 
						|
 | 
						|
  value = t->values[0];
 | 
						|
 | 
						|
  snapGridIncrement(t, &value);
 | 
						|
 | 
						|
  applyNumInput(&t->num, &value);
 | 
						|
 | 
						|
  t->values[0] = value;
 | 
						|
 | 
						|
  /* header print for NumInput */
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    char c[NUM_STR_REP_LEN];
 | 
						|
 | 
						|
    outputNumInput(&(t->num), c, &t->scene->unit);
 | 
						|
 | 
						|
    BLI_snprintf(str, sizeof(str), IFACE_("Shear: %s %s"), c, t->proptext);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    /* default header print */
 | 
						|
    BLI_snprintf(str,
 | 
						|
                 sizeof(str),
 | 
						|
                 IFACE_("Shear: %.3f %s (Press X or Y to set shear axis)"),
 | 
						|
                 value,
 | 
						|
                 t->proptext);
 | 
						|
  }
 | 
						|
 | 
						|
  unit_m3(smat);
 | 
						|
 | 
						|
  // Custom data signals shear direction
 | 
						|
  if (t->custom.mode.data == NULL) {
 | 
						|
    smat[1][0] = value;
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    smat[0][1] = value;
 | 
						|
  }
 | 
						|
 | 
						|
  copy_v3_v3(axismat_inv[0], t->orient_matrix[t->orient_axis_ortho]);
 | 
						|
  copy_v3_v3(axismat_inv[2], t->orient_matrix[t->orient_axis]);
 | 
						|
  cross_v3_v3v3(axismat_inv[1], axismat_inv[0], axismat_inv[2]);
 | 
						|
  invert_m3_m3(axismat, axismat_inv);
 | 
						|
 | 
						|
  mul_m3_series(totmat, axismat_inv, smat, axismat);
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      const float *center, *co;
 | 
						|
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      if (t->flag & T_EDIT) {
 | 
						|
        mul_m3_series(tmat, td->smtx, totmat, td->mtx);
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        copy_m3_m3(tmat, totmat);
 | 
						|
      }
 | 
						|
 | 
						|
      if (is_local_center) {
 | 
						|
        center = td->center;
 | 
						|
        co = td->loc;
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        center = tc->center_local;
 | 
						|
        co = td->center;
 | 
						|
      }
 | 
						|
 | 
						|
      sub_v3_v3v3(vec, co, center);
 | 
						|
 | 
						|
      mul_m3_v3(tmat, vec);
 | 
						|
 | 
						|
      add_v3_v3(vec, center);
 | 
						|
      sub_v3_v3(vec, co);
 | 
						|
 | 
						|
      if (t->options & CTX_GPENCIL_STROKES) {
 | 
						|
        /* grease pencil multiframe falloff */
 | 
						|
        bGPDstroke *gps = (bGPDstroke *)td->extra;
 | 
						|
        if (gps != NULL) {
 | 
						|
          mul_v3_fl(vec, td->factor * gps->runtime.multi_frame_falloff);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          mul_v3_fl(vec, td->factor);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        mul_v3_fl(vec, td->factor);
 | 
						|
      }
 | 
						|
 | 
						|
      add_v3_v3v3(td->loc, td->iloc, vec);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Resize) */
 | 
						|
 | 
						|
/** \name Transform Resize
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initResize(TransInfo *t)
 | 
						|
{
 | 
						|
  t->mode = TFM_RESIZE;
 | 
						|
  t->transform = applyResize;
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_SPRING_FLIP);
 | 
						|
 | 
						|
  t->flag |= T_NULL_ONE;
 | 
						|
  t->num.val_flag[0] |= NUM_NULL_ONE;
 | 
						|
  t->num.val_flag[1] |= NUM_NULL_ONE;
 | 
						|
  t->num.val_flag[2] |= NUM_NULL_ONE;
 | 
						|
  t->num.flag |= NUM_AFFECT_ALL;
 | 
						|
  if ((t->flag & T_EDIT) == 0) {
 | 
						|
    t->flag |= T_NO_ZERO;
 | 
						|
#ifdef USE_NUM_NO_ZERO
 | 
						|
    t->num.val_flag[0] |= NUM_NO_ZERO;
 | 
						|
    t->num.val_flag[1] |= NUM_NO_ZERO;
 | 
						|
    t->num.val_flag[2] |= NUM_NO_ZERO;
 | 
						|
#endif
 | 
						|
  }
 | 
						|
 | 
						|
  t->idx_max = 2;
 | 
						|
  t->num.idx_max = 2;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = 0.1f;
 | 
						|
  t->snap[2] = t->snap[1] * 0.1f;
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_type[0] = B_UNIT_NONE;
 | 
						|
  t->num.unit_type[1] = B_UNIT_NONE;
 | 
						|
  t->num.unit_type[2] = B_UNIT_NONE;
 | 
						|
}
 | 
						|
 | 
						|
static 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,
 | 
						|
                            IFACE_("Scale: %s%s %s"),
 | 
						|
                            &tvec[0],
 | 
						|
                            t->con.text,
 | 
						|
                            t->proptext);
 | 
						|
        break;
 | 
						|
      case 1:
 | 
						|
        ofs += BLI_snprintf(str + ofs,
 | 
						|
                            UI_MAX_DRAW_STR - ofs,
 | 
						|
                            IFACE_("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,
 | 
						|
                            IFACE_("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,
 | 
						|
                          IFACE_("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,
 | 
						|
                          IFACE_("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, IFACE_(" 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];
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static 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->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);
 | 
						|
      applyNumInput(&t->num, t->values);
 | 
						|
 | 
						|
      float ratio = t->values[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);
 | 
						|
}
 | 
						|
 | 
						|
static void applyResize(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  float mat[3][3];
 | 
						|
  int i;
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  if (t->flag & T_AUTOVALUES) {
 | 
						|
    copy_v3_v3(t->values, t->auto_values);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    float ratio = t->values[0];
 | 
						|
 | 
						|
    copy_v3_fl(t->values, ratio);
 | 
						|
 | 
						|
    snapGridIncrement(t, t->values);
 | 
						|
 | 
						|
    if (applyNumInput(&t->num, t->values)) {
 | 
						|
      constraintNumInput(t, t->values);
 | 
						|
    }
 | 
						|
 | 
						|
    applySnapping(t, t->values);
 | 
						|
  }
 | 
						|
 | 
						|
  size_to_mat3(mat, t->values);
 | 
						|
  if (t->con.mode & CON_APPLY) {
 | 
						|
    t->con.applySize(t, NULL, NULL, mat);
 | 
						|
 | 
						|
    /* Only so we have re-usable value with redo. */
 | 
						|
    float pvec[3] = {0.0f, 0.0f, 0.0f};
 | 
						|
    int j = 0;
 | 
						|
    for (i = 0; i < 3; i++) {
 | 
						|
      if (!(t->con.mode & (CON_AXIS0 << i))) {
 | 
						|
        t->values[i] = 1.0f;
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        pvec[j++] = t->values[i];
 | 
						|
      }
 | 
						|
    }
 | 
						|
    headerResize(t, pvec, str);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    headerResize(t, t->values, str);
 | 
						|
  }
 | 
						|
 | 
						|
  copy_m3_m3(t->mat, mat);  // used in gizmo
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      ElementResize(t, tc, td, mat);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* evil hack - redo resize if cliping needed */
 | 
						|
  if (t->flag & T_CLIP_UV && clipUVTransform(t, t->values, 1)) {
 | 
						|
    size_to_mat3(mat, t->values);
 | 
						|
 | 
						|
    if (t->con.mode & CON_APPLY) {
 | 
						|
      t->con.applySize(t, NULL, NULL, mat);
 | 
						|
    }
 | 
						|
 | 
						|
    FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
      TransData *td = tc->data;
 | 
						|
      for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
        ElementResize(t, tc, td, mat);
 | 
						|
      }
 | 
						|
 | 
						|
      /* In proportional edit it can happen that */
 | 
						|
      /* vertices in the radius of the brush end */
 | 
						|
      /* outside the clipping area               */
 | 
						|
      /* XXX HACK - dg */
 | 
						|
      if (t->flag & T_PROP_EDIT_ALL) {
 | 
						|
        clipUVData(t);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Skin) */
 | 
						|
 | 
						|
/** \name Transform Skin
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initSkinResize(TransInfo *t)
 | 
						|
{
 | 
						|
  t->mode = TFM_SKIN_RESIZE;
 | 
						|
  t->transform = applySkinResize;
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_SPRING_FLIP);
 | 
						|
 | 
						|
  t->flag |= T_NULL_ONE;
 | 
						|
  t->num.val_flag[0] |= NUM_NULL_ONE;
 | 
						|
  t->num.val_flag[1] |= NUM_NULL_ONE;
 | 
						|
  t->num.val_flag[2] |= NUM_NULL_ONE;
 | 
						|
  t->num.flag |= NUM_AFFECT_ALL;
 | 
						|
  if ((t->flag & T_EDIT) == 0) {
 | 
						|
    t->flag |= T_NO_ZERO;
 | 
						|
#ifdef USE_NUM_NO_ZERO
 | 
						|
    t->num.val_flag[0] |= NUM_NO_ZERO;
 | 
						|
    t->num.val_flag[1] |= NUM_NO_ZERO;
 | 
						|
    t->num.val_flag[2] |= NUM_NO_ZERO;
 | 
						|
#endif
 | 
						|
  }
 | 
						|
 | 
						|
  t->idx_max = 2;
 | 
						|
  t->num.idx_max = 2;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = 0.1f;
 | 
						|
  t->snap[2] = t->snap[1] * 0.1f;
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_type[0] = B_UNIT_NONE;
 | 
						|
  t->num.unit_type[1] = B_UNIT_NONE;
 | 
						|
  t->num.unit_type[2] = B_UNIT_NONE;
 | 
						|
}
 | 
						|
 | 
						|
static void applySkinResize(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  float size[3], mat[3][3];
 | 
						|
  int i;
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  copy_v3_fl(size, t->values[0]);
 | 
						|
 | 
						|
  snapGridIncrement(t, size);
 | 
						|
 | 
						|
  if (applyNumInput(&t->num, size)) {
 | 
						|
    constraintNumInput(t, size);
 | 
						|
  }
 | 
						|
 | 
						|
  applySnapping(t, size);
 | 
						|
 | 
						|
  if (t->flag & T_AUTOVALUES) {
 | 
						|
    copy_v3_v3(size, t->auto_values);
 | 
						|
  }
 | 
						|
 | 
						|
  copy_v3_v3(t->values, size);
 | 
						|
 | 
						|
  size_to_mat3(mat, size);
 | 
						|
 | 
						|
  headerResize(t, size, str);
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      float tmat[3][3], smat[3][3];
 | 
						|
      float fsize[3];
 | 
						|
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      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, NULL, NULL, tmat);
 | 
						|
      }
 | 
						|
 | 
						|
      mat3_to_size(fsize, tmat);
 | 
						|
      td->val[0] = td->ext->isize[0] * (1 + (fsize[0] - 1) * td->factor);
 | 
						|
      td->val[1] = td->ext->isize[1] * (1 + (fsize[1] - 1) * td->factor);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (ToSphere) */
 | 
						|
 | 
						|
/** \name Transform ToSphere
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initToSphere(TransInfo *t)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
 | 
						|
  t->mode = TFM_TOSPHERE;
 | 
						|
  t->transform = applyToSphere;
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_HORIZONTAL_RATIO);
 | 
						|
 | 
						|
  t->idx_max = 0;
 | 
						|
  t->num.idx_max = 0;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = 0.1f;
 | 
						|
  t->snap[2] = t->snap[1] * 0.1f;
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_type[0] = B_UNIT_NONE;
 | 
						|
 | 
						|
  t->num.val_flag[0] |= NUM_NULL_ONE | NUM_NO_NEGATIVE;
 | 
						|
  t->flag |= T_NO_CONSTRAINT;
 | 
						|
 | 
						|
  // Calculate average radius
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      t->val += len_v3v3(tc->center_local, td->iloc);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  t->val /= (float)t->data_len_all;
 | 
						|
}
 | 
						|
 | 
						|
static void applyToSphere(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  float vec[3];
 | 
						|
  float ratio, radius;
 | 
						|
  int i;
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  ratio = t->values[0];
 | 
						|
 | 
						|
  snapGridIncrement(t, &ratio);
 | 
						|
 | 
						|
  applyNumInput(&t->num, &ratio);
 | 
						|
 | 
						|
  CLAMP(ratio, 0.0f, 1.0f);
 | 
						|
 | 
						|
  t->values[0] = ratio;
 | 
						|
 | 
						|
  /* header print for NumInput */
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    char c[NUM_STR_REP_LEN];
 | 
						|
 | 
						|
    outputNumInput(&(t->num), c, &t->scene->unit);
 | 
						|
 | 
						|
    BLI_snprintf(str, sizeof(str), IFACE_("To Sphere: %s %s"), c, t->proptext);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    /* default header print */
 | 
						|
    BLI_snprintf(str, sizeof(str), IFACE_("To Sphere: %.4f %s"), ratio, t->proptext);
 | 
						|
  }
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      float tratio;
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      sub_v3_v3v3(vec, td->iloc, tc->center_local);
 | 
						|
 | 
						|
      radius = normalize_v3(vec);
 | 
						|
 | 
						|
      tratio = ratio * td->factor;
 | 
						|
 | 
						|
      mul_v3_fl(vec, radius * (1.0f - tratio) + t->val * tratio);
 | 
						|
 | 
						|
      add_v3_v3v3(td->loc, tc->center_local, vec);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Rotation) */
 | 
						|
 | 
						|
/** \name Transform Rotation
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static 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);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void initRotation(TransInfo *t)
 | 
						|
{
 | 
						|
  t->mode = TFM_ROTATION;
 | 
						|
  t->transform = applyRotation;
 | 
						|
 | 
						|
  setInputPostFct(&t->mouse, postInputRotation);
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_ANGLE);
 | 
						|
 | 
						|
  t->idx_max = 0;
 | 
						|
  t->num.idx_max = 0;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = DEG2RAD(5.0);
 | 
						|
  t->snap[2] = DEG2RAD(1.0);
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[2]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_use_radians = (t->scene->unit.system_rotation == USER_UNIT_ROT_RADIANS);
 | 
						|
  t->num.unit_type[0] = B_UNIT_ROTATION;
 | 
						|
 | 
						|
  if (t->flag & T_2D_EDIT) {
 | 
						|
    t->flag |= T_NO_CONSTRAINT;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* Used by Transform Rotation and Transform Normal Rotation */
 | 
						|
static 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,
 | 
						|
                        IFACE_("Rot: %s %s %s"),
 | 
						|
                        &c[0],
 | 
						|
                        t->con.text,
 | 
						|
                        t->proptext);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    ofs += BLI_snprintf(str + ofs,
 | 
						|
                        UI_MAX_DRAW_STR - ofs,
 | 
						|
                        IFACE_("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, IFACE_(" Proportional size: %.2f"), t->prop_size);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * 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.
 | 
						|
 */
 | 
						|
static void ElementRotation_ex(
 | 
						|
    TransInfo *t, TransDataContainer *tc, TransData *td, 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);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static 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);
 | 
						|
}
 | 
						|
 | 
						|
static void applyRotationValue(TransInfo *t, float angle, float axis[3])
 | 
						|
{
 | 
						|
  float mat[3][3];
 | 
						|
  int i;
 | 
						|
 | 
						|
  axis_angle_normalized_to_mat3(mat, axis, angle);
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      if (t->con.applyRot) {
 | 
						|
        t->con.applyRot(t, tc, td, axis, NULL);
 | 
						|
        axis_angle_normalized_to_mat3(mat, axis, angle * td->factor);
 | 
						|
      }
 | 
						|
      else if (t->flag & T_PROP_EDIT) {
 | 
						|
        axis_angle_normalized_to_mat3(mat, axis, angle * td->factor);
 | 
						|
      }
 | 
						|
 | 
						|
      ElementRotation(t, tc, td, mat, t->around);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void applyRotation(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  float final;
 | 
						|
 | 
						|
  final = t->values[0];
 | 
						|
 | 
						|
  snapGridIncrement(t, &final);
 | 
						|
 | 
						|
  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, NULL);
 | 
						|
  }
 | 
						|
 | 
						|
  applySnapping(t, &final);
 | 
						|
 | 
						|
  /* Used to clamp final result in [-PI, PI[ range, no idea why,
 | 
						|
   * inheritance from 2.4x area, see T48998. */
 | 
						|
  applyNumInput(&t->num, &final);
 | 
						|
 | 
						|
  t->values[0] = final;
 | 
						|
 | 
						|
  headerRotation(t, str, final);
 | 
						|
 | 
						|
  applyRotationValue(t, final, axis_final);
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Rotation - Trackball) */
 | 
						|
 | 
						|
/** \name Transform Rotation - Trackball
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initTrackball(TransInfo *t)
 | 
						|
{
 | 
						|
  t->mode = TFM_TRACKBALL;
 | 
						|
  t->transform = applyTrackball;
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_TRACKBALL);
 | 
						|
 | 
						|
  t->idx_max = 1;
 | 
						|
  t->num.idx_max = 1;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = DEG2RAD(5.0);
 | 
						|
  t->snap[2] = DEG2RAD(1.0);
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[2]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_use_radians = (t->scene->unit.system_rotation == USER_UNIT_ROT_RADIANS);
 | 
						|
  t->num.unit_type[0] = B_UNIT_ROTATION;
 | 
						|
  t->num.unit_type[1] = B_UNIT_ROTATION;
 | 
						|
 | 
						|
  t->flag |= T_NO_CONSTRAINT;
 | 
						|
}
 | 
						|
 | 
						|
static void applyTrackballValue(TransInfo *t,
 | 
						|
                                const float axis1[3],
 | 
						|
                                const float axis2[3],
 | 
						|
                                float angles[2])
 | 
						|
{
 | 
						|
  float mat[3][3];
 | 
						|
  float axis[3];
 | 
						|
  float angle;
 | 
						|
  int i;
 | 
						|
 | 
						|
  mul_v3_v3fl(axis, axis1, angles[0]);
 | 
						|
  madd_v3_v3fl(axis, axis2, angles[1]);
 | 
						|
  angle = normalize_v3(axis);
 | 
						|
  axis_angle_normalized_to_mat3(mat, axis, angle);
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      if (t->flag & T_PROP_EDIT) {
 | 
						|
        axis_angle_normalized_to_mat3(mat, axis, td->factor * angle);
 | 
						|
      }
 | 
						|
 | 
						|
      ElementRotation(t, tc, td, mat, t->around);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void applyTrackball(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
  size_t ofs = 0;
 | 
						|
  float axis1[3], axis2[3];
 | 
						|
#if 0 /* UNUSED */
 | 
						|
  float mat[3][3], totmat[3][3], smat[3][3];
 | 
						|
#endif
 | 
						|
  float phi[2];
 | 
						|
 | 
						|
  copy_v3_v3(axis1, t->persinv[0]);
 | 
						|
  copy_v3_v3(axis2, t->persinv[1]);
 | 
						|
  normalize_v3(axis1);
 | 
						|
  normalize_v3(axis2);
 | 
						|
 | 
						|
  copy_v2_v2(phi, t->values);
 | 
						|
 | 
						|
  snapGridIncrement(t, phi);
 | 
						|
 | 
						|
  applyNumInput(&t->num, phi);
 | 
						|
 | 
						|
  copy_v2_v2(t->values, phi);
 | 
						|
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    char c[NUM_STR_REP_LEN * 2];
 | 
						|
 | 
						|
    outputNumInput(&(t->num), c, &t->scene->unit);
 | 
						|
 | 
						|
    ofs += BLI_snprintf(str + ofs,
 | 
						|
                        sizeof(str) - ofs,
 | 
						|
                        IFACE_("Trackball: %s %s %s"),
 | 
						|
                        &c[0],
 | 
						|
                        &c[NUM_STR_REP_LEN],
 | 
						|
                        t->proptext);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    ofs += BLI_snprintf(str + ofs,
 | 
						|
                        sizeof(str) - ofs,
 | 
						|
                        IFACE_("Trackball: %.2f %.2f %s"),
 | 
						|
                        RAD2DEGF(phi[0]),
 | 
						|
                        RAD2DEGF(phi[1]),
 | 
						|
                        t->proptext);
 | 
						|
  }
 | 
						|
 | 
						|
  if (t->flag & T_PROP_EDIT_ALL) {
 | 
						|
    ofs += BLI_snprintf(
 | 
						|
        str + ofs, sizeof(str) - ofs, IFACE_(" Proportional size: %.2f"), t->prop_size);
 | 
						|
  }
 | 
						|
 | 
						|
#if 0 /* UNUSED */
 | 
						|
  axis_angle_normalized_to_mat3(smat, axis1, phi[0]);
 | 
						|
  axis_angle_normalized_to_mat3(totmat, axis2, phi[1]);
 | 
						|
 | 
						|
  mul_m3_m3m3(mat, smat, totmat);
 | 
						|
 | 
						|
  // TRANSFORM_FIX_ME
 | 
						|
  //copy_m3_m3(t->mat, mat);  // used in gizmo
 | 
						|
#endif
 | 
						|
 | 
						|
  applyTrackballValue(t, axis1, axis2, phi);
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Normal Rotation) */
 | 
						|
 | 
						|
/** \name Transform Normal Rotation
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void storeCustomLNorValue(TransDataContainer *tc, BMesh *bm)
 | 
						|
{
 | 
						|
  BMLoopNorEditDataArray *lnors_ed_arr = BM_loop_normal_editdata_array_init(bm);
 | 
						|
  // BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
 | 
						|
 | 
						|
  tc->custom.mode.data = lnors_ed_arr;
 | 
						|
  tc->custom.mode.free_cb = freeCustomNormalArray;
 | 
						|
}
 | 
						|
 | 
						|
void freeCustomNormalArray(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data)
 | 
						|
{
 | 
						|
  BMLoopNorEditDataArray *lnors_ed_arr = custom_data->data;
 | 
						|
 | 
						|
  if (t->state == TRANS_CANCEL) {
 | 
						|
    BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
 | 
						|
    BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
 | 
						|
    BMesh *bm = em->bm;
 | 
						|
 | 
						|
    /* Restore custom loop normal on cancel */
 | 
						|
    for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
 | 
						|
      BKE_lnor_space_custom_normal_to_data(
 | 
						|
          bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->niloc, lnor_ed->clnors_data);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  BM_loop_normal_editdata_array_free(lnors_ed_arr);
 | 
						|
 | 
						|
  tc->custom.mode.data = NULL;
 | 
						|
  tc->custom.mode.free_cb = NULL;
 | 
						|
}
 | 
						|
 | 
						|
static void initNormalRotation(TransInfo *t)
 | 
						|
{
 | 
						|
  t->mode = TFM_NORMAL_ROTATION;
 | 
						|
  t->transform = applyNormalRotation;
 | 
						|
 | 
						|
  setInputPostFct(&t->mouse, postInputRotation);
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_ANGLE);
 | 
						|
 | 
						|
  t->idx_max = 0;
 | 
						|
  t->num.idx_max = 0;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = DEG2RAD(5.0);
 | 
						|
  t->snap[2] = DEG2RAD(1.0);
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[2]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_use_radians = (t->scene->unit.system_rotation == USER_UNIT_ROT_RADIANS);
 | 
						|
  t->num.unit_type[0] = B_UNIT_ROTATION;
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
 | 
						|
    BMesh *bm = em->bm;
 | 
						|
 | 
						|
    BKE_editmesh_ensure_autosmooth(em);
 | 
						|
    BKE_editmesh_lnorspace_update(em);
 | 
						|
 | 
						|
    storeCustomLNorValue(tc, bm);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/* Works by getting custom normal from clnor_data, transform, then store */
 | 
						|
static void applyNormalRotation(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  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, NULL);
 | 
						|
  }
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
 | 
						|
    BMesh *bm = em->bm;
 | 
						|
 | 
						|
    BMLoopNorEditDataArray *lnors_ed_arr = tc->custom.mode.data;
 | 
						|
    BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
 | 
						|
 | 
						|
    float axis[3];
 | 
						|
    float mat[3][3];
 | 
						|
    float angle = t->values[0];
 | 
						|
    copy_v3_v3(axis, axis_final);
 | 
						|
 | 
						|
    snapGridIncrement(t, &angle);
 | 
						|
 | 
						|
    applySnapping(t, &angle);
 | 
						|
 | 
						|
    applyNumInput(&t->num, &angle);
 | 
						|
 | 
						|
    headerRotation(t, str, angle);
 | 
						|
 | 
						|
    axis_angle_normalized_to_mat3(mat, axis, angle);
 | 
						|
 | 
						|
    for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
 | 
						|
      mul_v3_m3v3(lnor_ed->nloc, mat, lnor_ed->niloc);
 | 
						|
 | 
						|
      BKE_lnor_space_custom_normal_to_data(
 | 
						|
          bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->nloc, lnor_ed->clnors_data);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Translation) */
 | 
						|
 | 
						|
static void initSnapSpatial(TransInfo *t, float r_snap[3])
 | 
						|
{
 | 
						|
  if (t->spacetype == SPACE_VIEW3D) {
 | 
						|
    RegionView3D *rv3d = t->ar->regiondata;
 | 
						|
 | 
						|
    if (rv3d) {
 | 
						|
      View3D *v3d = t->sa->spacedata.first;
 | 
						|
      r_snap[0] = 0.0f;
 | 
						|
      r_snap[1] = ED_view3d_grid_view_scale(t->scene, v3d, rv3d, NULL) * 1.0f;
 | 
						|
      r_snap[2] = r_snap[1] * 0.1f;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_IMAGE) {
 | 
						|
    r_snap[0] = 0.0f;
 | 
						|
    r_snap[1] = 0.0625f;
 | 
						|
    r_snap[2] = 0.03125f;
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_CLIP) {
 | 
						|
    r_snap[0] = 0.0f;
 | 
						|
    r_snap[1] = 0.125f;
 | 
						|
    r_snap[2] = 0.0625f;
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_NODE) {
 | 
						|
    r_snap[0] = 0.0f;
 | 
						|
    r_snap[1] = r_snap[2] = ED_node_grid_size();
 | 
						|
  }
 | 
						|
  else if (t->spacetype == SPACE_GRAPH) {
 | 
						|
    r_snap[0] = 0.0f;
 | 
						|
    r_snap[1] = 1.0;
 | 
						|
    r_snap[2] = 0.1f;
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    r_snap[0] = 0.0f;
 | 
						|
    r_snap[1] = r_snap[2] = 1.0f;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/** \name Transform Translation
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initTranslation(TransInfo *t)
 | 
						|
{
 | 
						|
  if (t->spacetype == SPACE_ACTION) {
 | 
						|
    /* this space uses time translate */
 | 
						|
    BKE_report(t->reports,
 | 
						|
               RPT_ERROR,
 | 
						|
               "Use 'Time_Translate' transform mode instead of 'Translation' mode "
 | 
						|
               "for translating keyframes in Dope Sheet Editor");
 | 
						|
    t->state = TRANS_CANCEL;
 | 
						|
  }
 | 
						|
 | 
						|
  t->mode = TFM_TRANSLATION;
 | 
						|
  t->transform = applyTranslation;
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_VECTOR);
 | 
						|
 | 
						|
  t->idx_max = (t->flag & T_2D_EDIT) ? 1 : 2;
 | 
						|
  t->num.flag = 0;
 | 
						|
  t->num.idx_max = t->idx_max;
 | 
						|
 | 
						|
  copy_v3_v3(t->snap, t->snap_spatial);
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  if (t->spacetype == SPACE_VIEW3D) {
 | 
						|
    /* Handling units makes only sense in 3Dview... See T38877. */
 | 
						|
    t->num.unit_type[0] = B_UNIT_LENGTH;
 | 
						|
    t->num.unit_type[1] = B_UNIT_LENGTH;
 | 
						|
    t->num.unit_type[2] = B_UNIT_LENGTH;
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    /* SPACE_GRAPH, SPACE_ACTION, etc. could use some time units, when we have them... */
 | 
						|
    t->num.unit_type[0] = B_UNIT_NONE;
 | 
						|
    t->num.unit_type[1] = B_UNIT_NONE;
 | 
						|
    t->num.unit_type[2] = B_UNIT_NONE;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_DRAW_STR])
 | 
						|
{
 | 
						|
  size_t ofs = 0;
 | 
						|
  char tvec[NUM_STR_REP_LEN * 3];
 | 
						|
  char distvec[NUM_STR_REP_LEN];
 | 
						|
  char autoik[NUM_STR_REP_LEN];
 | 
						|
  float dist;
 | 
						|
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    outputNumInput(&(t->num), tvec, &t->scene->unit);
 | 
						|
    dist = len_v3(t->num.val);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    float dvec[3];
 | 
						|
 | 
						|
    copy_v3_v3(dvec, vec);
 | 
						|
    applyAspectRatio(t, dvec);
 | 
						|
 | 
						|
    dist = len_v3(vec);
 | 
						|
    if (!(t->flag & T_2D_EDIT) && t->scene->unit.system) {
 | 
						|
      int i;
 | 
						|
 | 
						|
      for (i = 0; i < 3; i++) {
 | 
						|
        bUnit_AsString2(&tvec[NUM_STR_REP_LEN * i],
 | 
						|
                        NUM_STR_REP_LEN,
 | 
						|
                        dvec[i] * t->scene->unit.scale_length,
 | 
						|
                        4,
 | 
						|
                        B_UNIT_LENGTH,
 | 
						|
                        &t->scene->unit,
 | 
						|
                        true);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", dvec[0]);
 | 
						|
      BLI_snprintf(&tvec[NUM_STR_REP_LEN], NUM_STR_REP_LEN, "%.4f", dvec[1]);
 | 
						|
      BLI_snprintf(&tvec[NUM_STR_REP_LEN * 2], NUM_STR_REP_LEN, "%.4f", dvec[2]);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (!(t->flag & T_2D_EDIT) && t->scene->unit.system) {
 | 
						|
    bUnit_AsString2(distvec,
 | 
						|
                    sizeof(distvec),
 | 
						|
                    dist * t->scene->unit.scale_length,
 | 
						|
                    4,
 | 
						|
                    B_UNIT_LENGTH,
 | 
						|
                    &t->scene->unit,
 | 
						|
                    false);
 | 
						|
  }
 | 
						|
  else if (dist > 1e10f || dist < -1e10f) {
 | 
						|
    /* prevent string buffer overflow */
 | 
						|
    BLI_snprintf(distvec, NUM_STR_REP_LEN, "%.4e", dist);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    BLI_snprintf(distvec, NUM_STR_REP_LEN, "%.4f", dist);
 | 
						|
  }
 | 
						|
 | 
						|
  if (t->flag & T_AUTOIK) {
 | 
						|
    short chainlen = t->settings->autoik_chainlen;
 | 
						|
 | 
						|
    if (chainlen) {
 | 
						|
      BLI_snprintf(autoik, NUM_STR_REP_LEN, IFACE_("AutoIK-Len: %d"), chainlen);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      autoik[0] = '\0';
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    autoik[0] = '\0';
 | 
						|
  }
 | 
						|
 | 
						|
  if (t->con.mode & CON_APPLY) {
 | 
						|
    switch (t->num.idx_max) {
 | 
						|
      case 0:
 | 
						|
        ofs += BLI_snprintf(str + ofs,
 | 
						|
                            UI_MAX_DRAW_STR - ofs,
 | 
						|
                            "D: %s (%s)%s %s  %s",
 | 
						|
                            &tvec[0],
 | 
						|
                            distvec,
 | 
						|
                            t->con.text,
 | 
						|
                            t->proptext,
 | 
						|
                            autoik);
 | 
						|
        break;
 | 
						|
      case 1:
 | 
						|
        ofs += BLI_snprintf(str + ofs,
 | 
						|
                            UI_MAX_DRAW_STR - ofs,
 | 
						|
                            "D: %s   D: %s (%s)%s %s  %s",
 | 
						|
                            &tvec[0],
 | 
						|
                            &tvec[NUM_STR_REP_LEN],
 | 
						|
                            distvec,
 | 
						|
                            t->con.text,
 | 
						|
                            t->proptext,
 | 
						|
                            autoik);
 | 
						|
        break;
 | 
						|
      case 2:
 | 
						|
        ofs += BLI_snprintf(str + ofs,
 | 
						|
                            UI_MAX_DRAW_STR - ofs,
 | 
						|
                            "D: %s   D: %s  D: %s (%s)%s %s  %s",
 | 
						|
                            &tvec[0],
 | 
						|
                            &tvec[NUM_STR_REP_LEN],
 | 
						|
                            &tvec[NUM_STR_REP_LEN * 2],
 | 
						|
                            distvec,
 | 
						|
                            t->con.text,
 | 
						|
                            t->proptext,
 | 
						|
                            autoik);
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    if (t->flag & T_2D_EDIT) {
 | 
						|
      ofs += BLI_snprintf(str + ofs,
 | 
						|
                          UI_MAX_DRAW_STR - ofs,
 | 
						|
                          "Dx: %s   Dy: %s (%s)%s %s",
 | 
						|
                          &tvec[0],
 | 
						|
                          &tvec[NUM_STR_REP_LEN],
 | 
						|
                          distvec,
 | 
						|
                          t->con.text,
 | 
						|
                          t->proptext);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      ofs += BLI_snprintf(str + ofs,
 | 
						|
                          UI_MAX_DRAW_STR - ofs,
 | 
						|
                          "Dx: %s   Dy: %s  Dz: %s (%s)%s %s  %s",
 | 
						|
                          &tvec[0],
 | 
						|
                          &tvec[NUM_STR_REP_LEN],
 | 
						|
                          &tvec[NUM_STR_REP_LEN * 2],
 | 
						|
                          distvec,
 | 
						|
                          t->con.text,
 | 
						|
                          t->proptext,
 | 
						|
                          autoik);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (t->flag & T_PROP_EDIT_ALL) {
 | 
						|
    ofs += BLI_snprintf(
 | 
						|
        str + ofs, UI_MAX_DRAW_STR - ofs, IFACE_(" Proportional size: %.2f"), t->prop_size);
 | 
						|
  }
 | 
						|
 | 
						|
  if (t->spacetype == SPACE_NODE) {
 | 
						|
    SpaceNode *snode = (SpaceNode *)t->sa->spacedata.first;
 | 
						|
 | 
						|
    if ((snode->flag & SNODE_SKIP_INSOFFSET) == 0) {
 | 
						|
      const char *str_old = BLI_strdup(str);
 | 
						|
      const char *str_dir = (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_RIGHT) ?
 | 
						|
                                IFACE_("right") :
 | 
						|
                                IFACE_("left");
 | 
						|
      char str_km[64];
 | 
						|
 | 
						|
      WM_modalkeymap_items_to_string(
 | 
						|
          t->keymap, TFM_MODAL_INSERTOFS_TOGGLE_DIR, true, str_km, sizeof(str_km));
 | 
						|
 | 
						|
      ofs += BLI_snprintf(str,
 | 
						|
                          UI_MAX_DRAW_STR,
 | 
						|
                          IFACE_("Auto-offset set to %s - press %s to toggle direction  |  %s"),
 | 
						|
                          str_dir,
 | 
						|
                          str_km,
 | 
						|
                          str_old);
 | 
						|
 | 
						|
      MEM_freeN((void *)str_old);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void applyTranslationValue(TransInfo *t, const float vec[3])
 | 
						|
{
 | 
						|
  const bool apply_snap_align_rotation = usingSnappingNormal(
 | 
						|
      t);  // && (t->tsnap.status & POINT_INIT);
 | 
						|
  float tvec[3];
 | 
						|
 | 
						|
  /* The ideal would be "apply_snap_align_rotation" only when a snap point is found
 | 
						|
   * so, maybe inside this function is not the best place to apply this rotation.
 | 
						|
   * but you need "handle snapping rotation before doing the translation" (really?) */
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
 | 
						|
    float pivot[3];
 | 
						|
    if (apply_snap_align_rotation) {
 | 
						|
      copy_v3_v3(pivot, t->tsnap.snapTarget);
 | 
						|
      /* The pivot has to be in local-space (see T49494) */
 | 
						|
      if (tc->use_local_mat) {
 | 
						|
        mul_m4_v3(tc->imat, pivot);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (int i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      float rotate_offset[3] = {0};
 | 
						|
      bool use_rotate_offset = false;
 | 
						|
 | 
						|
      /* handle snapping rotation before doing the translation */
 | 
						|
      if (apply_snap_align_rotation) {
 | 
						|
        float mat[3][3];
 | 
						|
 | 
						|
        if (validSnappingNormal(t)) {
 | 
						|
          const float *original_normal;
 | 
						|
 | 
						|
          /* In pose mode, we want to align normals with Y axis of bones... */
 | 
						|
          if (t->flag & T_POSE) {
 | 
						|
            original_normal = td->axismtx[1];
 | 
						|
          }
 | 
						|
          else {
 | 
						|
            original_normal = td->axismtx[2];
 | 
						|
          }
 | 
						|
 | 
						|
          rotation_between_vecs_to_mat3(mat, original_normal, t->tsnap.snapNormal);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          unit_m3(mat);
 | 
						|
        }
 | 
						|
 | 
						|
        ElementRotation_ex(t, tc, td, mat, pivot);
 | 
						|
 | 
						|
        if (td->loc) {
 | 
						|
          use_rotate_offset = true;
 | 
						|
          sub_v3_v3v3(rotate_offset, td->loc, td->iloc);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      if (t->con.applyVec) {
 | 
						|
        float pvec[3];
 | 
						|
        t->con.applyVec(t, tc, td, vec, tvec, pvec);
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        copy_v3_v3(tvec, vec);
 | 
						|
      }
 | 
						|
 | 
						|
      if (use_rotate_offset) {
 | 
						|
        add_v3_v3(tvec, rotate_offset);
 | 
						|
      }
 | 
						|
 | 
						|
      mul_m3_v3(td->smtx, tvec);
 | 
						|
 | 
						|
      if (t->options & CTX_GPENCIL_STROKES) {
 | 
						|
        /* grease pencil multiframe falloff */
 | 
						|
        bGPDstroke *gps = (bGPDstroke *)td->extra;
 | 
						|
        if (gps != NULL) {
 | 
						|
          mul_v3_fl(tvec, td->factor * gps->runtime.multi_frame_falloff);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          mul_v3_fl(tvec, td->factor);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        /* proportional editing falloff */
 | 
						|
        mul_v3_fl(tvec, td->factor);
 | 
						|
      }
 | 
						|
 | 
						|
      protectedTransBits(td->protectflag, tvec);
 | 
						|
 | 
						|
      if (td->loc) {
 | 
						|
        add_v3_v3v3(td->loc, td->iloc, tvec);
 | 
						|
      }
 | 
						|
 | 
						|
      constraintTransLim(t, td);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void applyTranslation(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
  float value_final[3];
 | 
						|
 | 
						|
  if (t->flag & T_AUTOVALUES) {
 | 
						|
    copy_v3_v3(t->values, t->auto_values);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    if ((t->con.mode & CON_APPLY) == 0) {
 | 
						|
      snapGridIncrement(t, t->values);
 | 
						|
    }
 | 
						|
 | 
						|
    if (applyNumInput(&t->num, t->values)) {
 | 
						|
      removeAspectRatio(t, t->values);
 | 
						|
    }
 | 
						|
 | 
						|
    applySnapping(t, t->values);
 | 
						|
  }
 | 
						|
 | 
						|
  if (t->con.mode & CON_APPLY) {
 | 
						|
    float pvec[3] = {0.0f, 0.0f, 0.0f};
 | 
						|
    t->con.applyVec(t, NULL, NULL, t->values, value_final, pvec);
 | 
						|
    headerTranslation(t, pvec, str);
 | 
						|
 | 
						|
    /* only so we have re-usable value with redo, see T46741. */
 | 
						|
    mul_v3_m3v3(t->values, t->con.imtx, value_final);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    headerTranslation(t, t->values, str);
 | 
						|
    copy_v3_v3(value_final, t->values);
 | 
						|
  }
 | 
						|
 | 
						|
  /* don't use 't->values' now on */
 | 
						|
 | 
						|
  applyTranslationValue(t, value_final);
 | 
						|
 | 
						|
  /* evil hack - redo translation if clipping needed */
 | 
						|
  if (t->flag & T_CLIP_UV && clipUVTransform(t, value_final, 0)) {
 | 
						|
    applyTranslationValue(t, value_final);
 | 
						|
 | 
						|
    /* In proportional edit it can happen that */
 | 
						|
    /* vertices in the radius of the brush end */
 | 
						|
    /* outside the clipping area               */
 | 
						|
    /* XXX HACK - dg */
 | 
						|
    if (t->flag & T_PROP_EDIT_ALL) {
 | 
						|
      clipUVData(t);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Shrink-Fatten) */
 | 
						|
 | 
						|
/** \name Transform Shrink-Fatten
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initShrinkFatten(TransInfo *t)
 | 
						|
{
 | 
						|
  // If not in mesh edit mode, fallback to Resize
 | 
						|
  if ((t->flag & T_EDIT) == 0 || (t->obedit_type != OB_MESH)) {
 | 
						|
    initResize(t);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    t->mode = TFM_SHRINKFATTEN;
 | 
						|
    t->transform = applyShrinkFatten;
 | 
						|
 | 
						|
    initMouseInputMode(t, &t->mouse, INPUT_VERTICAL_ABSOLUTE);
 | 
						|
 | 
						|
    t->idx_max = 0;
 | 
						|
    t->num.idx_max = 0;
 | 
						|
    t->snap[0] = 0.0f;
 | 
						|
    t->snap[1] = 1.0f;
 | 
						|
    t->snap[2] = t->snap[1] * 0.1f;
 | 
						|
 | 
						|
    copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
    t->num.unit_sys = t->scene->unit.system;
 | 
						|
    t->num.unit_type[0] = B_UNIT_LENGTH;
 | 
						|
 | 
						|
    t->flag |= T_NO_CONSTRAINT;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  float distance;
 | 
						|
  int i;
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
  size_t ofs = 0;
 | 
						|
 | 
						|
  distance = -t->values[0];
 | 
						|
 | 
						|
  snapGridIncrement(t, &distance);
 | 
						|
 | 
						|
  applyNumInput(&t->num, &distance);
 | 
						|
 | 
						|
  t->values[0] = -distance;
 | 
						|
 | 
						|
  /* header print for NumInput */
 | 
						|
  ofs += BLI_strncpy_rlen(str + ofs, IFACE_("Shrink/Fatten:"), sizeof(str) - ofs);
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    char c[NUM_STR_REP_LEN];
 | 
						|
    outputNumInput(&(t->num), c, &t->scene->unit);
 | 
						|
    ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, " %s", c);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    /* default header print */
 | 
						|
    ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, " %.4f", distance);
 | 
						|
  }
 | 
						|
 | 
						|
  if (t->proptext[0]) {
 | 
						|
    ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, " %s", t->proptext);
 | 
						|
  }
 | 
						|
  ofs += BLI_strncpy_rlen(str + ofs, ", (", sizeof(str) - ofs);
 | 
						|
 | 
						|
  if (t->keymap) {
 | 
						|
    wmKeyMapItem *kmi = WM_modalkeymap_find_propvalue(t->keymap, TFM_MODAL_RESIZE);
 | 
						|
    if (kmi) {
 | 
						|
      ofs += WM_keymap_item_to_string(kmi, false, str + ofs, sizeof(str) - ofs);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  BLI_snprintf(str + ofs,
 | 
						|
               sizeof(str) - ofs,
 | 
						|
               IFACE_(" or Alt) Even Thickness %s"),
 | 
						|
               WM_bool_as_string((t->flag & T_ALT_TRANSFORM) != 0));
 | 
						|
  /* done with header string */
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      float tdistance; /* temp dist */
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      /* get the final offset */
 | 
						|
      tdistance = distance * td->factor;
 | 
						|
      if (td->ext && (t->flag & T_ALT_TRANSFORM) != 0) {
 | 
						|
        tdistance *= td->ext->isize[0]; /* shell factor */
 | 
						|
      }
 | 
						|
 | 
						|
      madd_v3_v3v3fl(td->loc, td->iloc, td->axismtx[2], tdistance);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Tilt) */
 | 
						|
 | 
						|
/** \name Transform Tilt
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initTilt(TransInfo *t)
 | 
						|
{
 | 
						|
  t->mode = TFM_TILT;
 | 
						|
  t->transform = applyTilt;
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_ANGLE);
 | 
						|
 | 
						|
  t->idx_max = 0;
 | 
						|
  t->num.idx_max = 0;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = DEG2RAD(5.0);
 | 
						|
  t->snap[2] = DEG2RAD(1.0);
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[2]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_use_radians = (t->scene->unit.system_rotation == USER_UNIT_ROT_RADIANS);
 | 
						|
  t->num.unit_type[0] = B_UNIT_ROTATION;
 | 
						|
 | 
						|
  t->flag |= T_NO_CONSTRAINT | T_NO_PROJECT;
 | 
						|
}
 | 
						|
 | 
						|
static void applyTilt(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  float final;
 | 
						|
 | 
						|
  final = t->values[0];
 | 
						|
 | 
						|
  snapGridIncrement(t, &final);
 | 
						|
 | 
						|
  applyNumInput(&t->num, &final);
 | 
						|
 | 
						|
  t->values[0] = final;
 | 
						|
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    char c[NUM_STR_REP_LEN];
 | 
						|
 | 
						|
    outputNumInput(&(t->num), c, &t->scene->unit);
 | 
						|
 | 
						|
    BLI_snprintf(str, sizeof(str), IFACE_("Tilt: %s° %s"), &c[0], t->proptext);
 | 
						|
 | 
						|
    /* XXX For some reason, this seems needed for this op, else RNA prop is not updated... :/ */
 | 
						|
    t->values[0] = final;
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    BLI_snprintf(str, sizeof(str), IFACE_("Tilt: %.2f° %s"), RAD2DEGF(final), t->proptext);
 | 
						|
  }
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->val) {
 | 
						|
        *td->val = td->ival + final * td->factor;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Curve Shrink/Fatten) */
 | 
						|
 | 
						|
/** \name Transform Curve Shrink/Fatten
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initCurveShrinkFatten(TransInfo *t)
 | 
						|
{
 | 
						|
  t->mode = TFM_CURVE_SHRINKFATTEN;
 | 
						|
  t->transform = applyCurveShrinkFatten;
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_SPRING);
 | 
						|
 | 
						|
  t->idx_max = 0;
 | 
						|
  t->num.idx_max = 0;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = 0.1f;
 | 
						|
  t->snap[2] = t->snap[1] * 0.1f;
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_type[0] = B_UNIT_NONE;
 | 
						|
 | 
						|
  t->flag |= T_NO_ZERO;
 | 
						|
#ifdef USE_NUM_NO_ZERO
 | 
						|
  t->num.val_flag[0] |= NUM_NO_ZERO;
 | 
						|
#endif
 | 
						|
 | 
						|
  t->flag |= T_NO_CONSTRAINT;
 | 
						|
}
 | 
						|
 | 
						|
static void applyCurveShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  float ratio;
 | 
						|
  int i;
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  ratio = t->values[0];
 | 
						|
 | 
						|
  snapGridIncrement(t, &ratio);
 | 
						|
 | 
						|
  applyNumInput(&t->num, &ratio);
 | 
						|
 | 
						|
  t->values[0] = ratio;
 | 
						|
 | 
						|
  /* header print for NumInput */
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    char c[NUM_STR_REP_LEN];
 | 
						|
 | 
						|
    outputNumInput(&(t->num), c, &t->scene->unit);
 | 
						|
    BLI_snprintf(str, sizeof(str), IFACE_("Shrink/Fatten: %s"), c);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    BLI_snprintf(str, sizeof(str), IFACE_("Shrink/Fatten: %3f"), ratio);
 | 
						|
  }
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->val) {
 | 
						|
        *td->val = td->ival * ratio;
 | 
						|
        /* apply PET */
 | 
						|
        *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival);
 | 
						|
        if (*td->val <= 0.0f) {
 | 
						|
          *td->val = 0.001f;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Mask Shrink/Fatten) */
 | 
						|
 | 
						|
/** \name Transform Mask Shrink/Fatten
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initMaskShrinkFatten(TransInfo *t)
 | 
						|
{
 | 
						|
  t->mode = TFM_MASK_SHRINKFATTEN;
 | 
						|
  t->transform = applyMaskShrinkFatten;
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_SPRING);
 | 
						|
 | 
						|
  t->idx_max = 0;
 | 
						|
  t->num.idx_max = 0;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = 0.1f;
 | 
						|
  t->snap[2] = t->snap[1] * 0.1f;
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_type[0] = B_UNIT_NONE;
 | 
						|
 | 
						|
  t->flag |= T_NO_ZERO;
 | 
						|
#ifdef USE_NUM_NO_ZERO
 | 
						|
  t->num.val_flag[0] |= NUM_NO_ZERO;
 | 
						|
#endif
 | 
						|
 | 
						|
  t->flag |= T_NO_CONSTRAINT;
 | 
						|
}
 | 
						|
 | 
						|
static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  float ratio;
 | 
						|
  int i;
 | 
						|
  bool initial_feather = false;
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  ratio = t->values[0];
 | 
						|
 | 
						|
  snapGridIncrement(t, &ratio);
 | 
						|
 | 
						|
  applyNumInput(&t->num, &ratio);
 | 
						|
 | 
						|
  t->values[0] = ratio;
 | 
						|
 | 
						|
  /* header print for NumInput */
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    char c[NUM_STR_REP_LEN];
 | 
						|
 | 
						|
    outputNumInput(&(t->num), c, &t->scene->unit);
 | 
						|
    BLI_snprintf(str, sizeof(str), IFACE_("Feather Shrink/Fatten: %s"), c);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    BLI_snprintf(str, sizeof(str), IFACE_("Feather Shrink/Fatten: %3f"), ratio);
 | 
						|
  }
 | 
						|
 | 
						|
  /* detect if no points have feather yet */
 | 
						|
  if (ratio > 1.0f) {
 | 
						|
    initial_feather = true;
 | 
						|
 | 
						|
    FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
      TransData *td = tc->data;
 | 
						|
      for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
        if (td->flag & TD_NOACTION) {
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        if (td->flag & TD_SKIP) {
 | 
						|
          continue;
 | 
						|
        }
 | 
						|
 | 
						|
        if (td->ival >= 0.001f) {
 | 
						|
          initial_feather = false;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* apply shrink/fatten */
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (td = tc->data, i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->val) {
 | 
						|
        if (initial_feather) {
 | 
						|
          *td->val = td->ival + (ratio - 1.0f) * 0.01f;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          *td->val = td->ival * ratio;
 | 
						|
        }
 | 
						|
 | 
						|
        /* apply PET */
 | 
						|
        *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival);
 | 
						|
        if (*td->val <= 0.0f) {
 | 
						|
          *td->val = 0.001f;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (GPencil Shrink/Fatten) */
 | 
						|
 | 
						|
/** \name Transform GPencil Strokes Shrink/Fatten
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initGPShrinkFatten(TransInfo *t)
 | 
						|
{
 | 
						|
  t->mode = TFM_GPENCIL_SHRINKFATTEN;
 | 
						|
  t->transform = applyGPShrinkFatten;
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_SPRING);
 | 
						|
 | 
						|
  t->idx_max = 0;
 | 
						|
  t->num.idx_max = 0;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = 0.1f;
 | 
						|
  t->snap[2] = t->snap[1] * 0.1f;
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_type[0] = B_UNIT_NONE;
 | 
						|
 | 
						|
  t->flag |= T_NO_ZERO;
 | 
						|
#ifdef USE_NUM_NO_ZERO
 | 
						|
  t->num.val_flag[0] |= NUM_NO_ZERO;
 | 
						|
#endif
 | 
						|
 | 
						|
  t->flag |= T_NO_CONSTRAINT;
 | 
						|
}
 | 
						|
 | 
						|
static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  float ratio;
 | 
						|
  int i;
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  ratio = t->values[0];
 | 
						|
 | 
						|
  snapGridIncrement(t, &ratio);
 | 
						|
 | 
						|
  applyNumInput(&t->num, &ratio);
 | 
						|
 | 
						|
  t->values[0] = ratio;
 | 
						|
 | 
						|
  /* header print for NumInput */
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    char c[NUM_STR_REP_LEN];
 | 
						|
 | 
						|
    outputNumInput(&(t->num), c, &t->scene->unit);
 | 
						|
    BLI_snprintf(str, sizeof(str), IFACE_("Shrink/Fatten: %s"), c);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    BLI_snprintf(str, sizeof(str), IFACE_("Shrink/Fatten: %3f"), ratio);
 | 
						|
  }
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->val) {
 | 
						|
        *td->val = td->ival * ratio;
 | 
						|
        /* apply PET */
 | 
						|
        *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival);
 | 
						|
        if (*td->val <= 0.0f) {
 | 
						|
          *td->val = 0.001f;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (GPencil Opacity) */
 | 
						|
 | 
						|
/** \name Transform GPencil Strokes Opacity
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initGPOpacity(TransInfo *t)
 | 
						|
{
 | 
						|
  t->mode = TFM_GPENCIL_OPACITY;
 | 
						|
  t->transform = applyGPOpacity;
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_SPRING);
 | 
						|
 | 
						|
  t->idx_max = 0;
 | 
						|
  t->num.idx_max = 0;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = 0.1f;
 | 
						|
  t->snap[2] = t->snap[1] * 0.1f;
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_type[0] = B_UNIT_NONE;
 | 
						|
 | 
						|
  t->flag |= T_NO_ZERO;
 | 
						|
#ifdef USE_NUM_NO_ZERO
 | 
						|
  t->num.val_flag[0] |= NUM_NO_ZERO;
 | 
						|
#endif
 | 
						|
 | 
						|
  t->flag |= T_NO_CONSTRAINT;
 | 
						|
}
 | 
						|
 | 
						|
static void applyGPOpacity(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  float ratio;
 | 
						|
  int i;
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  ratio = t->values[0];
 | 
						|
 | 
						|
  snapGridIncrement(t, &ratio);
 | 
						|
 | 
						|
  applyNumInput(&t->num, &ratio);
 | 
						|
 | 
						|
  t->values[0] = ratio;
 | 
						|
 | 
						|
  /* header print for NumInput */
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    char c[NUM_STR_REP_LEN];
 | 
						|
 | 
						|
    outputNumInput(&(t->num), c, &t->scene->unit);
 | 
						|
    BLI_snprintf(str, sizeof(str), IFACE_("Opacity: %s"), c);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    BLI_snprintf(str, sizeof(str), IFACE_("Opacity: %3f"), ratio);
 | 
						|
  }
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->val) {
 | 
						|
        *td->val = td->ival * ratio;
 | 
						|
        /* apply PET */
 | 
						|
        *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival);
 | 
						|
        CLAMP(*td->val, 0.0f, 1.0f);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Push/Pull) */
 | 
						|
 | 
						|
/** \name Transform Push/Pull
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initPushPull(TransInfo *t)
 | 
						|
{
 | 
						|
  t->mode = TFM_PUSHPULL;
 | 
						|
  t->transform = applyPushPull;
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_VERTICAL_ABSOLUTE);
 | 
						|
 | 
						|
  t->idx_max = 0;
 | 
						|
  t->num.idx_max = 0;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = 1.0f;
 | 
						|
  t->snap[2] = t->snap[1] * 0.1f;
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_type[0] = B_UNIT_LENGTH;
 | 
						|
}
 | 
						|
 | 
						|
static void applyPushPull(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  float vec[3], axis_global[3];
 | 
						|
  float distance;
 | 
						|
  int i;
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  distance = t->values[0];
 | 
						|
 | 
						|
  snapGridIncrement(t, &distance);
 | 
						|
 | 
						|
  applyNumInput(&t->num, &distance);
 | 
						|
 | 
						|
  t->values[0] = distance;
 | 
						|
 | 
						|
  /* header print for NumInput */
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    char c[NUM_STR_REP_LEN];
 | 
						|
 | 
						|
    outputNumInput(&(t->num), c, &t->scene->unit);
 | 
						|
 | 
						|
    BLI_snprintf(str, sizeof(str), IFACE_("Push/Pull: %s%s %s"), c, t->con.text, t->proptext);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    /* default header print */
 | 
						|
    BLI_snprintf(
 | 
						|
        str, sizeof(str), IFACE_("Push/Pull: %.4f%s %s"), distance, t->con.text, t->proptext);
 | 
						|
  }
 | 
						|
 | 
						|
  if (t->con.applyRot && t->con.mode & CON_APPLY) {
 | 
						|
    t->con.applyRot(t, NULL, NULL, axis_global, NULL);
 | 
						|
  }
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      sub_v3_v3v3(vec, tc->center_local, td->center);
 | 
						|
      if (t->con.applyRot && t->con.mode & CON_APPLY) {
 | 
						|
        float axis[3];
 | 
						|
        copy_v3_v3(axis, axis_global);
 | 
						|
        t->con.applyRot(t, tc, td, axis, NULL);
 | 
						|
 | 
						|
        mul_m3_v3(td->smtx, axis);
 | 
						|
        if (isLockConstraint(t)) {
 | 
						|
          float dvec[3];
 | 
						|
          project_v3_v3v3(dvec, vec, axis);
 | 
						|
          sub_v3_v3(vec, dvec);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          project_v3_v3v3(vec, vec, axis);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      normalize_v3_length(vec, distance * td->factor);
 | 
						|
 | 
						|
      add_v3_v3v3(td->loc, td->iloc, vec);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Bevel Weight) */
 | 
						|
 | 
						|
/** \name Transform Bevel Weight
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initBevelWeight(TransInfo *t)
 | 
						|
{
 | 
						|
  t->mode = TFM_BWEIGHT;
 | 
						|
  t->transform = applyBevelWeight;
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_SPRING_DELTA);
 | 
						|
 | 
						|
  t->idx_max = 0;
 | 
						|
  t->num.idx_max = 0;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = 0.1f;
 | 
						|
  t->snap[2] = t->snap[1] * 0.1f;
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_type[0] = B_UNIT_NONE;
 | 
						|
 | 
						|
  t->flag |= T_NO_CONSTRAINT | T_NO_PROJECT;
 | 
						|
}
 | 
						|
 | 
						|
static void applyBevelWeight(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  float weight;
 | 
						|
  int i;
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  weight = t->values[0];
 | 
						|
 | 
						|
  CLAMP_MAX(weight, 1.0f);
 | 
						|
 | 
						|
  snapGridIncrement(t, &weight);
 | 
						|
 | 
						|
  applyNumInput(&t->num, &weight);
 | 
						|
 | 
						|
  t->values[0] = weight;
 | 
						|
 | 
						|
  /* header print for NumInput */
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    char c[NUM_STR_REP_LEN];
 | 
						|
 | 
						|
    outputNumInput(&(t->num), c, &t->scene->unit);
 | 
						|
 | 
						|
    if (weight >= 0.0f) {
 | 
						|
      BLI_snprintf(str, sizeof(str), IFACE_("Bevel Weight: +%s %s"), c, t->proptext);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      BLI_snprintf(str, sizeof(str), IFACE_("Bevel Weight: %s %s"), c, t->proptext);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    /* default header print */
 | 
						|
    if (weight >= 0.0f) {
 | 
						|
      BLI_snprintf(str, sizeof(str), IFACE_("Bevel Weight: +%.3f %s"), weight, t->proptext);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      BLI_snprintf(str, sizeof(str), IFACE_("Bevel Weight: %.3f %s"), weight, t->proptext);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->val) {
 | 
						|
        *td->val = td->ival + weight * td->factor;
 | 
						|
        if (*td->val < 0.0f) {
 | 
						|
          *td->val = 0.0f;
 | 
						|
        }
 | 
						|
        if (*td->val > 1.0f) {
 | 
						|
          *td->val = 1.0f;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Crease) */
 | 
						|
 | 
						|
/** \name Transform Crease
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initCrease(TransInfo *t)
 | 
						|
{
 | 
						|
  t->mode = TFM_CREASE;
 | 
						|
  t->transform = applyCrease;
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_SPRING_DELTA);
 | 
						|
 | 
						|
  t->idx_max = 0;
 | 
						|
  t->num.idx_max = 0;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = 0.1f;
 | 
						|
  t->snap[2] = t->snap[1] * 0.1f;
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_type[0] = B_UNIT_NONE;
 | 
						|
 | 
						|
  t->flag |= T_NO_CONSTRAINT | T_NO_PROJECT;
 | 
						|
}
 | 
						|
 | 
						|
static void applyCrease(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  float crease;
 | 
						|
  int i;
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  crease = t->values[0];
 | 
						|
 | 
						|
  CLAMP_MAX(crease, 1.0f);
 | 
						|
 | 
						|
  snapGridIncrement(t, &crease);
 | 
						|
 | 
						|
  applyNumInput(&t->num, &crease);
 | 
						|
 | 
						|
  t->values[0] = crease;
 | 
						|
 | 
						|
  /* header print for NumInput */
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    char c[NUM_STR_REP_LEN];
 | 
						|
 | 
						|
    outputNumInput(&(t->num), c, &t->scene->unit);
 | 
						|
 | 
						|
    if (crease >= 0.0f) {
 | 
						|
      BLI_snprintf(str, sizeof(str), IFACE_("Crease: +%s %s"), c, t->proptext);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      BLI_snprintf(str, sizeof(str), IFACE_("Crease: %s %s"), c, t->proptext);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    /* default header print */
 | 
						|
    if (crease >= 0.0f) {
 | 
						|
      BLI_snprintf(str, sizeof(str), IFACE_("Crease: +%.3f %s"), crease, t->proptext);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      BLI_snprintf(str, sizeof(str), IFACE_("Crease: %.3f %s"), crease, t->proptext);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->val) {
 | 
						|
        *td->val = td->ival + crease * td->factor;
 | 
						|
        if (*td->val < 0.0f) {
 | 
						|
          *td->val = 0.0f;
 | 
						|
        }
 | 
						|
        if (*td->val > 1.0f) {
 | 
						|
          *td->val = 1.0f;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (EditBone (B-bone) width scaling) */
 | 
						|
 | 
						|
/** \name Transform B-bone width scaling
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initBoneSize(TransInfo *t)
 | 
						|
{
 | 
						|
  t->mode = TFM_BONESIZE;
 | 
						|
  t->transform = applyBoneSize;
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_SPRING_FLIP);
 | 
						|
 | 
						|
  t->idx_max = 2;
 | 
						|
  t->num.idx_max = 2;
 | 
						|
  t->num.val_flag[0] |= NUM_NULL_ONE;
 | 
						|
  t->num.val_flag[1] |= NUM_NULL_ONE;
 | 
						|
  t->num.val_flag[2] |= NUM_NULL_ONE;
 | 
						|
  t->num.flag |= NUM_AFFECT_ALL;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = 0.1f;
 | 
						|
  t->snap[2] = t->snap[1] * 0.1f;
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_type[0] = B_UNIT_NONE;
 | 
						|
  t->num.unit_type[1] = B_UNIT_NONE;
 | 
						|
  t->num.unit_type[2] = B_UNIT_NONE;
 | 
						|
}
 | 
						|
 | 
						|
static void headerBoneSize(TransInfo *t, const float vec[3], char str[UI_MAX_DRAW_STR])
 | 
						|
{
 | 
						|
  char tvec[NUM_STR_REP_LEN * 3];
 | 
						|
  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]);
 | 
						|
  }
 | 
						|
 | 
						|
  /* hmm... perhaps the y-axis values don't need to be shown? */
 | 
						|
  if (t->con.mode & CON_APPLY) {
 | 
						|
    if (t->num.idx_max == 0) {
 | 
						|
      BLI_snprintf(
 | 
						|
          str, UI_MAX_DRAW_STR, IFACE_("ScaleB: %s%s %s"), &tvec[0], t->con.text, t->proptext);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      BLI_snprintf(str,
 | 
						|
                   UI_MAX_DRAW_STR,
 | 
						|
                   IFACE_("ScaleB: %s : %s : %s%s %s"),
 | 
						|
                   &tvec[0],
 | 
						|
                   &tvec[NUM_STR_REP_LEN],
 | 
						|
                   &tvec[NUM_STR_REP_LEN * 2],
 | 
						|
                   t->con.text,
 | 
						|
                   t->proptext);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    BLI_snprintf(str,
 | 
						|
                 UI_MAX_DRAW_STR,
 | 
						|
                 IFACE_("ScaleB 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);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void ElementBoneSize(TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3])
 | 
						|
{
 | 
						|
  float tmat[3][3], smat[3][3], oldy;
 | 
						|
  float sizemat[3][3];
 | 
						|
 | 
						|
  mul_m3_m3m3(smat, mat, td->mtx);
 | 
						|
  mul_m3_m3m3(tmat, td->smtx, smat);
 | 
						|
 | 
						|
  if (t->con.applySize) {
 | 
						|
    t->con.applySize(t, tc, td, tmat);
 | 
						|
  }
 | 
						|
 | 
						|
  /* we've tucked the scale in loc */
 | 
						|
  oldy = td->iloc[1];
 | 
						|
  size_to_mat3(sizemat, td->iloc);
 | 
						|
  mul_m3_m3m3(tmat, tmat, sizemat);
 | 
						|
  mat3_to_size(td->loc, tmat);
 | 
						|
  td->loc[1] = oldy;
 | 
						|
}
 | 
						|
 | 
						|
static void applyBoneSize(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  float size[3], mat[3][3];
 | 
						|
  float ratio = t->values[0];
 | 
						|
  int i;
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  copy_v3_fl(size, ratio);
 | 
						|
 | 
						|
  snapGridIncrement(t, size);
 | 
						|
 | 
						|
  if (applyNumInput(&t->num, size)) {
 | 
						|
    constraintNumInput(t, size);
 | 
						|
  }
 | 
						|
 | 
						|
  copy_v3_v3(t->values, size);
 | 
						|
 | 
						|
  size_to_mat3(mat, size);
 | 
						|
 | 
						|
  if (t->con.applySize) {
 | 
						|
    t->con.applySize(t, NULL, NULL, mat);
 | 
						|
  }
 | 
						|
 | 
						|
  copy_m3_m3(t->mat, mat);  // used in gizmo
 | 
						|
 | 
						|
  headerBoneSize(t, size, str);
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      ElementBoneSize(t, tc, td, mat);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Bone Envelope) */
 | 
						|
 | 
						|
/** \name Transform Bone Envelope
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initBoneEnvelope(TransInfo *t)
 | 
						|
{
 | 
						|
  t->mode = TFM_BONE_ENVELOPE;
 | 
						|
  t->transform = applyBoneEnvelope;
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_SPRING);
 | 
						|
 | 
						|
  t->idx_max = 0;
 | 
						|
  t->num.idx_max = 0;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = 0.1f;
 | 
						|
  t->snap[2] = t->snap[1] * 0.1f;
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_type[0] = B_UNIT_NONE;
 | 
						|
 | 
						|
  t->flag |= T_NO_CONSTRAINT | T_NO_PROJECT;
 | 
						|
}
 | 
						|
 | 
						|
static void applyBoneEnvelope(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  float ratio;
 | 
						|
  int i;
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  ratio = t->values[0];
 | 
						|
 | 
						|
  snapGridIncrement(t, &ratio);
 | 
						|
 | 
						|
  applyNumInput(&t->num, &ratio);
 | 
						|
 | 
						|
  t->values[0] = ratio;
 | 
						|
 | 
						|
  /* header print for NumInput */
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    char c[NUM_STR_REP_LEN];
 | 
						|
 | 
						|
    outputNumInput(&(t->num), c, &t->scene->unit);
 | 
						|
    BLI_snprintf(str, sizeof(str), IFACE_("Envelope: %s"), c);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    BLI_snprintf(str, sizeof(str), IFACE_("Envelope: %3f"), ratio);
 | 
						|
  }
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->val) {
 | 
						|
        /* if the old/original value was 0.0f, then just use ratio */
 | 
						|
        if (td->ival) {
 | 
						|
          *td->val = td->ival * ratio;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          *td->val = ratio;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Original Data Store */
 | 
						|
 | 
						|
/** \name Orig-Data Store Utility Functions
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void slide_origdata_init_flag(TransInfo *t, TransDataContainer *tc, SlideOrigData *sod)
 | 
						|
{
 | 
						|
  BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
 | 
						|
  BMesh *bm = em->bm;
 | 
						|
  const bool has_layer_math = CustomData_has_math(&bm->ldata);
 | 
						|
  const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
 | 
						|
 | 
						|
  if ((t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) &&
 | 
						|
      /* don't do this at all for non-basis shape keys, too easy to
 | 
						|
       * accidentally break uv maps or vertex colors then */
 | 
						|
      (bm->shapenr <= 1) && (has_layer_math || (cd_loop_mdisp_offset != -1))) {
 | 
						|
    sod->use_origfaces = true;
 | 
						|
    sod->cd_loop_mdisp_offset = cd_loop_mdisp_offset;
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    sod->use_origfaces = false;
 | 
						|
    sod->cd_loop_mdisp_offset = -1;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void slide_origdata_init_data(TransDataContainer *tc, SlideOrigData *sod)
 | 
						|
{
 | 
						|
  if (sod->use_origfaces) {
 | 
						|
    BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
 | 
						|
    BMesh *bm = em->bm;
 | 
						|
 | 
						|
    sod->origfaces = BLI_ghash_ptr_new(__func__);
 | 
						|
    sod->bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default,
 | 
						|
                                       &((struct BMeshCreateParams){
 | 
						|
                                           .use_toolflags = false,
 | 
						|
                                       }));
 | 
						|
    /* we need to have matching customdata */
 | 
						|
    BM_mesh_copy_init_customdata(sod->bm_origfaces, bm, NULL);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void slide_origdata_create_data_vert(BMesh *bm,
 | 
						|
                                            SlideOrigData *sod,
 | 
						|
                                            TransDataGenericSlideVert *sv)
 | 
						|
{
 | 
						|
  BMIter liter;
 | 
						|
  int j, l_num;
 | 
						|
  float *loop_weights;
 | 
						|
 | 
						|
  /* copy face data */
 | 
						|
  // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) {
 | 
						|
  BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, sv->v);
 | 
						|
  l_num = liter.count;
 | 
						|
  loop_weights = BLI_array_alloca(loop_weights, l_num);
 | 
						|
  for (j = 0; j < l_num; j++) {
 | 
						|
    BMLoop *l = BM_iter_step(&liter);
 | 
						|
    BMLoop *l_prev, *l_next;
 | 
						|
    void **val_p;
 | 
						|
    if (!BLI_ghash_ensure_p(sod->origfaces, l->f, &val_p)) {
 | 
						|
      BMFace *f_copy = BM_face_copy(sod->bm_origfaces, bm, l->f, true, true);
 | 
						|
      *val_p = f_copy;
 | 
						|
    }
 | 
						|
 | 
						|
    if ((l_prev = BM_loop_find_prev_nodouble(l, l->next, FLT_EPSILON)) &&
 | 
						|
        (l_next = BM_loop_find_next_nodouble(l, l_prev, FLT_EPSILON))) {
 | 
						|
      loop_weights[j] = angle_v3v3v3(l_prev->v->co, l->v->co, l_next->v->co);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      loop_weights[j] = 0.0f;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* store cd_loop_groups */
 | 
						|
  if (sod->layer_math_map_num && (l_num != 0)) {
 | 
						|
    sv->cd_loop_groups = BLI_memarena_alloc(sod->arena, sod->layer_math_map_num * sizeof(void *));
 | 
						|
    for (j = 0; j < sod->layer_math_map_num; j++) {
 | 
						|
      const int layer_nr = sod->layer_math_map[j];
 | 
						|
      sv->cd_loop_groups[j] = BM_vert_loop_groups_data_layer_create(
 | 
						|
          bm, sv->v, layer_nr, loop_weights, sod->arena);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    sv->cd_loop_groups = NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  BLI_ghash_insert(sod->origverts, sv->v, sv);
 | 
						|
}
 | 
						|
 | 
						|
static void slide_origdata_create_data(TransDataContainer *tc,
 | 
						|
                                       SlideOrigData *sod,
 | 
						|
                                       TransDataGenericSlideVert *sv_array,
 | 
						|
                                       unsigned int v_stride,
 | 
						|
                                       unsigned int v_num)
 | 
						|
{
 | 
						|
  if (sod->use_origfaces) {
 | 
						|
    BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
 | 
						|
    BMesh *bm = em->bm;
 | 
						|
    unsigned int i;
 | 
						|
    TransDataGenericSlideVert *sv;
 | 
						|
 | 
						|
    int layer_index_dst;
 | 
						|
    int j;
 | 
						|
 | 
						|
    layer_index_dst = 0;
 | 
						|
 | 
						|
    if (CustomData_has_math(&bm->ldata)) {
 | 
						|
      /* over alloc, only 'math' layers are indexed */
 | 
						|
      sod->layer_math_map = MEM_mallocN(bm->ldata.totlayer * sizeof(int), __func__);
 | 
						|
      for (j = 0; j < bm->ldata.totlayer; j++) {
 | 
						|
        if (CustomData_layer_has_math(&bm->ldata, j)) {
 | 
						|
          sod->layer_math_map[layer_index_dst++] = j;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      BLI_assert(layer_index_dst != 0);
 | 
						|
    }
 | 
						|
 | 
						|
    sod->layer_math_map_num = layer_index_dst;
 | 
						|
 | 
						|
    sod->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
 | 
						|
 | 
						|
    sod->origverts = BLI_ghash_ptr_new_ex(__func__, v_num);
 | 
						|
 | 
						|
    for (i = 0, sv = sv_array; i < v_num; i++, sv = POINTER_OFFSET(sv, v_stride)) {
 | 
						|
      slide_origdata_create_data_vert(bm, sod, sv);
 | 
						|
    }
 | 
						|
 | 
						|
    if (tc->mirror.axis_flag) {
 | 
						|
      TransData *td = tc->data;
 | 
						|
      TransDataGenericSlideVert *sv_mirror;
 | 
						|
 | 
						|
      sod->sv_mirror = MEM_callocN(sizeof(*sv_mirror) * tc->data_len, __func__);
 | 
						|
      sod->totsv_mirror = tc->data_len;
 | 
						|
 | 
						|
      sv_mirror = sod->sv_mirror;
 | 
						|
 | 
						|
      for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
        BMVert *eve = td->extra;
 | 
						|
        /* Check the vertex has been used since both sides
 | 
						|
         * of the mirror may be selected & sliding. */
 | 
						|
        if (eve && !BLI_ghash_haskey(sod->origverts, eve)) {
 | 
						|
          sv_mirror->v = eve;
 | 
						|
          copy_v3_v3(sv_mirror->co_orig_3d, eve->co);
 | 
						|
 | 
						|
          slide_origdata_create_data_vert(bm, sod, sv_mirror);
 | 
						|
          sv_mirror++;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          sod->totsv_mirror--;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      if (sod->totsv_mirror == 0) {
 | 
						|
        MEM_freeN(sod->sv_mirror);
 | 
						|
        sod->sv_mirror = NULL;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * If we're sliding the vert, return its original location, if not, the current location is good.
 | 
						|
 */
 | 
						|
static const float *slide_origdata_orig_vert_co(SlideOrigData *sod, BMVert *v)
 | 
						|
{
 | 
						|
  TransDataGenericSlideVert *sv = BLI_ghash_lookup(sod->origverts, v);
 | 
						|
  return sv ? sv->co_orig_3d : v->co;
 | 
						|
}
 | 
						|
 | 
						|
static void slide_origdata_interp_data_vert(SlideOrigData *sod,
 | 
						|
                                            BMesh *bm,
 | 
						|
                                            bool is_final,
 | 
						|
                                            TransDataGenericSlideVert *sv)
 | 
						|
{
 | 
						|
  BMIter liter;
 | 
						|
  int j, l_num;
 | 
						|
  float *loop_weights;
 | 
						|
  const bool is_moved = (len_squared_v3v3(sv->v->co, sv->co_orig_3d) > FLT_EPSILON);
 | 
						|
  const bool do_loop_weight = sod->layer_math_map_num && is_moved;
 | 
						|
  const bool do_loop_mdisps = is_final && is_moved && (sod->cd_loop_mdisp_offset != -1);
 | 
						|
  const float *v_proj_axis = sv->v->no;
 | 
						|
  /* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */
 | 
						|
  float v_proj[3][3];
 | 
						|
 | 
						|
  if (do_loop_weight || do_loop_mdisps) {
 | 
						|
    project_plane_normalized_v3_v3v3(v_proj[1], sv->co_orig_3d, v_proj_axis);
 | 
						|
  }
 | 
						|
 | 
						|
  // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT)
 | 
						|
  BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, sv->v);
 | 
						|
  l_num = liter.count;
 | 
						|
  loop_weights = do_loop_weight ? BLI_array_alloca(loop_weights, l_num) : NULL;
 | 
						|
  for (j = 0; j < l_num; j++) {
 | 
						|
    BMFace *f_copy; /* the copy of 'f' */
 | 
						|
    BMLoop *l = BM_iter_step(&liter);
 | 
						|
 | 
						|
    f_copy = BLI_ghash_lookup(sod->origfaces, l->f);
 | 
						|
 | 
						|
    /* only loop data, no vertex data since that contains shape keys,
 | 
						|
     * and we do not want to mess up other shape keys */
 | 
						|
    BM_loop_interp_from_face(bm, l, f_copy, false, false);
 | 
						|
 | 
						|
    /* make sure face-attributes are correct (e.g. MTexPoly) */
 | 
						|
    BM_elem_attrs_copy_ex(sod->bm_origfaces, bm, f_copy, l->f, 0x0, CD_MASK_NORMAL);
 | 
						|
 | 
						|
    /* weight the loop */
 | 
						|
    if (do_loop_weight) {
 | 
						|
      const float eps = 1.0e-8f;
 | 
						|
      const BMLoop *l_prev = l->prev;
 | 
						|
      const BMLoop *l_next = l->next;
 | 
						|
      const float *co_prev = slide_origdata_orig_vert_co(sod, l_prev->v);
 | 
						|
      const float *co_next = slide_origdata_orig_vert_co(sod, l_next->v);
 | 
						|
      bool co_prev_ok;
 | 
						|
      bool co_next_ok;
 | 
						|
 | 
						|
      /* In the unlikely case that we're next to a zero length edge -
 | 
						|
       * walk around the to the next.
 | 
						|
       *
 | 
						|
       * Since we only need to check if the vertex is in this corner,
 | 
						|
       * its not important _which_ loop - as long as its not overlapping
 | 
						|
       * 'sv->co_orig_3d', see: T45096. */
 | 
						|
      project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis);
 | 
						|
      while (UNLIKELY(((co_prev_ok = (len_squared_v3v3(v_proj[1], v_proj[0]) > eps)) == false) &&
 | 
						|
                      ((l_prev = l_prev->prev) != l->next))) {
 | 
						|
        co_prev = slide_origdata_orig_vert_co(sod, l_prev->v);
 | 
						|
        project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis);
 | 
						|
      }
 | 
						|
      project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis);
 | 
						|
      while (UNLIKELY(((co_next_ok = (len_squared_v3v3(v_proj[1], v_proj[2]) > eps)) == false) &&
 | 
						|
                      ((l_next = l_next->next) != l->prev))) {
 | 
						|
        co_next = slide_origdata_orig_vert_co(sod, l_next->v);
 | 
						|
        project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis);
 | 
						|
      }
 | 
						|
 | 
						|
      if (co_prev_ok && co_next_ok) {
 | 
						|
        const float dist = dist_signed_squared_to_corner_v3v3v3(
 | 
						|
            sv->v->co, UNPACK3(v_proj), v_proj_axis);
 | 
						|
 | 
						|
        loop_weights[j] = (dist >= 0.0f) ? 1.0f : ((dist <= -eps) ? 0.0f : (1.0f + (dist / eps)));
 | 
						|
        if (UNLIKELY(!isfinite(loop_weights[j]))) {
 | 
						|
          loop_weights[j] = 0.0f;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        loop_weights[j] = 0.0f;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (sod->layer_math_map_num) {
 | 
						|
    if (do_loop_weight) {
 | 
						|
      for (j = 0; j < sod->layer_math_map_num; j++) {
 | 
						|
        BM_vert_loop_groups_data_layer_merge_weights(
 | 
						|
            bm, sv->cd_loop_groups[j], sod->layer_math_map[j], loop_weights);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      for (j = 0; j < sod->layer_math_map_num; j++) {
 | 
						|
        BM_vert_loop_groups_data_layer_merge(bm, sv->cd_loop_groups[j], sod->layer_math_map[j]);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* Special handling for multires
 | 
						|
   *
 | 
						|
   * Interpolate from every other loop (not ideal)
 | 
						|
   * However values will only be taken from loops which overlap other mdisps.
 | 
						|
   * */
 | 
						|
  if (do_loop_mdisps) {
 | 
						|
    float(*faces_center)[3] = BLI_array_alloca(faces_center, l_num);
 | 
						|
    BMLoop *l;
 | 
						|
 | 
						|
    BM_ITER_ELEM_INDEX (l, &liter, sv->v, BM_LOOPS_OF_VERT, j) {
 | 
						|
      BM_face_calc_center_median(l->f, faces_center[j]);
 | 
						|
    }
 | 
						|
 | 
						|
    BM_ITER_ELEM_INDEX (l, &liter, sv->v, BM_LOOPS_OF_VERT, j) {
 | 
						|
      BMFace *f_copy = BLI_ghash_lookup(sod->origfaces, l->f);
 | 
						|
      float f_copy_center[3];
 | 
						|
      BMIter liter_other;
 | 
						|
      BMLoop *l_other;
 | 
						|
      int j_other;
 | 
						|
 | 
						|
      BM_face_calc_center_median(f_copy, f_copy_center);
 | 
						|
 | 
						|
      BM_ITER_ELEM_INDEX (l_other, &liter_other, sv->v, BM_LOOPS_OF_VERT, j_other) {
 | 
						|
        BM_face_interp_multires_ex(bm,
 | 
						|
                                   l_other->f,
 | 
						|
                                   f_copy,
 | 
						|
                                   faces_center[j_other],
 | 
						|
                                   f_copy_center,
 | 
						|
                                   sod->cd_loop_mdisp_offset);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void slide_origdata_interp_data(Object *obedit,
 | 
						|
                                       SlideOrigData *sod,
 | 
						|
                                       TransDataGenericSlideVert *sv,
 | 
						|
                                       unsigned int v_stride,
 | 
						|
                                       unsigned int v_num,
 | 
						|
                                       bool is_final)
 | 
						|
{
 | 
						|
  if (sod->use_origfaces) {
 | 
						|
    BMEditMesh *em = BKE_editmesh_from_object(obedit);
 | 
						|
    BMesh *bm = em->bm;
 | 
						|
    unsigned int i;
 | 
						|
    const bool has_mdisps = (sod->cd_loop_mdisp_offset != -1);
 | 
						|
 | 
						|
    for (i = 0; i < v_num; i++, sv = POINTER_OFFSET(sv, v_stride)) {
 | 
						|
 | 
						|
      if (sv->cd_loop_groups || has_mdisps) {
 | 
						|
        slide_origdata_interp_data_vert(sod, bm, is_final, sv);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (sod->sv_mirror) {
 | 
						|
      sv = sod->sv_mirror;
 | 
						|
      for (i = 0; i < v_num; i++, sv++) {
 | 
						|
        if (sv->cd_loop_groups || has_mdisps) {
 | 
						|
          slide_origdata_interp_data_vert(sod, bm, is_final, sv);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void slide_origdata_free_date(SlideOrigData *sod)
 | 
						|
{
 | 
						|
  if (sod->use_origfaces) {
 | 
						|
    if (sod->bm_origfaces) {
 | 
						|
      BM_mesh_free(sod->bm_origfaces);
 | 
						|
      sod->bm_origfaces = NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    if (sod->origfaces) {
 | 
						|
      BLI_ghash_free(sod->origfaces, NULL, NULL);
 | 
						|
      sod->origfaces = NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    if (sod->origverts) {
 | 
						|
      BLI_ghash_free(sod->origverts, NULL, NULL);
 | 
						|
      sod->origverts = NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    if (sod->arena) {
 | 
						|
      BLI_memarena_free(sod->arena);
 | 
						|
      sod->arena = NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    MEM_SAFE_FREE(sod->layer_math_map);
 | 
						|
 | 
						|
    MEM_SAFE_FREE(sod->sv_mirror);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Edge Slide) */
 | 
						|
 | 
						|
/** \name Transform Edge Slide
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void calcEdgeSlideCustomPoints(struct TransInfo *t)
 | 
						|
{
 | 
						|
  EdgeSlideData *sld = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data;
 | 
						|
 | 
						|
  setCustomPoints(t, &t->mouse, sld->mval_end, sld->mval_start);
 | 
						|
 | 
						|
  /* setCustomPoints isn't normally changing as the mouse moves,
 | 
						|
   * in this case apply mouse input immediately so we don't refresh
 | 
						|
   * with the value from the previous points */
 | 
						|
  applyMouseInput(t, &t->mouse, t->mval, t->values);
 | 
						|
}
 | 
						|
 | 
						|
static BMEdge *get_other_edge(BMVert *v, BMEdge *e)
 | 
						|
{
 | 
						|
  BMIter iter;
 | 
						|
  BMEdge *e_iter;
 | 
						|
 | 
						|
  BM_ITER_ELEM (e_iter, &iter, v, BM_EDGES_OF_VERT) {
 | 
						|
    if (BM_elem_flag_test(e_iter, BM_ELEM_SELECT) && e_iter != e) {
 | 
						|
      return e_iter;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/* interpoaltes along a line made up of 2 segments (used for edge slide) */
 | 
						|
static void interp_line_v3_v3v3v3(
 | 
						|
    float p[3], const float v1[3], const float v2[3], const float v3[3], float t)
 | 
						|
{
 | 
						|
  float t_mid, t_delta;
 | 
						|
 | 
						|
  /* could be pre-calculated */
 | 
						|
  t_mid = line_point_factor_v3(v2, v1, v3);
 | 
						|
 | 
						|
  t_delta = t - t_mid;
 | 
						|
  if (t_delta < 0.0f) {
 | 
						|
    if (UNLIKELY(fabsf(t_mid) < FLT_EPSILON)) {
 | 
						|
      copy_v3_v3(p, v2);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      interp_v3_v3v3(p, v1, v2, t / t_mid);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    t = t - t_mid;
 | 
						|
    t_mid = 1.0f - t_mid;
 | 
						|
 | 
						|
    if (UNLIKELY(fabsf(t_mid) < FLT_EPSILON)) {
 | 
						|
      copy_v3_v3(p, v3);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      interp_v3_v3v3(p, v2, v3, t / t_mid);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Find the closest point on the ngon on the opposite side.
 | 
						|
 * used to set the edge slide distance for ngons.
 | 
						|
 */
 | 
						|
static bool bm_loop_calc_opposite_co(BMLoop *l_tmp, const float plane_no[3], float r_co[3])
 | 
						|
{
 | 
						|
  /* skip adjacent edges */
 | 
						|
  BMLoop *l_first = l_tmp->next;
 | 
						|
  BMLoop *l_last = l_tmp->prev;
 | 
						|
  BMLoop *l_iter;
 | 
						|
  float dist = FLT_MAX;
 | 
						|
  bool found = false;
 | 
						|
 | 
						|
  l_iter = l_first;
 | 
						|
  do {
 | 
						|
    float tvec[3];
 | 
						|
    if (isect_line_plane_v3(tvec, l_iter->v->co, l_iter->next->v->co, l_tmp->v->co, plane_no)) {
 | 
						|
      const float fac = line_point_factor_v3(tvec, l_iter->v->co, l_iter->next->v->co);
 | 
						|
      /* allow some overlap to avoid missing the intersection because of float precision */
 | 
						|
      if ((fac > -FLT_EPSILON) && (fac < 1.0f + FLT_EPSILON)) {
 | 
						|
        /* likelihood of multiple intersections per ngon is quite low,
 | 
						|
         * it would have to loop back on its self, but better support it
 | 
						|
         * so check for the closest opposite edge */
 | 
						|
        const float tdist = len_v3v3(l_tmp->v->co, tvec);
 | 
						|
        if (tdist < dist) {
 | 
						|
          copy_v3_v3(r_co, tvec);
 | 
						|
          dist = tdist;
 | 
						|
          found = true;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  } while ((l_iter = l_iter->next) != l_last);
 | 
						|
 | 
						|
  return found;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Given 2 edges and a loop, step over the loops
 | 
						|
 * and calculate a direction to slide along.
 | 
						|
 *
 | 
						|
 * \param r_slide_vec: the direction to slide,
 | 
						|
 * the length of the vector defines the slide distance.
 | 
						|
 */
 | 
						|
static BMLoop *get_next_loop(
 | 
						|
    BMVert *v, BMLoop *l, BMEdge *e_prev, BMEdge *e_next, float r_slide_vec[3])
 | 
						|
{
 | 
						|
  BMLoop *l_first;
 | 
						|
  float vec_accum[3] = {0.0f, 0.0f, 0.0f};
 | 
						|
  float vec_accum_len = 0.0f;
 | 
						|
  int i = 0;
 | 
						|
 | 
						|
  BLI_assert(BM_edge_share_vert(e_prev, e_next) == v);
 | 
						|
  BLI_assert(BM_vert_in_edge(l->e, v));
 | 
						|
 | 
						|
  l_first = l;
 | 
						|
  do {
 | 
						|
    l = BM_loop_other_edge_loop(l, v);
 | 
						|
 | 
						|
    if (l->e == e_next) {
 | 
						|
      if (i) {
 | 
						|
        normalize_v3_length(vec_accum, vec_accum_len / (float)i);
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        /* When there is no edge to slide along,
 | 
						|
         * we must slide along the vector defined by the face we're attach to */
 | 
						|
        BMLoop *l_tmp = BM_face_vert_share_loop(l_first->f, v);
 | 
						|
 | 
						|
        BLI_assert(ELEM(l_tmp->e, e_prev, e_next) && ELEM(l_tmp->prev->e, e_prev, e_next));
 | 
						|
 | 
						|
        if (l_tmp->f->len == 4) {
 | 
						|
          /* we could use code below, but in this case
 | 
						|
           * sliding diagonally across the quad works well */
 | 
						|
          sub_v3_v3v3(vec_accum, l_tmp->next->next->v->co, v->co);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          float tdir[3];
 | 
						|
          BM_loop_calc_face_direction(l_tmp, tdir);
 | 
						|
          cross_v3_v3v3(vec_accum, l_tmp->f->no, tdir);
 | 
						|
#if 0
 | 
						|
          /* rough guess, we can  do better! */
 | 
						|
          normalize_v3_length(vec_accum,
 | 
						|
                              (BM_edge_calc_length(e_prev) + BM_edge_calc_length(e_next)) / 2.0f);
 | 
						|
#else
 | 
						|
          /* be clever, check the opposite ngon edge to slide into.
 | 
						|
           * this gives best results */
 | 
						|
          {
 | 
						|
            float tvec[3];
 | 
						|
            float dist;
 | 
						|
 | 
						|
            if (bm_loop_calc_opposite_co(l_tmp, tdir, tvec)) {
 | 
						|
              dist = len_v3v3(l_tmp->v->co, tvec);
 | 
						|
            }
 | 
						|
            else {
 | 
						|
              dist = (BM_edge_calc_length(e_prev) + BM_edge_calc_length(e_next)) / 2.0f;
 | 
						|
            }
 | 
						|
 | 
						|
            normalize_v3_length(vec_accum, dist);
 | 
						|
          }
 | 
						|
#endif
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      copy_v3_v3(r_slide_vec, vec_accum);
 | 
						|
      return l;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      /* accumulate the normalized edge vector,
 | 
						|
       * normalize so some edges don't skew the result */
 | 
						|
      float tvec[3];
 | 
						|
      sub_v3_v3v3(tvec, BM_edge_other_vert(l->e, v)->co, v->co);
 | 
						|
      vec_accum_len += normalize_v3(tvec);
 | 
						|
      add_v3_v3(vec_accum, tvec);
 | 
						|
      i += 1;
 | 
						|
    }
 | 
						|
 | 
						|
    if (BM_loop_other_edge_loop(l, v)->e == e_next) {
 | 
						|
      if (i) {
 | 
						|
        normalize_v3_length(vec_accum, vec_accum_len / (float)i);
 | 
						|
      }
 | 
						|
 | 
						|
      copy_v3_v3(r_slide_vec, vec_accum);
 | 
						|
      return BM_loop_other_edge_loop(l, v);
 | 
						|
    }
 | 
						|
 | 
						|
  } while ((l != l->radial_next) && ((l = l->radial_next) != l_first));
 | 
						|
 | 
						|
  if (i) {
 | 
						|
    normalize_v3_length(vec_accum, vec_accum_len / (float)i);
 | 
						|
  }
 | 
						|
 | 
						|
  copy_v3_v3(r_slide_vec, vec_accum);
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Calculate screenspace `mval_start` / `mval_end`, optionally slide direction.
 | 
						|
 */
 | 
						|
static void calcEdgeSlide_mval_range(TransInfo *t,
 | 
						|
                                     TransDataContainer *tc,
 | 
						|
                                     EdgeSlideData *sld,
 | 
						|
                                     const int *sv_table,
 | 
						|
                                     const int loop_nr,
 | 
						|
                                     const float mval[2],
 | 
						|
                                     const bool use_occlude_geometry,
 | 
						|
                                     const bool use_calc_direction)
 | 
						|
{
 | 
						|
  TransDataEdgeSlideVert *sv_array = sld->sv;
 | 
						|
  BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
 | 
						|
  BMesh *bm = em->bm;
 | 
						|
  ARegion *ar = t->ar;
 | 
						|
  View3D *v3d = NULL;
 | 
						|
  RegionView3D *rv3d = NULL;
 | 
						|
  float projectMat[4][4];
 | 
						|
  BMBVHTree *bmbvh;
 | 
						|
 | 
						|
  /* only for use_calc_direction */
 | 
						|
  float(*loop_dir)[3] = NULL, *loop_maxdist = NULL;
 | 
						|
 | 
						|
  float mval_start[2], mval_end[2];
 | 
						|
  float mval_dir[3], dist_best_sq;
 | 
						|
  BMIter iter;
 | 
						|
  BMEdge *e;
 | 
						|
 | 
						|
  if (t->spacetype == SPACE_VIEW3D) {
 | 
						|
    /* background mode support */
 | 
						|
    v3d = t->sa ? t->sa->spacedata.first : NULL;
 | 
						|
    rv3d = t->ar ? t->ar->regiondata : NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!rv3d) {
 | 
						|
    /* ok, let's try to survive this */
 | 
						|
    unit_m4(projectMat);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    ED_view3d_ob_project_mat_get(rv3d, tc->obedit, projectMat);
 | 
						|
  }
 | 
						|
 | 
						|
  if (use_occlude_geometry) {
 | 
						|
    bmbvh = BKE_bmbvh_new_from_editmesh(em, BMBVH_RESPECT_HIDDEN, NULL, false);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    bmbvh = NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  /* find mouse vectors, the global one, and one per loop in case we have
 | 
						|
   * multiple loops selected, in case they are oriented different */
 | 
						|
  zero_v3(mval_dir);
 | 
						|
  dist_best_sq = -1.0f;
 | 
						|
 | 
						|
  if (use_calc_direction) {
 | 
						|
    loop_dir = MEM_callocN(sizeof(float[3]) * loop_nr, "sv loop_dir");
 | 
						|
    loop_maxdist = MEM_mallocN(sizeof(float) * loop_nr, "sv loop_maxdist");
 | 
						|
    copy_vn_fl(loop_maxdist, loop_nr, -1.0f);
 | 
						|
  }
 | 
						|
 | 
						|
  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
 | 
						|
    if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
 | 
						|
      int i;
 | 
						|
 | 
						|
      /* search cross edges for visible edge to the mouse cursor,
 | 
						|
       * then use the shared vertex to calculate screen vector*/
 | 
						|
      for (i = 0; i < 2; i++) {
 | 
						|
        BMIter iter_other;
 | 
						|
        BMEdge *e_other;
 | 
						|
 | 
						|
        BMVert *v = i ? e->v1 : e->v2;
 | 
						|
        BM_ITER_ELEM (e_other, &iter_other, v, BM_EDGES_OF_VERT) {
 | 
						|
          /* screen-space coords */
 | 
						|
          float sco_a[3], sco_b[3];
 | 
						|
          float dist_sq;
 | 
						|
          int j, l_nr;
 | 
						|
 | 
						|
          if (BM_elem_flag_test(e_other, BM_ELEM_SELECT)) {
 | 
						|
            continue;
 | 
						|
          }
 | 
						|
 | 
						|
          /* This test is only relevant if object is not wire-drawn! See [#32068]. */
 | 
						|
          if (use_occlude_geometry &&
 | 
						|
              !BMBVH_EdgeVisible(bmbvh, e_other, t->depsgraph, ar, v3d, tc->obedit)) {
 | 
						|
            continue;
 | 
						|
          }
 | 
						|
 | 
						|
          BLI_assert(sv_table[BM_elem_index_get(v)] != -1);
 | 
						|
          j = sv_table[BM_elem_index_get(v)];
 | 
						|
 | 
						|
          if (sv_array[j].v_side[1]) {
 | 
						|
            ED_view3d_project_float_v3_m4(ar, sv_array[j].v_side[1]->co, sco_b, projectMat);
 | 
						|
          }
 | 
						|
          else {
 | 
						|
            add_v3_v3v3(sco_b, v->co, sv_array[j].dir_side[1]);
 | 
						|
            ED_view3d_project_float_v3_m4(ar, sco_b, sco_b, projectMat);
 | 
						|
          }
 | 
						|
 | 
						|
          if (sv_array[j].v_side[0]) {
 | 
						|
            ED_view3d_project_float_v3_m4(ar, sv_array[j].v_side[0]->co, sco_a, projectMat);
 | 
						|
          }
 | 
						|
          else {
 | 
						|
            add_v3_v3v3(sco_a, v->co, sv_array[j].dir_side[0]);
 | 
						|
            ED_view3d_project_float_v3_m4(ar, sco_a, sco_a, projectMat);
 | 
						|
          }
 | 
						|
 | 
						|
          /* global direction */
 | 
						|
          dist_sq = dist_squared_to_line_segment_v2(mval, sco_b, sco_a);
 | 
						|
          if ((dist_best_sq == -1.0f) ||
 | 
						|
              /* intentionally use 2d size on 3d vector */
 | 
						|
              (dist_sq < dist_best_sq && (len_squared_v2v2(sco_b, sco_a) > 0.1f))) {
 | 
						|
            dist_best_sq = dist_sq;
 | 
						|
            sub_v3_v3v3(mval_dir, sco_b, sco_a);
 | 
						|
          }
 | 
						|
 | 
						|
          if (use_calc_direction) {
 | 
						|
            /* per loop direction */
 | 
						|
            l_nr = sv_array[j].loop_nr;
 | 
						|
            if (loop_maxdist[l_nr] == -1.0f || dist_sq < loop_maxdist[l_nr]) {
 | 
						|
              loop_maxdist[l_nr] = dist_sq;
 | 
						|
              sub_v3_v3v3(loop_dir[l_nr], sco_b, sco_a);
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (use_calc_direction) {
 | 
						|
    int i;
 | 
						|
    sv_array = sld->sv;
 | 
						|
    for (i = 0; i < sld->totsv; i++, sv_array++) {
 | 
						|
      /* switch a/b if loop direction is different from global direction */
 | 
						|
      int l_nr = sv_array->loop_nr;
 | 
						|
      if (dot_v3v3(loop_dir[l_nr], mval_dir) < 0.0f) {
 | 
						|
        swap_v3_v3(sv_array->dir_side[0], sv_array->dir_side[1]);
 | 
						|
        SWAP(BMVert *, sv_array->v_side[0], sv_array->v_side[1]);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    MEM_freeN(loop_dir);
 | 
						|
    MEM_freeN(loop_maxdist);
 | 
						|
  }
 | 
						|
 | 
						|
  /* possible all of the edge loops are pointing directly at the view */
 | 
						|
  if (UNLIKELY(len_squared_v2(mval_dir) < 0.1f)) {
 | 
						|
    mval_dir[0] = 0.0f;
 | 
						|
    mval_dir[1] = 100.0f;
 | 
						|
  }
 | 
						|
 | 
						|
  /* zero out start */
 | 
						|
  zero_v2(mval_start);
 | 
						|
 | 
						|
  /* dir holds a vector along edge loop */
 | 
						|
  copy_v2_v2(mval_end, mval_dir);
 | 
						|
  mul_v2_fl(mval_end, 0.5f);
 | 
						|
 | 
						|
  sld->mval_start[0] = t->mval[0] + mval_start[0];
 | 
						|
  sld->mval_start[1] = t->mval[1] + mval_start[1];
 | 
						|
 | 
						|
  sld->mval_end[0] = t->mval[0] + mval_end[0];
 | 
						|
  sld->mval_end[1] = t->mval[1] + mval_end[1];
 | 
						|
 | 
						|
  if (bmbvh) {
 | 
						|
    BKE_bmbvh_free(bmbvh);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void calcEdgeSlide_even(TransInfo *t,
 | 
						|
                               TransDataContainer *tc,
 | 
						|
                               EdgeSlideData *sld,
 | 
						|
                               const float mval[2])
 | 
						|
{
 | 
						|
  TransDataEdgeSlideVert *sv = sld->sv;
 | 
						|
 | 
						|
  if (sld->totsv > 0) {
 | 
						|
    ARegion *ar = t->ar;
 | 
						|
    RegionView3D *rv3d = NULL;
 | 
						|
    float projectMat[4][4];
 | 
						|
 | 
						|
    int i = 0;
 | 
						|
 | 
						|
    float v_proj[2];
 | 
						|
    float dist_sq = 0;
 | 
						|
    float dist_min_sq = FLT_MAX;
 | 
						|
 | 
						|
    if (t->spacetype == SPACE_VIEW3D) {
 | 
						|
      /* background mode support */
 | 
						|
      rv3d = t->ar ? t->ar->regiondata : NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!rv3d) {
 | 
						|
      /* ok, let's try to survive this */
 | 
						|
      unit_m4(projectMat);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      ED_view3d_ob_project_mat_get(rv3d, tc->obedit, projectMat);
 | 
						|
    }
 | 
						|
 | 
						|
    for (i = 0; i < sld->totsv; i++, sv++) {
 | 
						|
      /* Set length */
 | 
						|
      sv->edge_len = len_v3v3(sv->dir_side[0], sv->dir_side[1]);
 | 
						|
 | 
						|
      ED_view3d_project_float_v2_m4(ar, sv->v->co, v_proj, projectMat);
 | 
						|
      dist_sq = len_squared_v2v2(mval, v_proj);
 | 
						|
      if (dist_sq < dist_min_sq) {
 | 
						|
        dist_min_sq = dist_sq;
 | 
						|
        sld->curr_sv_index = i;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    sld->curr_sv_index = 0;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static bool createEdgeSlideVerts_double_side(TransInfo *t, TransDataContainer *tc)
 | 
						|
{
 | 
						|
  BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
 | 
						|
  BMesh *bm = em->bm;
 | 
						|
  BMIter iter;
 | 
						|
  BMEdge *e;
 | 
						|
  BMVert *v;
 | 
						|
  TransDataEdgeSlideVert *sv_array;
 | 
						|
  int sv_tot;
 | 
						|
  int *sv_table; /* BMVert -> sv_array index */
 | 
						|
  EdgeSlideData *sld = MEM_callocN(sizeof(*sld), "sld");
 | 
						|
  float mval[2] = {(float)t->mval[0], (float)t->mval[1]};
 | 
						|
  int numsel, i, loop_nr;
 | 
						|
  bool use_occlude_geometry = false;
 | 
						|
  View3D *v3d = NULL;
 | 
						|
  RegionView3D *rv3d = NULL;
 | 
						|
 | 
						|
  slide_origdata_init_flag(t, tc, &sld->orig_data);
 | 
						|
 | 
						|
  sld->curr_sv_index = 0;
 | 
						|
 | 
						|
  /*ensure valid selection*/
 | 
						|
  BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
 | 
						|
    if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
 | 
						|
      BMIter iter2;
 | 
						|
      numsel = 0;
 | 
						|
      BM_ITER_ELEM (e, &iter2, v, BM_EDGES_OF_VERT) {
 | 
						|
        if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
 | 
						|
          /* BMESH_TODO: this is probably very evil,
 | 
						|
           * set v->e to a selected edge*/
 | 
						|
          v->e = e;
 | 
						|
 | 
						|
          numsel++;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      if (numsel == 0 || numsel > 2) {
 | 
						|
        MEM_freeN(sld);
 | 
						|
        return false; /* invalid edge selection */
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
 | 
						|
    if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
 | 
						|
      /* note, any edge with loops can work, but we won't get predictable results, so bail out */
 | 
						|
      if (!BM_edge_is_manifold(e) && !BM_edge_is_boundary(e)) {
 | 
						|
        /* can edges with at least once face user */
 | 
						|
        MEM_freeN(sld);
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  sv_table = MEM_mallocN(sizeof(*sv_table) * bm->totvert, __func__);
 | 
						|
 | 
						|
#define INDEX_UNSET -1
 | 
						|
#define INDEX_INVALID -2
 | 
						|
 | 
						|
  {
 | 
						|
    int j = 0;
 | 
						|
    BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
 | 
						|
      if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
 | 
						|
        BM_elem_flag_enable(v, BM_ELEM_TAG);
 | 
						|
        sv_table[i] = INDEX_UNSET;
 | 
						|
        j += 1;
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        BM_elem_flag_disable(v, BM_ELEM_TAG);
 | 
						|
        sv_table[i] = INDEX_INVALID;
 | 
						|
      }
 | 
						|
      BM_elem_index_set(v, i); /* set_inline */
 | 
						|
    }
 | 
						|
    bm->elem_index_dirty &= ~BM_VERT;
 | 
						|
 | 
						|
    if (!j) {
 | 
						|
      MEM_freeN(sld);
 | 
						|
      MEM_freeN(sv_table);
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    sv_tot = j;
 | 
						|
  }
 | 
						|
 | 
						|
  sv_array = MEM_callocN(sizeof(TransDataEdgeSlideVert) * sv_tot, "sv_array");
 | 
						|
  loop_nr = 0;
 | 
						|
 | 
						|
  STACK_DECLARE(sv_array);
 | 
						|
  STACK_INIT(sv_array, sv_tot);
 | 
						|
 | 
						|
  while (1) {
 | 
						|
    float vec_a[3], vec_b[3];
 | 
						|
    BMLoop *l_a, *l_b;
 | 
						|
    BMLoop *l_a_prev, *l_b_prev;
 | 
						|
    BMVert *v_first;
 | 
						|
    /* If this succeeds call get_next_loop()
 | 
						|
     * which calculates the direction to slide based on clever checks.
 | 
						|
     *
 | 
						|
     * otherwise we simply use 'e_dir' as an edge-rail.
 | 
						|
     * (which is better when the attached edge is a boundary, see: T40422)
 | 
						|
     */
 | 
						|
#define EDGESLIDE_VERT_IS_INNER(v, e_dir) \
 | 
						|
  ((BM_edge_is_boundary(e_dir) == false) && (BM_vert_edge_count_nonwire(v) == 2))
 | 
						|
 | 
						|
    v = NULL;
 | 
						|
    BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
 | 
						|
      if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (!v) {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!v->e) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    v_first = v;
 | 
						|
 | 
						|
    /*walk along the edge loop*/
 | 
						|
    e = v->e;
 | 
						|
 | 
						|
    /*first, rewind*/
 | 
						|
    do {
 | 
						|
      e = get_other_edge(v, e);
 | 
						|
      if (!e) {
 | 
						|
        e = v->e;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (!BM_elem_flag_test(BM_edge_other_vert(e, v), BM_ELEM_TAG)) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      v = BM_edge_other_vert(e, v);
 | 
						|
    } while (e != v_first->e);
 | 
						|
 | 
						|
    BM_elem_flag_disable(v, BM_ELEM_TAG);
 | 
						|
 | 
						|
    l_a = e->l;
 | 
						|
    l_b = e->l->radial_next;
 | 
						|
 | 
						|
    /* regarding e_next, use get_next_loop()'s improved interpolation where possible */
 | 
						|
    {
 | 
						|
      BMEdge *e_next = get_other_edge(v, e);
 | 
						|
      if (e_next) {
 | 
						|
        get_next_loop(v, l_a, e, e_next, vec_a);
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        BMLoop *l_tmp = BM_loop_other_edge_loop(l_a, v);
 | 
						|
        if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) {
 | 
						|
          get_next_loop(v, l_a, e, l_tmp->e, vec_a);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          sub_v3_v3v3(vec_a, BM_edge_other_vert(l_tmp->e, v)->co, v->co);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    /* !BM_edge_is_boundary(e); */
 | 
						|
    if (l_b != l_a) {
 | 
						|
      BMEdge *e_next = get_other_edge(v, e);
 | 
						|
      if (e_next) {
 | 
						|
        get_next_loop(v, l_b, e, e_next, vec_b);
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        BMLoop *l_tmp = BM_loop_other_edge_loop(l_b, v);
 | 
						|
        if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) {
 | 
						|
          get_next_loop(v, l_b, e, l_tmp->e, vec_b);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          sub_v3_v3v3(vec_b, BM_edge_other_vert(l_tmp->e, v)->co, v->co);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      l_b = NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    l_a_prev = NULL;
 | 
						|
    l_b_prev = NULL;
 | 
						|
 | 
						|
#define SV_FROM_VERT(v) \
 | 
						|
  ((sv_table[BM_elem_index_get(v)] == INDEX_UNSET) ? \
 | 
						|
       ((void)(sv_table[BM_elem_index_get(v)] = STACK_SIZE(sv_array)), \
 | 
						|
        STACK_PUSH_RET_PTR(sv_array)) : \
 | 
						|
       (&sv_array[sv_table[BM_elem_index_get(v)]]))
 | 
						|
 | 
						|
    /*iterate over the loop*/
 | 
						|
    v_first = v;
 | 
						|
    do {
 | 
						|
      bool l_a_ok_prev;
 | 
						|
      bool l_b_ok_prev;
 | 
						|
      TransDataEdgeSlideVert *sv;
 | 
						|
      BMVert *v_prev;
 | 
						|
      BMEdge *e_prev;
 | 
						|
 | 
						|
      /* XXX, 'sv' will initialize multiple times, this is suspicious. see [#34024] */
 | 
						|
      BLI_assert(v != NULL);
 | 
						|
      BLI_assert(sv_table[BM_elem_index_get(v)] != INDEX_INVALID);
 | 
						|
      sv = SV_FROM_VERT(v);
 | 
						|
      sv->v = v;
 | 
						|
      copy_v3_v3(sv->v_co_orig, v->co);
 | 
						|
      sv->loop_nr = loop_nr;
 | 
						|
 | 
						|
      if (l_a || l_a_prev) {
 | 
						|
        BMLoop *l_tmp = BM_loop_other_edge_loop(l_a ? l_a : l_a_prev, v);
 | 
						|
        sv->v_side[0] = BM_edge_other_vert(l_tmp->e, v);
 | 
						|
        copy_v3_v3(sv->dir_side[0], vec_a);
 | 
						|
      }
 | 
						|
 | 
						|
      if (l_b || l_b_prev) {
 | 
						|
        BMLoop *l_tmp = BM_loop_other_edge_loop(l_b ? l_b : l_b_prev, v);
 | 
						|
        sv->v_side[1] = BM_edge_other_vert(l_tmp->e, v);
 | 
						|
        copy_v3_v3(sv->dir_side[1], vec_b);
 | 
						|
      }
 | 
						|
 | 
						|
      v_prev = v;
 | 
						|
      v = BM_edge_other_vert(e, v);
 | 
						|
 | 
						|
      e_prev = e;
 | 
						|
      e = get_other_edge(v, e);
 | 
						|
 | 
						|
      if (!e) {
 | 
						|
        BLI_assert(v != NULL);
 | 
						|
 | 
						|
        BLI_assert(sv_table[BM_elem_index_get(v)] != INDEX_INVALID);
 | 
						|
        sv = SV_FROM_VERT(v);
 | 
						|
 | 
						|
        sv->v = v;
 | 
						|
        copy_v3_v3(sv->v_co_orig, v->co);
 | 
						|
        sv->loop_nr = loop_nr;
 | 
						|
 | 
						|
        if (l_a) {
 | 
						|
          BMLoop *l_tmp = BM_loop_other_edge_loop(l_a, v);
 | 
						|
          sv->v_side[0] = BM_edge_other_vert(l_tmp->e, v);
 | 
						|
          if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) {
 | 
						|
            get_next_loop(v, l_a, e_prev, l_tmp->e, sv->dir_side[0]);
 | 
						|
          }
 | 
						|
          else {
 | 
						|
            sub_v3_v3v3(sv->dir_side[0], sv->v_side[0]->co, v->co);
 | 
						|
          }
 | 
						|
        }
 | 
						|
 | 
						|
        if (l_b) {
 | 
						|
          BMLoop *l_tmp = BM_loop_other_edge_loop(l_b, v);
 | 
						|
          sv->v_side[1] = BM_edge_other_vert(l_tmp->e, v);
 | 
						|
          if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) {
 | 
						|
            get_next_loop(v, l_b, e_prev, l_tmp->e, sv->dir_side[1]);
 | 
						|
          }
 | 
						|
          else {
 | 
						|
            sub_v3_v3v3(sv->dir_side[1], sv->v_side[1]->co, v->co);
 | 
						|
          }
 | 
						|
        }
 | 
						|
 | 
						|
        BM_elem_flag_disable(v, BM_ELEM_TAG);
 | 
						|
        BM_elem_flag_disable(v_prev, BM_ELEM_TAG);
 | 
						|
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      l_a_ok_prev = (l_a != NULL);
 | 
						|
      l_b_ok_prev = (l_b != NULL);
 | 
						|
 | 
						|
      l_a_prev = l_a;
 | 
						|
      l_b_prev = l_b;
 | 
						|
 | 
						|
      if (l_a) {
 | 
						|
        l_a = get_next_loop(v, l_a, e_prev, e, vec_a);
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        zero_v3(vec_a);
 | 
						|
      }
 | 
						|
 | 
						|
      if (l_b) {
 | 
						|
        l_b = get_next_loop(v, l_b, e_prev, e, vec_b);
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        zero_v3(vec_b);
 | 
						|
      }
 | 
						|
 | 
						|
      if (l_a && l_b) {
 | 
						|
        /* pass */
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        if (l_a || l_b) {
 | 
						|
          /* find the opposite loop if it was missing previously */
 | 
						|
          if (l_a == NULL && l_b && (l_b->radial_next != l_b)) {
 | 
						|
            l_a = l_b->radial_next;
 | 
						|
          }
 | 
						|
          else if (l_b == NULL && l_a && (l_a->radial_next != l_a)) {
 | 
						|
            l_b = l_a->radial_next;
 | 
						|
          }
 | 
						|
        }
 | 
						|
        else if (e->l != NULL) {
 | 
						|
          /* if there are non-contiguous faces, we can still recover
 | 
						|
           * the loops of the new edges faces */
 | 
						|
 | 
						|
          /* note!, the behavior in this case means edges may move in opposite directions,
 | 
						|
           * this could be made to work more usefully. */
 | 
						|
 | 
						|
          if (l_a_ok_prev) {
 | 
						|
            l_a = e->l;
 | 
						|
            l_b = (l_a->radial_next != l_a) ? l_a->radial_next : NULL;
 | 
						|
          }
 | 
						|
          else if (l_b_ok_prev) {
 | 
						|
            l_b = e->l;
 | 
						|
            l_a = (l_b->radial_next != l_b) ? l_b->radial_next : NULL;
 | 
						|
          }
 | 
						|
        }
 | 
						|
 | 
						|
        if (!l_a_ok_prev && l_a) {
 | 
						|
          get_next_loop(v, l_a, e, e_prev, vec_a);
 | 
						|
        }
 | 
						|
        if (!l_b_ok_prev && l_b) {
 | 
						|
          get_next_loop(v, l_b, e, e_prev, vec_b);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      BM_elem_flag_disable(v, BM_ELEM_TAG);
 | 
						|
      BM_elem_flag_disable(v_prev, BM_ELEM_TAG);
 | 
						|
    } while ((e != v_first->e) && (l_a || l_b));
 | 
						|
 | 
						|
#undef SV_FROM_VERT
 | 
						|
#undef INDEX_UNSET
 | 
						|
#undef INDEX_INVALID
 | 
						|
 | 
						|
    loop_nr++;
 | 
						|
 | 
						|
#undef EDGESLIDE_VERT_IS_INNER
 | 
						|
  }
 | 
						|
 | 
						|
  /* EDBM_flag_disable_all(em, BM_ELEM_SELECT); */
 | 
						|
 | 
						|
  BLI_assert(STACK_SIZE(sv_array) == sv_tot);
 | 
						|
 | 
						|
  sld->sv = sv_array;
 | 
						|
  sld->totsv = sv_tot;
 | 
						|
 | 
						|
  /* use for visibility checks */
 | 
						|
  if (t->spacetype == SPACE_VIEW3D) {
 | 
						|
    v3d = t->sa ? t->sa->spacedata.first : NULL;
 | 
						|
    rv3d = t->ar ? t->ar->regiondata : NULL;
 | 
						|
    use_occlude_geometry = (v3d && TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->dt > OB_WIRE &&
 | 
						|
                            !XRAY_ENABLED(v3d));
 | 
						|
  }
 | 
						|
 | 
						|
  calcEdgeSlide_mval_range(t, tc, sld, sv_table, loop_nr, mval, use_occlude_geometry, true);
 | 
						|
 | 
						|
  /* create copies of faces for customdata projection */
 | 
						|
  bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
 | 
						|
  slide_origdata_init_data(tc, &sld->orig_data);
 | 
						|
  slide_origdata_create_data(
 | 
						|
      tc, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv);
 | 
						|
 | 
						|
  if (rv3d) {
 | 
						|
    calcEdgeSlide_even(t, tc, sld, mval);
 | 
						|
  }
 | 
						|
 | 
						|
  sld->em = em;
 | 
						|
 | 
						|
  tc->custom.mode.data = sld;
 | 
						|
 | 
						|
  MEM_freeN(sv_table);
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * A simple version of #createEdgeSlideVerts_double_side
 | 
						|
 * Which assumes the longest unselected.
 | 
						|
 */
 | 
						|
static bool createEdgeSlideVerts_single_side(TransInfo *t, TransDataContainer *tc)
 | 
						|
{
 | 
						|
  BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
 | 
						|
  BMesh *bm = em->bm;
 | 
						|
  BMIter iter;
 | 
						|
  BMEdge *e;
 | 
						|
  TransDataEdgeSlideVert *sv_array;
 | 
						|
  int sv_tot;
 | 
						|
  int *sv_table; /* BMVert -> sv_array index */
 | 
						|
  EdgeSlideData *sld = MEM_callocN(sizeof(*sld), "sld");
 | 
						|
  float mval[2] = {(float)t->mval[0], (float)t->mval[1]};
 | 
						|
  int loop_nr;
 | 
						|
  bool use_occlude_geometry = false;
 | 
						|
  View3D *v3d = NULL;
 | 
						|
  RegionView3D *rv3d = NULL;
 | 
						|
 | 
						|
  if (t->spacetype == SPACE_VIEW3D) {
 | 
						|
    /* background mode support */
 | 
						|
    v3d = t->sa ? t->sa->spacedata.first : NULL;
 | 
						|
    rv3d = t->ar ? t->ar->regiondata : NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  slide_origdata_init_flag(t, tc, &sld->orig_data);
 | 
						|
 | 
						|
  sld->curr_sv_index = 0;
 | 
						|
  /* ensure valid selection */
 | 
						|
  {
 | 
						|
    int i = 0, j = 0;
 | 
						|
    BMVert *v;
 | 
						|
 | 
						|
    BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
 | 
						|
      if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
 | 
						|
        float len_sq_max = -1.0f;
 | 
						|
        BMIter iter2;
 | 
						|
        BM_ITER_ELEM (e, &iter2, v, BM_EDGES_OF_VERT) {
 | 
						|
          if (!BM_elem_flag_test(e, BM_ELEM_SELECT)) {
 | 
						|
            float len_sq = BM_edge_calc_length_squared(e);
 | 
						|
            if (len_sq > len_sq_max) {
 | 
						|
              len_sq_max = len_sq;
 | 
						|
              v->e = e;
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
 | 
						|
        if (len_sq_max != -1.0f) {
 | 
						|
          j++;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      BM_elem_index_set(v, i); /* set_inline */
 | 
						|
    }
 | 
						|
    bm->elem_index_dirty &= ~BM_VERT;
 | 
						|
 | 
						|
    if (!j) {
 | 
						|
      MEM_freeN(sld);
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    sv_tot = j;
 | 
						|
  }
 | 
						|
 | 
						|
  BLI_assert(sv_tot != 0);
 | 
						|
  /* over alloc */
 | 
						|
  sv_array = MEM_callocN(sizeof(TransDataEdgeSlideVert) * bm->totvertsel, "sv_array");
 | 
						|
 | 
						|
  /* same loop for all loops, weak but we dont connect loops in this case */
 | 
						|
  loop_nr = 1;
 | 
						|
 | 
						|
  sv_table = MEM_mallocN(sizeof(*sv_table) * bm->totvert, __func__);
 | 
						|
 | 
						|
  {
 | 
						|
    int i = 0, j = 0;
 | 
						|
    BMVert *v;
 | 
						|
 | 
						|
    BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
 | 
						|
      sv_table[i] = -1;
 | 
						|
      if ((v->e != NULL) && (BM_elem_flag_test(v, BM_ELEM_SELECT))) {
 | 
						|
        if (BM_elem_flag_test(v->e, BM_ELEM_SELECT) == 0) {
 | 
						|
          TransDataEdgeSlideVert *sv;
 | 
						|
          sv = &sv_array[j];
 | 
						|
          sv->v = v;
 | 
						|
          copy_v3_v3(sv->v_co_orig, v->co);
 | 
						|
          sv->v_side[0] = BM_edge_other_vert(v->e, v);
 | 
						|
          sub_v3_v3v3(sv->dir_side[0], sv->v_side[0]->co, v->co);
 | 
						|
          sv->loop_nr = 0;
 | 
						|
          sv_table[i] = j;
 | 
						|
          j += 1;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* check for wire vertices,
 | 
						|
   * interpolate the directions of wire verts between non-wire verts */
 | 
						|
  if (sv_tot != bm->totvert) {
 | 
						|
    const int sv_tot_nowire = sv_tot;
 | 
						|
    TransDataEdgeSlideVert *sv_iter = sv_array;
 | 
						|
 | 
						|
    for (int i = 0; i < sv_tot_nowire; i++, sv_iter++) {
 | 
						|
      BMIter eiter;
 | 
						|
      BM_ITER_ELEM (e, &eiter, sv_iter->v, BM_EDGES_OF_VERT) {
 | 
						|
        /* walk over wire */
 | 
						|
        TransDataEdgeSlideVert *sv_end = NULL;
 | 
						|
        BMEdge *e_step = e;
 | 
						|
        BMVert *v = sv_iter->v;
 | 
						|
        int j;
 | 
						|
 | 
						|
        j = sv_tot;
 | 
						|
 | 
						|
        while (1) {
 | 
						|
          BMVert *v_other = BM_edge_other_vert(e_step, v);
 | 
						|
          int endpoint = ((sv_table[BM_elem_index_get(v_other)] != -1) +
 | 
						|
                          (BM_vert_is_edge_pair(v_other) == false));
 | 
						|
 | 
						|
          if ((BM_elem_flag_test(e_step, BM_ELEM_SELECT) &&
 | 
						|
               BM_elem_flag_test(v_other, BM_ELEM_SELECT)) &&
 | 
						|
              (endpoint == 0)) {
 | 
						|
            /* scan down the list */
 | 
						|
            TransDataEdgeSlideVert *sv;
 | 
						|
            BLI_assert(sv_table[BM_elem_index_get(v_other)] == -1);
 | 
						|
            sv_table[BM_elem_index_get(v_other)] = j;
 | 
						|
            sv = &sv_array[j];
 | 
						|
            sv->v = v_other;
 | 
						|
            copy_v3_v3(sv->v_co_orig, v_other->co);
 | 
						|
            copy_v3_v3(sv->dir_side[0], sv_iter->dir_side[0]);
 | 
						|
            j++;
 | 
						|
 | 
						|
            /* advance! */
 | 
						|
            v = v_other;
 | 
						|
            e_step = BM_DISK_EDGE_NEXT(e_step, v_other);
 | 
						|
          }
 | 
						|
          else {
 | 
						|
            if ((endpoint == 2) && (sv_tot != j)) {
 | 
						|
              BLI_assert(BM_elem_index_get(v_other) != -1);
 | 
						|
              sv_end = &sv_array[sv_table[BM_elem_index_get(v_other)]];
 | 
						|
            }
 | 
						|
            break;
 | 
						|
          }
 | 
						|
        }
 | 
						|
 | 
						|
        if (sv_end) {
 | 
						|
          int sv_tot_prev = sv_tot;
 | 
						|
          const float *co_src = sv_iter->v->co;
 | 
						|
          const float *co_dst = sv_end->v->co;
 | 
						|
          const float *dir_src = sv_iter->dir_side[0];
 | 
						|
          const float *dir_dst = sv_end->dir_side[0];
 | 
						|
          sv_tot = j;
 | 
						|
 | 
						|
          while (j-- != sv_tot_prev) {
 | 
						|
            float factor;
 | 
						|
            factor = line_point_factor_v3(sv_array[j].v->co, co_src, co_dst);
 | 
						|
            interp_v3_v3v3(sv_array[j].dir_side[0], dir_src, dir_dst, factor);
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* EDBM_flag_disable_all(em, BM_ELEM_SELECT); */
 | 
						|
 | 
						|
  sld->sv = sv_array;
 | 
						|
  sld->totsv = sv_tot;
 | 
						|
 | 
						|
  /* use for visibility checks */
 | 
						|
  if (t->spacetype == SPACE_VIEW3D) {
 | 
						|
    v3d = t->sa ? t->sa->spacedata.first : NULL;
 | 
						|
    rv3d = t->ar ? t->ar->regiondata : NULL;
 | 
						|
    use_occlude_geometry = (v3d && TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->dt > OB_WIRE &&
 | 
						|
                            !XRAY_ENABLED(v3d));
 | 
						|
  }
 | 
						|
 | 
						|
  calcEdgeSlide_mval_range(t, tc, sld, sv_table, loop_nr, mval, use_occlude_geometry, false);
 | 
						|
 | 
						|
  /* create copies of faces for customdata projection */
 | 
						|
  bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
 | 
						|
  slide_origdata_init_data(tc, &sld->orig_data);
 | 
						|
  slide_origdata_create_data(
 | 
						|
      tc, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv);
 | 
						|
 | 
						|
  if (rv3d) {
 | 
						|
    calcEdgeSlide_even(t, tc, sld, mval);
 | 
						|
  }
 | 
						|
 | 
						|
  sld->em = em;
 | 
						|
 | 
						|
  tc->custom.mode.data = sld;
 | 
						|
 | 
						|
  MEM_freeN(sv_table);
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
void projectEdgeSlideData(TransInfo *t, bool is_final)
 | 
						|
{
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    EdgeSlideData *sld = tc->custom.mode.data;
 | 
						|
    SlideOrigData *sod = &sld->orig_data;
 | 
						|
 | 
						|
    if (sod->use_origfaces == false) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    slide_origdata_interp_data(tc->obedit,
 | 
						|
                               sod,
 | 
						|
                               (TransDataGenericSlideVert *)sld->sv,
 | 
						|
                               sizeof(*sld->sv),
 | 
						|
                               sld->totsv,
 | 
						|
                               is_final);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void freeEdgeSlideTempFaces(EdgeSlideData *sld)
 | 
						|
{
 | 
						|
  slide_origdata_free_date(&sld->orig_data);
 | 
						|
}
 | 
						|
 | 
						|
void freeEdgeSlideVerts(TransInfo *UNUSED(t),
 | 
						|
                        TransDataContainer *UNUSED(tc),
 | 
						|
                        TransCustomData *custom_data)
 | 
						|
{
 | 
						|
  EdgeSlideData *sld = custom_data->data;
 | 
						|
 | 
						|
  if (!sld) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  freeEdgeSlideTempFaces(sld);
 | 
						|
 | 
						|
  bmesh_edit_end(sld->em->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
 | 
						|
 | 
						|
  MEM_freeN(sld->sv);
 | 
						|
  MEM_freeN(sld);
 | 
						|
 | 
						|
  custom_data->data = NULL;
 | 
						|
}
 | 
						|
 | 
						|
static void initEdgeSlide_ex(
 | 
						|
    TransInfo *t, bool use_double_side, bool use_even, bool flipped, bool use_clamp)
 | 
						|
{
 | 
						|
  EdgeSlideData *sld;
 | 
						|
  bool ok = false;
 | 
						|
 | 
						|
  t->mode = TFM_EDGE_SLIDE;
 | 
						|
  t->transform = applyEdgeSlide;
 | 
						|
  t->handleEvent = handleEventEdgeSlide;
 | 
						|
 | 
						|
  {
 | 
						|
    EdgeSlideParams *slp = MEM_callocN(sizeof(*slp), __func__);
 | 
						|
    slp->use_even = use_even;
 | 
						|
    slp->flipped = flipped;
 | 
						|
    /* happens to be best for single-sided */
 | 
						|
    if (use_double_side == false) {
 | 
						|
      slp->flipped = !flipped;
 | 
						|
    }
 | 
						|
    slp->perc = 0.0f;
 | 
						|
 | 
						|
    if (!use_clamp) {
 | 
						|
      t->flag |= T_ALT_TRANSFORM;
 | 
						|
    }
 | 
						|
 | 
						|
    t->custom.mode.data = slp;
 | 
						|
    t->custom.mode.use_free = true;
 | 
						|
  }
 | 
						|
 | 
						|
  if (use_double_side) {
 | 
						|
    FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
      ok |= createEdgeSlideVerts_double_side(t, tc);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
      ok |= createEdgeSlideVerts_single_side(t, tc);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (!ok) {
 | 
						|
    t->state = TRANS_CANCEL;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    sld = tc->custom.mode.data;
 | 
						|
    if (!sld) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    tc->custom.mode.free_cb = freeEdgeSlideVerts;
 | 
						|
  }
 | 
						|
 | 
						|
  /* set custom point first if you want value to be initialized by init */
 | 
						|
  calcEdgeSlideCustomPoints(t);
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_CUSTOM_RATIO_FLIP);
 | 
						|
 | 
						|
  t->idx_max = 0;
 | 
						|
  t->num.idx_max = 0;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = 0.1f;
 | 
						|
  t->snap[2] = t->snap[1] * 0.1f;
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_type[0] = B_UNIT_NONE;
 | 
						|
 | 
						|
  t->flag |= T_NO_CONSTRAINT | T_NO_PROJECT;
 | 
						|
}
 | 
						|
 | 
						|
static void initEdgeSlide(TransInfo *t)
 | 
						|
{
 | 
						|
  initEdgeSlide_ex(t, true, false, false, true);
 | 
						|
}
 | 
						|
 | 
						|
static eRedrawFlag handleEventEdgeSlide(struct TransInfo *t, const struct wmEvent *event)
 | 
						|
{
 | 
						|
  if (t->mode == TFM_EDGE_SLIDE) {
 | 
						|
    EdgeSlideParams *slp = t->custom.mode.data;
 | 
						|
 | 
						|
    if (slp) {
 | 
						|
      switch (event->type) {
 | 
						|
        case EKEY:
 | 
						|
          if (event->val == KM_PRESS) {
 | 
						|
            slp->use_even = !slp->use_even;
 | 
						|
            calcEdgeSlideCustomPoints(t);
 | 
						|
            return TREDRAW_HARD;
 | 
						|
          }
 | 
						|
          break;
 | 
						|
        case FKEY:
 | 
						|
          if (event->val == KM_PRESS) {
 | 
						|
            slp->flipped = !slp->flipped;
 | 
						|
            calcEdgeSlideCustomPoints(t);
 | 
						|
            return TREDRAW_HARD;
 | 
						|
          }
 | 
						|
          break;
 | 
						|
        case CKEY:
 | 
						|
          /* use like a modifier key */
 | 
						|
          if (event->val == KM_PRESS) {
 | 
						|
            t->flag ^= T_ALT_TRANSFORM;
 | 
						|
            calcEdgeSlideCustomPoints(t);
 | 
						|
            return TREDRAW_HARD;
 | 
						|
          }
 | 
						|
          break;
 | 
						|
        case EVT_MODAL_MAP:
 | 
						|
#if 0
 | 
						|
          switch (event->val) {
 | 
						|
            case TFM_MODAL_EDGESLIDE_DOWN:
 | 
						|
              sld->curr_sv_index = ((sld->curr_sv_index - 1) + sld->totsv) % sld->totsv;
 | 
						|
              return TREDRAW_HARD;
 | 
						|
            case TFM_MODAL_EDGESLIDE_UP:
 | 
						|
              sld->curr_sv_index = (sld->curr_sv_index + 1) % sld->totsv;
 | 
						|
              return TREDRAW_HARD;
 | 
						|
          }
 | 
						|
#endif
 | 
						|
          break;
 | 
						|
        case MOUSEMOVE:
 | 
						|
          calcEdgeSlideCustomPoints(t);
 | 
						|
          break;
 | 
						|
        default:
 | 
						|
          break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return TREDRAW_NOTHING;
 | 
						|
}
 | 
						|
 | 
						|
static void drawEdgeSlide(TransInfo *t)
 | 
						|
{
 | 
						|
  if ((t->mode == TFM_EDGE_SLIDE) && TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data) {
 | 
						|
    const EdgeSlideParams *slp = t->custom.mode.data;
 | 
						|
    EdgeSlideData *sld = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data;
 | 
						|
    const bool is_clamp = !(t->flag & T_ALT_TRANSFORM);
 | 
						|
 | 
						|
    /* Even mode */
 | 
						|
    if ((slp->use_even == true) || (is_clamp == false)) {
 | 
						|
      const float line_size = UI_GetThemeValuef(TH_OUTLINE_WIDTH) + 0.5f;
 | 
						|
 | 
						|
      GPU_depth_test(false);
 | 
						|
 | 
						|
      GPU_blend(true);
 | 
						|
      GPU_blend_set_func_separate(
 | 
						|
          GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
 | 
						|
 | 
						|
      GPU_matrix_push();
 | 
						|
      GPU_matrix_mul(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat);
 | 
						|
 | 
						|
      uint pos = GPU_vertformat_attr_add(
 | 
						|
          immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
 | 
						|
 | 
						|
      immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
 | 
						|
 | 
						|
      if (slp->use_even == true) {
 | 
						|
        float co_a[3], co_b[3], co_mark[3];
 | 
						|
        TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index];
 | 
						|
        const float fac = (slp->perc + 1.0f) / 2.0f;
 | 
						|
        const float ctrl_size = UI_GetThemeValuef(TH_FACEDOT_SIZE) + 1.5f;
 | 
						|
        const float guide_size = ctrl_size - 0.5f;
 | 
						|
        const int alpha_shade = -30;
 | 
						|
 | 
						|
        add_v3_v3v3(co_a, curr_sv->v_co_orig, curr_sv->dir_side[0]);
 | 
						|
        add_v3_v3v3(co_b, curr_sv->v_co_orig, curr_sv->dir_side[1]);
 | 
						|
 | 
						|
        GPU_line_width(line_size);
 | 
						|
        immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade);
 | 
						|
        immBeginAtMost(GPU_PRIM_LINES, 4);
 | 
						|
        if (curr_sv->v_side[0]) {
 | 
						|
          immVertex3fv(pos, curr_sv->v_side[0]->co);
 | 
						|
          immVertex3fv(pos, curr_sv->v_co_orig);
 | 
						|
        }
 | 
						|
        if (curr_sv->v_side[1]) {
 | 
						|
          immVertex3fv(pos, curr_sv->v_side[1]->co);
 | 
						|
          immVertex3fv(pos, curr_sv->v_co_orig);
 | 
						|
        }
 | 
						|
        immEnd();
 | 
						|
 | 
						|
        immUniformThemeColorShadeAlpha(TH_SELECT, -30, alpha_shade);
 | 
						|
        GPU_point_size(ctrl_size);
 | 
						|
        immBegin(GPU_PRIM_POINTS, 1);
 | 
						|
        if (slp->flipped) {
 | 
						|
          if (curr_sv->v_side[1]) {
 | 
						|
            immVertex3fv(pos, curr_sv->v_side[1]->co);
 | 
						|
          }
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          if (curr_sv->v_side[0]) {
 | 
						|
            immVertex3fv(pos, curr_sv->v_side[0]->co);
 | 
						|
          }
 | 
						|
        }
 | 
						|
        immEnd();
 | 
						|
 | 
						|
        immUniformThemeColorShadeAlpha(TH_SELECT, 255, alpha_shade);
 | 
						|
        GPU_point_size(guide_size);
 | 
						|
        immBegin(GPU_PRIM_POINTS, 1);
 | 
						|
        interp_line_v3_v3v3v3(co_mark, co_b, curr_sv->v_co_orig, co_a, fac);
 | 
						|
        immVertex3fv(pos, co_mark);
 | 
						|
        immEnd();
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        if (is_clamp == false) {
 | 
						|
          const int side_index = sld->curr_side_unclamp;
 | 
						|
          TransDataEdgeSlideVert *sv;
 | 
						|
          int i;
 | 
						|
          const int alpha_shade = -160;
 | 
						|
 | 
						|
          GPU_line_width(line_size);
 | 
						|
          immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade);
 | 
						|
          immBegin(GPU_PRIM_LINES, sld->totsv * 2);
 | 
						|
 | 
						|
          /* TODO(campbell): Loop over all verts  */
 | 
						|
          sv = sld->sv;
 | 
						|
          for (i = 0; i < sld->totsv; i++, sv++) {
 | 
						|
            float a[3], b[3];
 | 
						|
 | 
						|
            if (!is_zero_v3(sv->dir_side[side_index])) {
 | 
						|
              copy_v3_v3(a, sv->dir_side[side_index]);
 | 
						|
            }
 | 
						|
            else {
 | 
						|
              copy_v3_v3(a, sv->dir_side[!side_index]);
 | 
						|
            }
 | 
						|
 | 
						|
            mul_v3_fl(a, 100.0f);
 | 
						|
            negate_v3_v3(b, a);
 | 
						|
            add_v3_v3(a, sv->v_co_orig);
 | 
						|
            add_v3_v3(b, sv->v_co_orig);
 | 
						|
 | 
						|
            immVertex3fv(pos, a);
 | 
						|
            immVertex3fv(pos, b);
 | 
						|
          }
 | 
						|
          immEnd();
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          BLI_assert(0);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      immUnbindProgram();
 | 
						|
 | 
						|
      GPU_matrix_pop();
 | 
						|
 | 
						|
      GPU_blend(false);
 | 
						|
 | 
						|
      GPU_depth_test(true);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void doEdgeSlide(TransInfo *t, float perc)
 | 
						|
{
 | 
						|
  EdgeSlideParams *slp = t->custom.mode.data;
 | 
						|
  EdgeSlideData *sld_active = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data;
 | 
						|
 | 
						|
  slp->perc = perc;
 | 
						|
 | 
						|
  if (slp->use_even == false) {
 | 
						|
    const bool is_clamp = !(t->flag & T_ALT_TRANSFORM);
 | 
						|
    if (is_clamp) {
 | 
						|
      const int side_index = (perc < 0.0f);
 | 
						|
      const float perc_final = fabsf(perc);
 | 
						|
      FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
        EdgeSlideData *sld = tc->custom.mode.data;
 | 
						|
        TransDataEdgeSlideVert *sv = sld->sv;
 | 
						|
        for (int i = 0; i < sld->totsv; i++, sv++) {
 | 
						|
          madd_v3_v3v3fl(sv->v->co, sv->v_co_orig, sv->dir_side[side_index], perc_final);
 | 
						|
        }
 | 
						|
        sld->curr_side_unclamp = side_index;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      const float perc_init = fabsf(perc) *
 | 
						|
                              ((sld_active->curr_side_unclamp == (perc < 0.0f)) ? 1 : -1);
 | 
						|
      const int side_index = sld_active->curr_side_unclamp;
 | 
						|
      FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
        EdgeSlideData *sld = tc->custom.mode.data;
 | 
						|
        TransDataEdgeSlideVert *sv = sld->sv;
 | 
						|
        for (int i = 0; i < sld->totsv; i++, sv++) {
 | 
						|
          float dir_flip[3];
 | 
						|
          float perc_final = perc_init;
 | 
						|
          if (!is_zero_v3(sv->dir_side[side_index])) {
 | 
						|
            copy_v3_v3(dir_flip, sv->dir_side[side_index]);
 | 
						|
          }
 | 
						|
          else {
 | 
						|
            copy_v3_v3(dir_flip, sv->dir_side[!side_index]);
 | 
						|
            perc_final *= -1;
 | 
						|
          }
 | 
						|
          madd_v3_v3v3fl(sv->v->co, sv->v_co_orig, dir_flip, perc_final);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    /**
 | 
						|
     * Implementation note, even mode ignores the starting positions and uses
 | 
						|
     * only the a/b verts, this could be changed/improved so the distance is
 | 
						|
     * still met but the verts are moved along their original path (which may not be straight),
 | 
						|
     * however how it works now is OK and matches 2.4x - Campbell
 | 
						|
     *
 | 
						|
     * \note `len_v3v3(curr_sv->dir_side[0], curr_sv->dir_side[1])`
 | 
						|
     * is the same as the distance between the original vert locations,
 | 
						|
     * same goes for the lines below.
 | 
						|
     */
 | 
						|
    TransDataEdgeSlideVert *curr_sv = &sld_active->sv[sld_active->curr_sv_index];
 | 
						|
    const float curr_length_perc = curr_sv->edge_len *
 | 
						|
                                   (((slp->flipped ? perc : -perc) + 1.0f) / 2.0f);
 | 
						|
 | 
						|
    float co_a[3];
 | 
						|
    float co_b[3];
 | 
						|
 | 
						|
    FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
      EdgeSlideData *sld = tc->custom.mode.data;
 | 
						|
      TransDataEdgeSlideVert *sv = sld->sv;
 | 
						|
      for (int i = 0; i < sld->totsv; i++, sv++) {
 | 
						|
        if (sv->edge_len > FLT_EPSILON) {
 | 
						|
          const float fac = min_ff(sv->edge_len, curr_length_perc) / sv->edge_len;
 | 
						|
 | 
						|
          add_v3_v3v3(co_a, sv->v_co_orig, sv->dir_side[0]);
 | 
						|
          add_v3_v3v3(co_b, sv->v_co_orig, sv->dir_side[1]);
 | 
						|
 | 
						|
          if (slp->flipped) {
 | 
						|
            interp_line_v3_v3v3v3(sv->v->co, co_b, sv->v_co_orig, co_a, fac);
 | 
						|
          }
 | 
						|
          else {
 | 
						|
            interp_line_v3_v3v3v3(sv->v->co, co_a, sv->v_co_orig, co_b, fac);
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
  size_t ofs = 0;
 | 
						|
  float final;
 | 
						|
  EdgeSlideParams *slp = t->custom.mode.data;
 | 
						|
  bool flipped = slp->flipped;
 | 
						|
  bool use_even = slp->use_even;
 | 
						|
  const bool is_clamp = !(t->flag & T_ALT_TRANSFORM);
 | 
						|
  const bool is_constrained = !(is_clamp == false || hasNumInput(&t->num));
 | 
						|
 | 
						|
  final = t->values[0];
 | 
						|
 | 
						|
  snapGridIncrement(t, &final);
 | 
						|
 | 
						|
  /* only do this so out of range values are not displayed */
 | 
						|
  if (is_constrained) {
 | 
						|
    CLAMP(final, -1.0f, 1.0f);
 | 
						|
  }
 | 
						|
 | 
						|
  applyNumInput(&t->num, &final);
 | 
						|
 | 
						|
  t->values[0] = final;
 | 
						|
 | 
						|
  /* header string */
 | 
						|
  ofs += BLI_strncpy_rlen(str + ofs, IFACE_("Edge Slide: "), sizeof(str) - ofs);
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    char c[NUM_STR_REP_LEN];
 | 
						|
    outputNumInput(&(t->num), c, &t->scene->unit);
 | 
						|
    ofs += BLI_strncpy_rlen(str + ofs, &c[0], sizeof(str) - ofs);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, "%.4f ", final);
 | 
						|
  }
 | 
						|
  ofs += BLI_snprintf(
 | 
						|
      str + ofs, sizeof(str) - ofs, IFACE_("(E)ven: %s, "), WM_bool_as_string(use_even));
 | 
						|
  if (use_even) {
 | 
						|
    ofs += BLI_snprintf(
 | 
						|
        str + ofs, sizeof(str) - ofs, IFACE_("(F)lipped: %s, "), WM_bool_as_string(flipped));
 | 
						|
  }
 | 
						|
  ofs += BLI_snprintf(
 | 
						|
      str + ofs, sizeof(str) - ofs, IFACE_("Alt or (C)lamp: %s"), WM_bool_as_string(is_clamp));
 | 
						|
  /* done with header string */
 | 
						|
 | 
						|
  /* do stuff here */
 | 
						|
  doEdgeSlide(t, final);
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Vert Slide) */
 | 
						|
 | 
						|
/** \name Transform Vert Slide
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void calcVertSlideCustomPoints(struct TransInfo *t)
 | 
						|
{
 | 
						|
  VertSlideParams *slp = t->custom.mode.data;
 | 
						|
  VertSlideData *sld = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data;
 | 
						|
  TransDataVertSlideVert *sv = &sld->sv[sld->curr_sv_index];
 | 
						|
 | 
						|
  const float *co_orig_3d = sv->co_orig_3d;
 | 
						|
  const float *co_curr_3d = sv->co_link_orig_3d[sv->co_link_curr];
 | 
						|
 | 
						|
  float co_curr_2d[2], co_orig_2d[2];
 | 
						|
 | 
						|
  int mval_ofs[2], mval_start[2], mval_end[2];
 | 
						|
 | 
						|
  ED_view3d_project_float_v2_m4(t->ar, co_orig_3d, co_orig_2d, sld->proj_mat);
 | 
						|
  ED_view3d_project_float_v2_m4(t->ar, co_curr_3d, co_curr_2d, sld->proj_mat);
 | 
						|
 | 
						|
  ARRAY_SET_ITEMS(mval_ofs, t->mouse.imval[0] - co_orig_2d[0], t->mouse.imval[1] - co_orig_2d[1]);
 | 
						|
  ARRAY_SET_ITEMS(mval_start, co_orig_2d[0] + mval_ofs[0], co_orig_2d[1] + mval_ofs[1]);
 | 
						|
  ARRAY_SET_ITEMS(mval_end, co_curr_2d[0] + mval_ofs[0], co_curr_2d[1] + mval_ofs[1]);
 | 
						|
 | 
						|
  if (slp->flipped && slp->use_even) {
 | 
						|
    setCustomPoints(t, &t->mouse, mval_start, mval_end);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    setCustomPoints(t, &t->mouse, mval_end, mval_start);
 | 
						|
  }
 | 
						|
 | 
						|
  /* setCustomPoints isn't normally changing as the mouse moves,
 | 
						|
   * in this case apply mouse input immediately so we don't refresh
 | 
						|
   * with the value from the previous points */
 | 
						|
  applyMouseInput(t, &t->mouse, t->mval, t->values);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Run once when initializing vert slide to find the reference edge
 | 
						|
 */
 | 
						|
static void calcVertSlideMouseActiveVert(struct TransInfo *t, const int mval[2])
 | 
						|
{
 | 
						|
  /* Active object may have no selected vertices. */
 | 
						|
  VertSlideData *sld = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data;
 | 
						|
  float mval_fl[2] = {UNPACK2(mval)};
 | 
						|
  TransDataVertSlideVert *sv;
 | 
						|
 | 
						|
  /* set the vertex to use as a reference for the mouse direction 'curr_sv_index' */
 | 
						|
  float dist_sq = 0.0f;
 | 
						|
  float dist_min_sq = FLT_MAX;
 | 
						|
  int i;
 | 
						|
 | 
						|
  for (i = 0, sv = sld->sv; i < sld->totsv; i++, sv++) {
 | 
						|
    float co_2d[2];
 | 
						|
 | 
						|
    ED_view3d_project_float_v2_m4(t->ar, sv->co_orig_3d, co_2d, sld->proj_mat);
 | 
						|
 | 
						|
    dist_sq = len_squared_v2v2(mval_fl, co_2d);
 | 
						|
    if (dist_sq < dist_min_sq) {
 | 
						|
      dist_min_sq = dist_sq;
 | 
						|
      sld->curr_sv_index = i;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Run while moving the mouse to slide along the edge matching the mouse direction
 | 
						|
 */
 | 
						|
static void calcVertSlideMouseActiveEdges(struct TransInfo *t, const int mval[2])
 | 
						|
{
 | 
						|
  VertSlideData *sld = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data;
 | 
						|
  float imval_fl[2] = {UNPACK2(t->mouse.imval)};
 | 
						|
  float mval_fl[2] = {UNPACK2(mval)};
 | 
						|
 | 
						|
  float dir[3];
 | 
						|
  TransDataVertSlideVert *sv;
 | 
						|
  int i;
 | 
						|
 | 
						|
  /* note: we could save a matrix-multiply for each vertex
 | 
						|
   * by finding the closest edge in local-space.
 | 
						|
   * However this skews the outcome with non-uniform-scale. */
 | 
						|
 | 
						|
  /* first get the direction of the original mouse position */
 | 
						|
  sub_v2_v2v2(dir, imval_fl, mval_fl);
 | 
						|
  ED_view3d_win_to_delta(t->ar, dir, dir, t->zfac);
 | 
						|
  normalize_v3(dir);
 | 
						|
 | 
						|
  for (i = 0, sv = sld->sv; i < sld->totsv; i++, sv++) {
 | 
						|
    if (sv->co_link_tot > 1) {
 | 
						|
      float dir_dot_best = -FLT_MAX;
 | 
						|
      int co_link_curr_best = -1;
 | 
						|
      int j;
 | 
						|
 | 
						|
      for (j = 0; j < sv->co_link_tot; j++) {
 | 
						|
        float tdir[3];
 | 
						|
        float dir_dot;
 | 
						|
 | 
						|
        sub_v3_v3v3(tdir, sv->co_orig_3d, sv->co_link_orig_3d[j]);
 | 
						|
        mul_mat3_m4_v3(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat, tdir);
 | 
						|
        project_plane_v3_v3v3(tdir, tdir, t->viewinv[2]);
 | 
						|
 | 
						|
        normalize_v3(tdir);
 | 
						|
        dir_dot = dot_v3v3(dir, tdir);
 | 
						|
        if (dir_dot > dir_dot_best) {
 | 
						|
          dir_dot_best = dir_dot;
 | 
						|
          co_link_curr_best = j;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      if (co_link_curr_best != -1) {
 | 
						|
        sv->co_link_curr = co_link_curr_best;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static bool createVertSlideVerts(TransInfo *t, TransDataContainer *tc)
 | 
						|
{
 | 
						|
  BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
 | 
						|
  BMesh *bm = em->bm;
 | 
						|
  BMIter iter;
 | 
						|
  BMIter eiter;
 | 
						|
  BMEdge *e;
 | 
						|
  BMVert *v;
 | 
						|
  TransDataVertSlideVert *sv_array;
 | 
						|
  VertSlideData *sld = MEM_callocN(sizeof(*sld), "sld");
 | 
						|
  int j;
 | 
						|
 | 
						|
  slide_origdata_init_flag(t, tc, &sld->orig_data);
 | 
						|
 | 
						|
  sld->curr_sv_index = 0;
 | 
						|
 | 
						|
  j = 0;
 | 
						|
  BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
 | 
						|
    bool ok = false;
 | 
						|
    if (BM_elem_flag_test(v, BM_ELEM_SELECT) && v->e) {
 | 
						|
      BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
 | 
						|
        if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
 | 
						|
          ok = true;
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (ok) {
 | 
						|
      BM_elem_flag_enable(v, BM_ELEM_TAG);
 | 
						|
      j += 1;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      BM_elem_flag_disable(v, BM_ELEM_TAG);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (!j) {
 | 
						|
    MEM_freeN(sld);
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  sv_array = MEM_callocN(sizeof(TransDataVertSlideVert) * j, "sv_array");
 | 
						|
 | 
						|
  j = 0;
 | 
						|
  BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
 | 
						|
    if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
 | 
						|
      int k;
 | 
						|
      sv_array[j].v = v;
 | 
						|
      copy_v3_v3(sv_array[j].co_orig_3d, v->co);
 | 
						|
 | 
						|
      k = 0;
 | 
						|
      BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
 | 
						|
        if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
 | 
						|
          k++;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      sv_array[j].co_link_orig_3d = MEM_mallocN(sizeof(*sv_array[j].co_link_orig_3d) * k,
 | 
						|
                                                __func__);
 | 
						|
      sv_array[j].co_link_tot = k;
 | 
						|
 | 
						|
      k = 0;
 | 
						|
      BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
 | 
						|
        if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
 | 
						|
          BMVert *v_other = BM_edge_other_vert(e, v);
 | 
						|
          copy_v3_v3(sv_array[j].co_link_orig_3d[k], v_other->co);
 | 
						|
          k++;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      j++;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  sld->sv = sv_array;
 | 
						|
  sld->totsv = j;
 | 
						|
 | 
						|
  bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
 | 
						|
  slide_origdata_init_data(tc, &sld->orig_data);
 | 
						|
  slide_origdata_create_data(
 | 
						|
      tc, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv);
 | 
						|
 | 
						|
  sld->em = em;
 | 
						|
 | 
						|
  tc->custom.mode.data = sld;
 | 
						|
 | 
						|
  /* most likely will be set below */
 | 
						|
  unit_m4(sld->proj_mat);
 | 
						|
 | 
						|
  if (t->spacetype == SPACE_VIEW3D) {
 | 
						|
    /* view vars */
 | 
						|
    RegionView3D *rv3d = NULL;
 | 
						|
    ARegion *ar = t->ar;
 | 
						|
 | 
						|
    rv3d = ar ? ar->regiondata : NULL;
 | 
						|
    if (rv3d) {
 | 
						|
      ED_view3d_ob_project_mat_get(rv3d, tc->obedit, sld->proj_mat);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* XXX, calc vert slide across all objects */
 | 
						|
  if (tc == t->data_container) {
 | 
						|
    calcVertSlideMouseActiveVert(t, t->mval);
 | 
						|
    calcVertSlideMouseActiveEdges(t, t->mval);
 | 
						|
  }
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
void projectVertSlideData(TransInfo *t, bool is_final)
 | 
						|
{
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    VertSlideData *sld = tc->custom.mode.data;
 | 
						|
    SlideOrigData *sod = &sld->orig_data;
 | 
						|
    if (sod->use_origfaces == true) {
 | 
						|
      slide_origdata_interp_data(tc->obedit,
 | 
						|
                                 sod,
 | 
						|
                                 (TransDataGenericSlideVert *)sld->sv,
 | 
						|
                                 sizeof(*sld->sv),
 | 
						|
                                 sld->totsv,
 | 
						|
                                 is_final);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void freeVertSlideTempFaces(VertSlideData *sld)
 | 
						|
{
 | 
						|
  slide_origdata_free_date(&sld->orig_data);
 | 
						|
}
 | 
						|
 | 
						|
void freeVertSlideVerts(TransInfo *UNUSED(t),
 | 
						|
                        TransDataContainer *UNUSED(tc),
 | 
						|
                        TransCustomData *custom_data)
 | 
						|
{
 | 
						|
  VertSlideData *sld = custom_data->data;
 | 
						|
 | 
						|
  if (!sld) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  freeVertSlideTempFaces(sld);
 | 
						|
 | 
						|
  bmesh_edit_end(sld->em->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
 | 
						|
 | 
						|
  if (sld->totsv > 0) {
 | 
						|
    TransDataVertSlideVert *sv = sld->sv;
 | 
						|
    int i = 0;
 | 
						|
    for (i = 0; i < sld->totsv; i++, sv++) {
 | 
						|
      MEM_freeN(sv->co_link_orig_3d);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  MEM_freeN(sld->sv);
 | 
						|
  MEM_freeN(sld);
 | 
						|
 | 
						|
  custom_data->data = NULL;
 | 
						|
}
 | 
						|
 | 
						|
static void initVertSlide_ex(TransInfo *t, bool use_even, bool flipped, bool use_clamp)
 | 
						|
{
 | 
						|
 | 
						|
  t->mode = TFM_VERT_SLIDE;
 | 
						|
  t->transform = applyVertSlide;
 | 
						|
  t->handleEvent = handleEventVertSlide;
 | 
						|
 | 
						|
  {
 | 
						|
    VertSlideParams *slp = MEM_callocN(sizeof(*slp), __func__);
 | 
						|
    slp->use_even = use_even;
 | 
						|
    slp->flipped = flipped;
 | 
						|
    slp->perc = 0.0f;
 | 
						|
 | 
						|
    if (!use_clamp) {
 | 
						|
      t->flag |= T_ALT_TRANSFORM;
 | 
						|
    }
 | 
						|
 | 
						|
    t->custom.mode.data = slp;
 | 
						|
    t->custom.mode.use_free = true;
 | 
						|
  }
 | 
						|
 | 
						|
  bool ok = false;
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    ok |= createVertSlideVerts(t, tc);
 | 
						|
    VertSlideData *sld = tc->custom.mode.data;
 | 
						|
    if (sld) {
 | 
						|
      tc->custom.mode.free_cb = freeVertSlideVerts;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (ok == false) {
 | 
						|
    t->state = TRANS_CANCEL;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  /* set custom point first if you want value to be initialized by init */
 | 
						|
  calcVertSlideCustomPoints(t);
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_CUSTOM_RATIO);
 | 
						|
 | 
						|
  t->idx_max = 0;
 | 
						|
  t->num.idx_max = 0;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = 0.1f;
 | 
						|
  t->snap[2] = t->snap[1] * 0.1f;
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_type[0] = B_UNIT_NONE;
 | 
						|
 | 
						|
  t->flag |= T_NO_CONSTRAINT | T_NO_PROJECT;
 | 
						|
}
 | 
						|
 | 
						|
static void initVertSlide(TransInfo *t)
 | 
						|
{
 | 
						|
  initVertSlide_ex(t, false, false, true);
 | 
						|
}
 | 
						|
 | 
						|
static eRedrawFlag handleEventVertSlide(struct TransInfo *t, const struct wmEvent *event)
 | 
						|
{
 | 
						|
  if (t->mode == TFM_VERT_SLIDE) {
 | 
						|
    VertSlideParams *slp = t->custom.mode.data;
 | 
						|
 | 
						|
    if (slp) {
 | 
						|
      switch (event->type) {
 | 
						|
        case EKEY:
 | 
						|
          if (event->val == KM_PRESS) {
 | 
						|
            slp->use_even = !slp->use_even;
 | 
						|
            if (slp->flipped) {
 | 
						|
              calcVertSlideCustomPoints(t);
 | 
						|
            }
 | 
						|
            return TREDRAW_HARD;
 | 
						|
          }
 | 
						|
          break;
 | 
						|
        case FKEY:
 | 
						|
          if (event->val == KM_PRESS) {
 | 
						|
            slp->flipped = !slp->flipped;
 | 
						|
            calcVertSlideCustomPoints(t);
 | 
						|
            return TREDRAW_HARD;
 | 
						|
          }
 | 
						|
          break;
 | 
						|
        case CKEY:
 | 
						|
          /* use like a modifier key */
 | 
						|
          if (event->val == KM_PRESS) {
 | 
						|
            t->flag ^= T_ALT_TRANSFORM;
 | 
						|
            calcVertSlideCustomPoints(t);
 | 
						|
            return TREDRAW_HARD;
 | 
						|
          }
 | 
						|
          break;
 | 
						|
#if 0
 | 
						|
        case EVT_MODAL_MAP:
 | 
						|
          switch (event->val) {
 | 
						|
            case TFM_MODAL_EDGESLIDE_DOWN:
 | 
						|
              sld->curr_sv_index = ((sld->curr_sv_index - 1) + sld->totsv) % sld->totsv;
 | 
						|
              break;
 | 
						|
            case TFM_MODAL_EDGESLIDE_UP:
 | 
						|
              sld->curr_sv_index = (sld->curr_sv_index + 1) % sld->totsv;
 | 
						|
              break;
 | 
						|
          }
 | 
						|
          break;
 | 
						|
#endif
 | 
						|
        case MOUSEMOVE: {
 | 
						|
          /* don't recalculate the best edge */
 | 
						|
          const bool is_clamp = !(t->flag & T_ALT_TRANSFORM);
 | 
						|
          if (is_clamp) {
 | 
						|
            calcVertSlideMouseActiveEdges(t, event->mval);
 | 
						|
          }
 | 
						|
          calcVertSlideCustomPoints(t);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
        default:
 | 
						|
          break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return TREDRAW_NOTHING;
 | 
						|
}
 | 
						|
 | 
						|
static void drawVertSlide(TransInfo *t)
 | 
						|
{
 | 
						|
  if ((t->mode == TFM_VERT_SLIDE) && TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data) {
 | 
						|
    const VertSlideParams *slp = t->custom.mode.data;
 | 
						|
    VertSlideData *sld = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data;
 | 
						|
    const bool is_clamp = !(t->flag & T_ALT_TRANSFORM);
 | 
						|
 | 
						|
    /* Non-Prop mode */
 | 
						|
    {
 | 
						|
      TransDataVertSlideVert *curr_sv = &sld->sv[sld->curr_sv_index];
 | 
						|
      TransDataVertSlideVert *sv;
 | 
						|
      const float ctrl_size = UI_GetThemeValuef(TH_FACEDOT_SIZE) + 1.5f;
 | 
						|
      const float line_size = UI_GetThemeValuef(TH_OUTLINE_WIDTH) + 0.5f;
 | 
						|
      const int alpha_shade = -160;
 | 
						|
      int i;
 | 
						|
 | 
						|
      GPU_depth_test(false);
 | 
						|
 | 
						|
      GPU_blend(true);
 | 
						|
      GPU_blend_set_func_separate(
 | 
						|
          GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
 | 
						|
 | 
						|
      GPU_matrix_push();
 | 
						|
      GPU_matrix_mul(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat);
 | 
						|
 | 
						|
      GPU_line_width(line_size);
 | 
						|
 | 
						|
      const uint shdr_pos = GPU_vertformat_attr_add(
 | 
						|
          immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
 | 
						|
 | 
						|
      immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
 | 
						|
      immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade);
 | 
						|
 | 
						|
      immBegin(GPU_PRIM_LINES, sld->totsv * 2);
 | 
						|
      if (is_clamp) {
 | 
						|
        sv = sld->sv;
 | 
						|
        for (i = 0; i < sld->totsv; i++, sv++) {
 | 
						|
          immVertex3fv(shdr_pos, sv->co_orig_3d);
 | 
						|
          immVertex3fv(shdr_pos, sv->co_link_orig_3d[sv->co_link_curr]);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        sv = sld->sv;
 | 
						|
        for (i = 0; i < sld->totsv; i++, sv++) {
 | 
						|
          float a[3], b[3];
 | 
						|
          sub_v3_v3v3(a, sv->co_link_orig_3d[sv->co_link_curr], sv->co_orig_3d);
 | 
						|
          mul_v3_fl(a, 100.0f);
 | 
						|
          negate_v3_v3(b, a);
 | 
						|
          add_v3_v3(a, sv->co_orig_3d);
 | 
						|
          add_v3_v3(b, sv->co_orig_3d);
 | 
						|
 | 
						|
          immVertex3fv(shdr_pos, a);
 | 
						|
          immVertex3fv(shdr_pos, b);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      immEnd();
 | 
						|
 | 
						|
      GPU_point_size(ctrl_size);
 | 
						|
 | 
						|
      immBegin(GPU_PRIM_POINTS, 1);
 | 
						|
      immVertex3fv(shdr_pos,
 | 
						|
                   (slp->flipped && slp->use_even) ?
 | 
						|
                       curr_sv->co_link_orig_3d[curr_sv->co_link_curr] :
 | 
						|
                       curr_sv->co_orig_3d);
 | 
						|
      immEnd();
 | 
						|
 | 
						|
      immUnbindProgram();
 | 
						|
 | 
						|
      /* direction from active vertex! */
 | 
						|
      if ((t->mval[0] != t->mouse.imval[0]) || (t->mval[1] != t->mouse.imval[1])) {
 | 
						|
        float zfac;
 | 
						|
        float mval_ofs[2];
 | 
						|
        float co_orig_3d[3];
 | 
						|
        float co_dest_3d[3];
 | 
						|
 | 
						|
        mval_ofs[0] = t->mval[0] - t->mouse.imval[0];
 | 
						|
        mval_ofs[1] = t->mval[1] - t->mouse.imval[1];
 | 
						|
 | 
						|
        mul_v3_m4v3(
 | 
						|
            co_orig_3d, TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat, curr_sv->co_orig_3d);
 | 
						|
        zfac = ED_view3d_calc_zfac(t->ar->regiondata, co_orig_3d, NULL);
 | 
						|
 | 
						|
        ED_view3d_win_to_delta(t->ar, mval_ofs, co_dest_3d, zfac);
 | 
						|
 | 
						|
        invert_m4_m4(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->imat,
 | 
						|
                     TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat);
 | 
						|
        mul_mat3_m4_v3(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->imat, co_dest_3d);
 | 
						|
 | 
						|
        add_v3_v3(co_dest_3d, curr_sv->co_orig_3d);
 | 
						|
 | 
						|
        GPU_line_width(1.0f);
 | 
						|
 | 
						|
        immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
 | 
						|
 | 
						|
        float viewport_size[4];
 | 
						|
        GPU_viewport_size_get_f(viewport_size);
 | 
						|
        immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
 | 
						|
 | 
						|
        immUniform1i("colors_len", 0); /* "simple" mode */
 | 
						|
        immUniformColor4f(1.0f, 1.0f, 1.0f, 1.0f);
 | 
						|
        immUniform1f("dash_width", 6.0f);
 | 
						|
        immUniform1f("dash_factor", 0.5f);
 | 
						|
 | 
						|
        immBegin(GPU_PRIM_LINES, 2);
 | 
						|
        immVertex3fv(shdr_pos, curr_sv->co_orig_3d);
 | 
						|
        immVertex3fv(shdr_pos, co_dest_3d);
 | 
						|
        immEnd();
 | 
						|
 | 
						|
        immUnbindProgram();
 | 
						|
      }
 | 
						|
 | 
						|
      GPU_matrix_pop();
 | 
						|
 | 
						|
      GPU_depth_test(true);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void doVertSlide(TransInfo *t, float perc)
 | 
						|
{
 | 
						|
  VertSlideParams *slp = t->custom.mode.data;
 | 
						|
 | 
						|
  slp->perc = perc;
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    VertSlideData *sld = tc->custom.mode.data;
 | 
						|
    TransDataVertSlideVert *svlist = sld->sv, *sv;
 | 
						|
    int i;
 | 
						|
 | 
						|
    sv = svlist;
 | 
						|
 | 
						|
    if (slp->use_even == false) {
 | 
						|
      for (i = 0; i < sld->totsv; i++, sv++) {
 | 
						|
        interp_v3_v3v3(sv->v->co, sv->co_orig_3d, sv->co_link_orig_3d[sv->co_link_curr], perc);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      TransDataVertSlideVert *sv_curr = &sld->sv[sld->curr_sv_index];
 | 
						|
      const float edge_len_curr = len_v3v3(sv_curr->co_orig_3d,
 | 
						|
                                           sv_curr->co_link_orig_3d[sv_curr->co_link_curr]);
 | 
						|
      const float tperc = perc * edge_len_curr;
 | 
						|
 | 
						|
      for (i = 0; i < sld->totsv; i++, sv++) {
 | 
						|
        float edge_len;
 | 
						|
        float dir[3];
 | 
						|
 | 
						|
        sub_v3_v3v3(dir, sv->co_link_orig_3d[sv->co_link_curr], sv->co_orig_3d);
 | 
						|
        edge_len = normalize_v3(dir);
 | 
						|
 | 
						|
        if (edge_len > FLT_EPSILON) {
 | 
						|
          if (slp->flipped) {
 | 
						|
            madd_v3_v3v3fl(sv->v->co, sv->co_link_orig_3d[sv->co_link_curr], dir, -tperc);
 | 
						|
          }
 | 
						|
          else {
 | 
						|
            madd_v3_v3v3fl(sv->v->co, sv->co_orig_3d, dir, tperc);
 | 
						|
          }
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          copy_v3_v3(sv->v->co, sv->co_orig_3d);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
  size_t ofs = 0;
 | 
						|
  float final;
 | 
						|
  VertSlideParams *slp = t->custom.mode.data;
 | 
						|
  const bool flipped = slp->flipped;
 | 
						|
  const bool use_even = slp->use_even;
 | 
						|
  const bool is_clamp = !(t->flag & T_ALT_TRANSFORM);
 | 
						|
  const bool is_constrained = !(is_clamp == false || hasNumInput(&t->num));
 | 
						|
 | 
						|
  final = t->values[0];
 | 
						|
 | 
						|
  snapGridIncrement(t, &final);
 | 
						|
 | 
						|
  /* only do this so out of range values are not displayed */
 | 
						|
  if (is_constrained) {
 | 
						|
    CLAMP(final, 0.0f, 1.0f);
 | 
						|
  }
 | 
						|
 | 
						|
  applyNumInput(&t->num, &final);
 | 
						|
 | 
						|
  t->values[0] = final;
 | 
						|
 | 
						|
  /* header string */
 | 
						|
  ofs += BLI_strncpy_rlen(str + ofs, IFACE_("Vert Slide: "), sizeof(str) - ofs);
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    char c[NUM_STR_REP_LEN];
 | 
						|
    outputNumInput(&(t->num), c, &t->scene->unit);
 | 
						|
    ofs += BLI_strncpy_rlen(str + ofs, &c[0], sizeof(str) - ofs);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, "%.4f ", final);
 | 
						|
  }
 | 
						|
  ofs += BLI_snprintf(
 | 
						|
      str + ofs, sizeof(str) - ofs, IFACE_("(E)ven: %s, "), WM_bool_as_string(use_even));
 | 
						|
  if (use_even) {
 | 
						|
    ofs += BLI_snprintf(
 | 
						|
        str + ofs, sizeof(str) - ofs, IFACE_("(F)lipped: %s, "), WM_bool_as_string(flipped));
 | 
						|
  }
 | 
						|
  ofs += BLI_snprintf(
 | 
						|
      str + ofs, sizeof(str) - ofs, IFACE_("Alt or (C)lamp: %s"), WM_bool_as_string(is_clamp));
 | 
						|
  /* done with header string */
 | 
						|
 | 
						|
  /* do stuff here */
 | 
						|
  doVertSlide(t, final);
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (EditBone Roll) */
 | 
						|
 | 
						|
/** \name Transform EditBone Roll
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initBoneRoll(TransInfo *t)
 | 
						|
{
 | 
						|
  t->mode = TFM_BONE_ROLL;
 | 
						|
  t->transform = applyBoneRoll;
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_ANGLE);
 | 
						|
 | 
						|
  t->idx_max = 0;
 | 
						|
  t->num.idx_max = 0;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = DEG2RAD(5.0);
 | 
						|
  t->snap[2] = DEG2RAD(1.0);
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_use_radians = (t->scene->unit.system_rotation == USER_UNIT_ROT_RADIANS);
 | 
						|
  t->num.unit_type[0] = B_UNIT_ROTATION;
 | 
						|
 | 
						|
  t->flag |= T_NO_CONSTRAINT | T_NO_PROJECT;
 | 
						|
}
 | 
						|
 | 
						|
static void applyBoneRoll(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  float final;
 | 
						|
 | 
						|
  final = t->values[0];
 | 
						|
 | 
						|
  snapGridIncrement(t, &final);
 | 
						|
 | 
						|
  applyNumInput(&t->num, &final);
 | 
						|
 | 
						|
  t->values[0] = final;
 | 
						|
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    char c[NUM_STR_REP_LEN];
 | 
						|
 | 
						|
    outputNumInput(&(t->num), c, &t->scene->unit);
 | 
						|
 | 
						|
    BLI_snprintf(str, sizeof(str), IFACE_("Roll: %s"), &c[0]);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    BLI_snprintf(str, sizeof(str), IFACE_("Roll: %.2f"), RAD2DEGF(final));
 | 
						|
  }
 | 
						|
 | 
						|
  /* set roll values */
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      *(td->val) = td->ival - final;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Bake-Time) */
 | 
						|
 | 
						|
/** \name Transform Bake-Time
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initBakeTime(TransInfo *t)
 | 
						|
{
 | 
						|
  t->transform = applyBakeTime;
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_NONE);
 | 
						|
 | 
						|
  t->idx_max = 0;
 | 
						|
  t->num.idx_max = 0;
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = 1.0f;
 | 
						|
  t->snap[2] = t->snap[1] * 0.1f;
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_type[0] = B_UNIT_NONE; /* Don't think this uses units? */
 | 
						|
}
 | 
						|
 | 
						|
static void applyBakeTime(TransInfo *t, const int mval[2])
 | 
						|
{
 | 
						|
  float time;
 | 
						|
  int i;
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  float fac = 0.1f;
 | 
						|
 | 
						|
  /* XXX, disable precision for now,
 | 
						|
   * this isn't even accessible by the user */
 | 
						|
#if 0
 | 
						|
  if (t->mouse.precision) {
 | 
						|
    /* calculate ratio for shiftkey pos, and for total, and blend these for precision */
 | 
						|
    time = (float)(t->center2d[0] - t->mouse.precision_mval[0]) * fac;
 | 
						|
    time += 0.1f * ((float)(t->center2d[0] * fac - mval[0]) - time);
 | 
						|
  }
 | 
						|
  else
 | 
						|
#endif
 | 
						|
  {
 | 
						|
    time = (float)(t->center2d[0] - mval[0]) * fac;
 | 
						|
  }
 | 
						|
 | 
						|
  snapGridIncrement(t, &time);
 | 
						|
 | 
						|
  applyNumInput(&t->num, &time);
 | 
						|
 | 
						|
  /* header print for NumInput */
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    char c[NUM_STR_REP_LEN];
 | 
						|
 | 
						|
    outputNumInput(&(t->num), c, &t->scene->unit);
 | 
						|
 | 
						|
    if (time >= 0.0f) {
 | 
						|
      BLI_snprintf(str, sizeof(str), IFACE_("Time: +%s %s"), c, t->proptext);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      BLI_snprintf(str, sizeof(str), IFACE_("Time: %s %s"), c, t->proptext);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    /* default header print */
 | 
						|
    if (time >= 0.0f) {
 | 
						|
      BLI_snprintf(str, sizeof(str), IFACE_("Time: +%.3f %s"), time, t->proptext);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      BLI_snprintf(str, sizeof(str), IFACE_("Time: %.3f %s"), time, t->proptext);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->val) {
 | 
						|
        *td->val = td->ival + time * td->factor;
 | 
						|
        if (td->ext->size && *td->val < *td->ext->size) {
 | 
						|
          *td->val = *td->ext->size;
 | 
						|
        }
 | 
						|
        if (td->ext->quat && *td->val > *td->ext->quat) {
 | 
						|
          *td->val = *td->ext->quat;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Mirror) */
 | 
						|
 | 
						|
/** \name Transform Mirror
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initMirror(TransInfo *t)
 | 
						|
{
 | 
						|
  t->transform = applyMirror;
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_NONE);
 | 
						|
 | 
						|
  t->flag |= T_NULL_ONE;
 | 
						|
  if ((t->flag & T_EDIT) == 0) {
 | 
						|
    t->flag |= T_NO_ZERO;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void applyMirror(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  float size[3], mat[3][3];
 | 
						|
  int i;
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  /*
 | 
						|
   * OPTIMIZATION:
 | 
						|
   * This still recalcs transformation on mouse move
 | 
						|
   * while it should only recalc on constraint change
 | 
						|
   * */
 | 
						|
 | 
						|
  /* if an axis has been selected */
 | 
						|
  if (t->con.mode & CON_APPLY) {
 | 
						|
    size[0] = size[1] = size[2] = -1;
 | 
						|
 | 
						|
    size_to_mat3(mat, size);
 | 
						|
 | 
						|
    if (t->con.applySize) {
 | 
						|
      t->con.applySize(t, NULL, NULL, mat);
 | 
						|
    }
 | 
						|
 | 
						|
    BLI_snprintf(str, sizeof(str), IFACE_("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_NOACTION) {
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        if (td->flag & TD_SKIP) {
 | 
						|
          continue;
 | 
						|
        }
 | 
						|
 | 
						|
        ElementResize(t, tc, td, mat);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    recalcData(t);
 | 
						|
 | 
						|
    ED_area_status_text(t->sa, str);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    size[0] = size[1] = size[2] = 1;
 | 
						|
 | 
						|
    size_to_mat3(mat, size);
 | 
						|
 | 
						|
    FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
      TransData *td = tc->data;
 | 
						|
      for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
        if (td->flag & TD_NOACTION) {
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        if (td->flag & TD_SKIP) {
 | 
						|
          continue;
 | 
						|
        }
 | 
						|
 | 
						|
        ElementResize(t, tc, td, mat);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    recalcData(t);
 | 
						|
 | 
						|
    if (t->flag & T_2D_EDIT) {
 | 
						|
      ED_area_status_text(t->sa, IFACE_("Select a mirror axis (X, Y)"));
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      ED_area_status_text(t->sa, IFACE_("Select a mirror axis (X, Y, Z)"));
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Align) */
 | 
						|
 | 
						|
/** \name Transform Align
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initAlign(TransInfo *t)
 | 
						|
{
 | 
						|
  t->flag |= T_NO_CONSTRAINT;
 | 
						|
 | 
						|
  t->transform = applyAlign;
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_NONE);
 | 
						|
}
 | 
						|
 | 
						|
static void applyAlign(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  float center[3];
 | 
						|
  int i;
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    /* saving original center */
 | 
						|
    copy_v3_v3(center, tc->center_local);
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      float mat[3][3], invmat[3][3];
 | 
						|
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      /* around local centers */
 | 
						|
      if (t->flag & (T_OBJECT | T_POSE)) {
 | 
						|
        copy_v3_v3(tc->center_local, td->center);
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        if (t->settings->selectmode & SCE_SELECT_FACE) {
 | 
						|
          copy_v3_v3(tc->center_local, td->center);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      invert_m3_m3(invmat, td->axismtx);
 | 
						|
 | 
						|
      mul_m3_m3m3(mat, t->spacemtx, invmat);
 | 
						|
 | 
						|
      ElementRotation(t, tc, td, mat, t->around);
 | 
						|
    }
 | 
						|
    /* restoring original center */
 | 
						|
    copy_v3_v3(tc->center_local, center);
 | 
						|
  }
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, IFACE_("Align"));
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Sequencer Slide) */
 | 
						|
 | 
						|
/** \name Transform Sequencer Slide
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initSeqSlide(TransInfo *t)
 | 
						|
{
 | 
						|
  t->transform = applySeqSlide;
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_VECTOR);
 | 
						|
 | 
						|
  t->idx_max = 1;
 | 
						|
  t->num.flag = 0;
 | 
						|
  t->num.idx_max = t->idx_max;
 | 
						|
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = floorf(t->scene->r.frs_sec / t->scene->r.frs_sec_base);
 | 
						|
  t->snap[2] = 10.0f;
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  /* Would be nice to have a time handling in units as well
 | 
						|
   * (supporting frames in addition to "natural" time...). */
 | 
						|
  t->num.unit_type[0] = B_UNIT_NONE;
 | 
						|
  t->num.unit_type[1] = B_UNIT_NONE;
 | 
						|
}
 | 
						|
 | 
						|
static void headerSeqSlide(TransInfo *t, const float val[2], 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, "%.0f, %.0f", val[0], val[1]);
 | 
						|
  }
 | 
						|
 | 
						|
  ofs += BLI_snprintf(
 | 
						|
      str + ofs, UI_MAX_DRAW_STR - ofs, IFACE_("Sequence Slide: %s%s, ("), &tvec[0], t->con.text);
 | 
						|
 | 
						|
  if (t->keymap) {
 | 
						|
    wmKeyMapItem *kmi = WM_modalkeymap_find_propvalue(t->keymap, TFM_MODAL_TRANSLATE);
 | 
						|
    if (kmi) {
 | 
						|
      ofs += WM_keymap_item_to_string(kmi, false, str + ofs, UI_MAX_DRAW_STR - ofs);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  ofs += BLI_snprintf(str + ofs,
 | 
						|
                      UI_MAX_DRAW_STR - ofs,
 | 
						|
                      IFACE_(" or Alt) Expand to fit %s"),
 | 
						|
                      WM_bool_as_string((t->flag & T_ALT_TRANSFORM) != 0));
 | 
						|
}
 | 
						|
 | 
						|
static void applySeqSlideValue(TransInfo *t, const float val[2])
 | 
						|
{
 | 
						|
  int i;
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      if (td->flag & TD_NOACTION) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      if (td->flag & TD_SKIP) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      madd_v2_v2v2fl(td->loc, td->iloc, val, td->factor);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void applySeqSlide(TransInfo *t, const int mval[2])
 | 
						|
{
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  snapSequenceBounds(t, mval);
 | 
						|
 | 
						|
  if (t->con.mode & CON_APPLY) {
 | 
						|
    float pvec[3] = {0.0f, 0.0f, 0.0f};
 | 
						|
    float tvec[3];
 | 
						|
    t->con.applyVec(t, NULL, NULL, t->values, tvec, pvec);
 | 
						|
    copy_v3_v3(t->values, tvec);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    // snapGridIncrement(t, t->values);
 | 
						|
    applyNumInput(&t->num, t->values);
 | 
						|
  }
 | 
						|
 | 
						|
  t->values[0] = floorf(t->values[0] + 0.5f);
 | 
						|
  t->values[1] = floorf(t->values[1] + 0.5f);
 | 
						|
 | 
						|
  headerSeqSlide(t, t->values, str);
 | 
						|
  applySeqSlideValue(t, t->values);
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Animation Editors - Transform Utils
 | 
						|
 *
 | 
						|
 * Special Helpers for Various Settings
 | 
						|
 */
 | 
						|
 | 
						|
/** \name Animation Editor 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
 | 
						|
static 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
 | 
						|
 */
 | 
						|
static void doAnimEdit_SnapFrame(
 | 
						|
    TransInfo *t, TransData *td, TransData2D *td2d, AnimData *adt, short autosnap)
 | 
						|
{
 | 
						|
  /* snap key to nearest frame or second? */
 | 
						|
  if (ELEM(autosnap, SACTSNAP_FRAME, SACTSNAP_SECOND)) {
 | 
						|
    const Scene *scene = t->scene;
 | 
						|
    const double secf = FPS;
 | 
						|
    double 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);
 | 
						|
    }
 | 
						|
 | 
						|
    /* do the snapping to nearest frame/second */
 | 
						|
    if (autosnap == SACTSNAP_FRAME) {
 | 
						|
      val = floorf(val + 0.5);
 | 
						|
    }
 | 
						|
    else if (autosnap == SACTSNAP_SECOND) {
 | 
						|
      val = (float)(floor((val / secf) + 0.5) * secf);
 | 
						|
    }
 | 
						|
 | 
						|
    /* convert frame out of nla-action time */
 | 
						|
    if (adt) {
 | 
						|
      *(td->val) = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      *(td->val) = val;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  /* snap key to nearest marker? */
 | 
						|
  else if (autosnap == 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);
 | 
						|
    }
 | 
						|
 | 
						|
    /* snap to nearest marker */
 | 
						|
    // TODO: need some more careful checks for where data comes from
 | 
						|
    val = (float)ED_markers_find_nearest_marker_time(&t->scene->markers, 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;
 | 
						|
  }
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Animation Translation) */
 | 
						|
 | 
						|
/** \name Transform Animation Translation
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initTimeTranslate(TransInfo *t)
 | 
						|
{
 | 
						|
  /* this tool is only really available in the Action Editor... */
 | 
						|
  if (!ELEM(t->spacetype, SPACE_ACTION, SPACE_SEQ)) {
 | 
						|
    t->state = TRANS_CANCEL;
 | 
						|
  }
 | 
						|
 | 
						|
  t->mode = TFM_TIME_TRANSLATE;
 | 
						|
  t->transform = applyTimeTranslate;
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_NONE);
 | 
						|
 | 
						|
  /* num-input has max of (n-1) */
 | 
						|
  t->idx_max = 0;
 | 
						|
  t->num.flag = 0;
 | 
						|
  t->num.idx_max = t->idx_max;
 | 
						|
 | 
						|
  /* initialize snap like for everything else */
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = t->snap[2] = 1.0f;
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  /* No time unit supporting frames currently... */
 | 
						|
  t->num.unit_type[0] = B_UNIT_NONE;
 | 
						|
}
 | 
						|
 | 
						|
static void headerTimeTranslate(TransInfo *t, char str[UI_MAX_DRAW_STR])
 | 
						|
{
 | 
						|
  char tvec[NUM_STR_REP_LEN * 3];
 | 
						|
  int ofs = 0;
 | 
						|
 | 
						|
  /* if numeric input is active, use results from that, otherwise apply snapping to result */
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    outputNumInput(&(t->num), tvec, &t->scene->unit);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    const Scene *scene = t->scene;
 | 
						|
    const short autosnap = getAnimEdit_SnapMode(t);
 | 
						|
    const double secf = FPS;
 | 
						|
    float val = t->values[0];
 | 
						|
 | 
						|
    /* apply snapping + frame->seconds conversions */
 | 
						|
    if (autosnap == SACTSNAP_STEP) {
 | 
						|
      /* frame step */
 | 
						|
      val = floorf(val + 0.5f);
 | 
						|
    }
 | 
						|
    else if (autosnap == SACTSNAP_TSTEP) {
 | 
						|
      /* second step */
 | 
						|
      val = floorf((double)val / secf + 0.5);
 | 
						|
    }
 | 
						|
    else if (autosnap == SACTSNAP_SECOND) {
 | 
						|
      /* nearest second */
 | 
						|
      val = (float)((double)val / secf);
 | 
						|
    }
 | 
						|
 | 
						|
    if (autosnap == SACTSNAP_FRAME) {
 | 
						|
      BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%d.00 (%.4f)", (int)val, val);
 | 
						|
    }
 | 
						|
    else if (autosnap == SACTSNAP_SECOND) {
 | 
						|
      BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%d.00 sec (%.4f)", (int)val, val);
 | 
						|
    }
 | 
						|
    else if (autosnap == SACTSNAP_TSTEP) {
 | 
						|
      BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f sec", val);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", val);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  ofs += BLI_snprintf(str, UI_MAX_DRAW_STR, IFACE_("DeltaX: %s"), &tvec[0]);
 | 
						|
 | 
						|
  if (t->flag & T_PROP_EDIT_ALL) {
 | 
						|
    ofs += BLI_snprintf(
 | 
						|
        str + ofs, UI_MAX_DRAW_STR - ofs, IFACE_(" Proportional size: %.2f"), t->prop_size);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void applyTimeTranslateValue(TransInfo *t)
 | 
						|
{
 | 
						|
  Scene *scene = t->scene;
 | 
						|
  int i;
 | 
						|
 | 
						|
  const short autosnap = getAnimEdit_SnapMode(t);
 | 
						|
  const double secf = FPS;
 | 
						|
 | 
						|
  float deltax, val /* , valprev */;
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    TransData2D *td2d = tc->data_2d;
 | 
						|
    /* It doesn't matter whether we apply to t->data or
 | 
						|
     * t->data2d, but t->data2d is more convenient. */
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++, td2d++) {
 | 
						|
      /* it is assumed that td->extra is a pointer to the AnimData,
 | 
						|
       * whose active action is where this keyframe comes from
 | 
						|
       * (this is only valid when not in NLA)
 | 
						|
       */
 | 
						|
      AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL;
 | 
						|
 | 
						|
      /* valprev = *td->val; */ /* UNUSED */
 | 
						|
 | 
						|
      /* check if any need to apply nla-mapping */
 | 
						|
      if (adt && (t->spacetype != SPACE_SEQ)) {
 | 
						|
        deltax = t->values[0];
 | 
						|
 | 
						|
        if (autosnap == SACTSNAP_TSTEP) {
 | 
						|
          deltax = (float)(floor(((double)deltax / secf) + 0.5) * secf);
 | 
						|
        }
 | 
						|
        else if (autosnap == SACTSNAP_STEP) {
 | 
						|
          deltax = floorf(deltax + 0.5f);
 | 
						|
        }
 | 
						|
 | 
						|
        val = BKE_nla_tweakedit_remap(adt, td->ival, NLATIME_CONVERT_MAP);
 | 
						|
        val += deltax * td->factor;
 | 
						|
        *(td->val) = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP);
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        deltax = val = t->values[0];
 | 
						|
 | 
						|
        if (autosnap == SACTSNAP_TSTEP) {
 | 
						|
          val = (float)(floor(((double)deltax / secf) + 0.5) * secf);
 | 
						|
        }
 | 
						|
        else if (autosnap == SACTSNAP_STEP) {
 | 
						|
          val = floorf(val + 0.5f);
 | 
						|
        }
 | 
						|
 | 
						|
        *(td->val) = td->ival + val;
 | 
						|
      }
 | 
						|
 | 
						|
      /* apply nearest snapping */
 | 
						|
      doAnimEdit_SnapFrame(t, td, td2d, adt, autosnap);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void applyTimeTranslate(TransInfo *t, const int mval[2])
 | 
						|
{
 | 
						|
  View2D *v2d = (View2D *)t->view;
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  /* calculate translation amount from mouse movement - in 'time-grid space' */
 | 
						|
  if (t->flag & T_MODAL) {
 | 
						|
    float cval[2], sval[2];
 | 
						|
    UI_view2d_region_to_view(v2d, mval[0], mval[0], &cval[0], &cval[1]);
 | 
						|
    UI_view2d_region_to_view(v2d, t->mouse.imval[0], t->mouse.imval[0], &sval[0], &sval[1]);
 | 
						|
 | 
						|
    /* we only need to calculate effect for time (applyTimeTranslate only needs that) */
 | 
						|
    t->values[0] = cval[0] - sval[0];
 | 
						|
  }
 | 
						|
 | 
						|
  /* handle numeric-input stuff */
 | 
						|
  t->vec[0] = t->values[0];
 | 
						|
  applyNumInput(&t->num, &t->vec[0]);
 | 
						|
  t->values[0] = t->vec[0];
 | 
						|
  headerTimeTranslate(t, str);
 | 
						|
 | 
						|
  applyTimeTranslateValue(t);
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Animation Time Slide) */
 | 
						|
 | 
						|
/** \name Transform Animation Time Slide
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initTimeSlide(TransInfo *t)
 | 
						|
{
 | 
						|
  /* this tool is only really available in the Action Editor... */
 | 
						|
  if (t->spacetype == SPACE_ACTION) {
 | 
						|
    SpaceAction *saction = (SpaceAction *)t->sa->spacedata.first;
 | 
						|
 | 
						|
    /* set flag for drawing stuff */
 | 
						|
    saction->flag |= SACTION_MOVING;
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    t->state = TRANS_CANCEL;
 | 
						|
  }
 | 
						|
 | 
						|
  t->mode = TFM_TIME_SLIDE;
 | 
						|
  t->transform = applyTimeSlide;
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_NONE);
 | 
						|
 | 
						|
  {
 | 
						|
    Scene *scene = t->scene;
 | 
						|
    float *range;
 | 
						|
    t->custom.mode.data = range = MEM_mallocN(sizeof(float[2]), "TimeSlide Min/Max");
 | 
						|
    t->custom.mode.use_free = true;
 | 
						|
 | 
						|
    float min = 999999999.0f, max = -999999999.0f;
 | 
						|
    int i;
 | 
						|
    FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
      TransData *td = tc->data;
 | 
						|
      for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
        AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL;
 | 
						|
        float val = *(td->val);
 | 
						|
 | 
						|
        /* strip/action time to global (mapped) time */
 | 
						|
        if (adt) {
 | 
						|
          val = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_MAP);
 | 
						|
        }
 | 
						|
 | 
						|
        if (min > val) {
 | 
						|
          min = val;
 | 
						|
        }
 | 
						|
        if (max < val) {
 | 
						|
          max = val;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (min == max) {
 | 
						|
      /* just use the current frame ranges */
 | 
						|
      min = (float)PSFRA;
 | 
						|
      max = (float)PEFRA;
 | 
						|
    }
 | 
						|
 | 
						|
    range[0] = min;
 | 
						|
    range[1] = max;
 | 
						|
  }
 | 
						|
 | 
						|
  /* num-input has max of (n-1) */
 | 
						|
  t->idx_max = 0;
 | 
						|
  t->num.flag = 0;
 | 
						|
  t->num.idx_max = t->idx_max;
 | 
						|
 | 
						|
  /* initialize snap like for everything else */
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = t->snap[2] = 1.0f;
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  /* No time unit supporting frames currently... */
 | 
						|
  t->num.unit_type[0] = B_UNIT_NONE;
 | 
						|
}
 | 
						|
 | 
						|
static void headerTimeSlide(TransInfo *t, const float sval, char str[UI_MAX_DRAW_STR])
 | 
						|
{
 | 
						|
  char tvec[NUM_STR_REP_LEN * 3];
 | 
						|
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    outputNumInput(&(t->num), tvec, &t->scene->unit);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    const float *range = t->custom.mode.data;
 | 
						|
    float minx = range[0];
 | 
						|
    float maxx = range[1];
 | 
						|
    float cval = t->values[0];
 | 
						|
    float val;
 | 
						|
 | 
						|
    val = 2.0f * (cval - sval) / (maxx - minx);
 | 
						|
    CLAMP(val, -1.0f, 1.0f);
 | 
						|
 | 
						|
    BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", val);
 | 
						|
  }
 | 
						|
 | 
						|
  BLI_snprintf(str, UI_MAX_DRAW_STR, IFACE_("TimeSlide: %s"), &tvec[0]);
 | 
						|
}
 | 
						|
 | 
						|
static void applyTimeSlideValue(TransInfo *t, float sval)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  const float *range = t->custom.mode.data;
 | 
						|
  float minx = range[0];
 | 
						|
  float maxx = range[1];
 | 
						|
 | 
						|
  /* set value for drawing black line */
 | 
						|
  if (t->spacetype == SPACE_ACTION) {
 | 
						|
    SpaceAction *saction = (SpaceAction *)t->sa->spacedata.first;
 | 
						|
    float cvalf = t->values[0];
 | 
						|
 | 
						|
    saction->timeslide = cvalf;
 | 
						|
  }
 | 
						|
 | 
						|
  /* It doesn't matter whether we apply to t->data or
 | 
						|
   * t->data2d, but t->data2d is more convenient. */
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++) {
 | 
						|
      /* it is assumed that td->extra is a pointer to the AnimData,
 | 
						|
       * whose active action is where this keyframe comes from
 | 
						|
       * (this is only valid when not in NLA)
 | 
						|
       */
 | 
						|
      AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL;
 | 
						|
      float cval = t->values[0];
 | 
						|
 | 
						|
      /* only apply to data if in range */
 | 
						|
      if ((sval > minx) && (sval < maxx)) {
 | 
						|
        float cvalc = CLAMPIS(cval, minx, maxx);
 | 
						|
        float ival = td->ival;
 | 
						|
        float timefac;
 | 
						|
 | 
						|
        /* NLA mapping magic here works as follows:
 | 
						|
         * - "ival" goes from strip time to global time
 | 
						|
         * - calculation is performed into td->val in global time
 | 
						|
         *   (since sval and min/max are all in global time)
 | 
						|
         * - "td->val" then gets put back into strip time
 | 
						|
         */
 | 
						|
        if (adt) {
 | 
						|
          /* strip to global */
 | 
						|
          ival = BKE_nla_tweakedit_remap(adt, ival, NLATIME_CONVERT_MAP);
 | 
						|
        }
 | 
						|
 | 
						|
        /* left half? */
 | 
						|
        if (ival < sval) {
 | 
						|
          timefac = (sval - ival) / (sval - minx);
 | 
						|
          *(td->val) = cvalc - timefac * (cvalc - minx);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          timefac = (ival - sval) / (maxx - sval);
 | 
						|
          *(td->val) = cvalc + timefac * (maxx - cvalc);
 | 
						|
        }
 | 
						|
 | 
						|
        if (adt) {
 | 
						|
          /* global to strip */
 | 
						|
          *(td->val) = BKE_nla_tweakedit_remap(adt, *(td->val), NLATIME_CONVERT_UNMAP);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void applyTimeSlide(TransInfo *t, const int mval[2])
 | 
						|
{
 | 
						|
  View2D *v2d = (View2D *)t->view;
 | 
						|
  float cval[2], sval[2];
 | 
						|
  const float *range = t->custom.mode.data;
 | 
						|
  float minx = range[0];
 | 
						|
  float maxx = range[1];
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  /* calculate mouse co-ordinates */
 | 
						|
  UI_view2d_region_to_view(v2d, mval[0], mval[1], &cval[0], &cval[1]);
 | 
						|
  UI_view2d_region_to_view(v2d, t->mouse.imval[0], t->mouse.imval[1], &sval[0], &sval[1]);
 | 
						|
 | 
						|
  /* t->values[0] stores cval[0], which is the current mouse-pointer location (in frames) */
 | 
						|
  // XXX Need to be able to repeat this
 | 
						|
  /* t->values[0] = cval[0]; */ /* UNUSED (reset again later). */
 | 
						|
 | 
						|
  /* handle numeric-input stuff */
 | 
						|
  t->vec[0] = 2.0f * (cval[0] - sval[0]) / (maxx - minx);
 | 
						|
  applyNumInput(&t->num, &t->vec[0]);
 | 
						|
  t->values[0] = (maxx - minx) * t->vec[0] / 2.0f + sval[0];
 | 
						|
 | 
						|
  headerTimeSlide(t, sval[0], str);
 | 
						|
  applyTimeSlideValue(t, sval[0]);
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
/* Transform (Animation Time Scale) */
 | 
						|
 | 
						|
/** \name Transform Animation Time Scale
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static void initTimeScale(TransInfo *t)
 | 
						|
{
 | 
						|
  float center[2];
 | 
						|
 | 
						|
  /* this tool is only really available in the Action Editor
 | 
						|
   * AND NLA Editor (for strip scaling)
 | 
						|
   */
 | 
						|
  if (ELEM(t->spacetype, SPACE_ACTION, SPACE_NLA) == 0) {
 | 
						|
    t->state = TRANS_CANCEL;
 | 
						|
  }
 | 
						|
 | 
						|
  t->mode = TFM_TIME_SCALE;
 | 
						|
  t->transform = applyTimeScale;
 | 
						|
 | 
						|
  /* recalculate center2d to use CFRA and mouse Y, since that's
 | 
						|
   * what is used in time scale */
 | 
						|
  if ((t->flag & T_OVERRIDE_CENTER) == 0) {
 | 
						|
    t->center_global[0] = t->scene->r.cfra;
 | 
						|
    projectFloatView(t, t->center_global, center);
 | 
						|
    center[1] = t->mouse.imval[1];
 | 
						|
  }
 | 
						|
 | 
						|
  /* force a reinit with the center2d used here */
 | 
						|
  initMouseInput(t, &t->mouse, center, t->mouse.imval, false);
 | 
						|
 | 
						|
  initMouseInputMode(t, &t->mouse, INPUT_SPRING_FLIP);
 | 
						|
 | 
						|
  t->flag |= T_NULL_ONE;
 | 
						|
  t->num.val_flag[0] |= NUM_NULL_ONE;
 | 
						|
 | 
						|
  /* num-input has max of (n-1) */
 | 
						|
  t->idx_max = 0;
 | 
						|
  t->num.flag = 0;
 | 
						|
  t->num.idx_max = t->idx_max;
 | 
						|
 | 
						|
  /* initialize snap like for everything else */
 | 
						|
  t->snap[0] = 0.0f;
 | 
						|
  t->snap[1] = t->snap[2] = 1.0f;
 | 
						|
 | 
						|
  copy_v3_fl(t->num.val_inc, t->snap[1]);
 | 
						|
  t->num.unit_sys = t->scene->unit.system;
 | 
						|
  t->num.unit_type[0] = B_UNIT_NONE;
 | 
						|
}
 | 
						|
 | 
						|
static void headerTimeScale(TransInfo *t, char str[UI_MAX_DRAW_STR])
 | 
						|
{
 | 
						|
  char tvec[NUM_STR_REP_LEN * 3];
 | 
						|
 | 
						|
  if (hasNumInput(&t->num)) {
 | 
						|
    outputNumInput(&(t->num), tvec, &t->scene->unit);
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", t->values[0]);
 | 
						|
  }
 | 
						|
 | 
						|
  BLI_snprintf(str, UI_MAX_DRAW_STR, IFACE_("ScaleX: %s"), &tvec[0]);
 | 
						|
}
 | 
						|
 | 
						|
static void applyTimeScaleValue(TransInfo *t)
 | 
						|
{
 | 
						|
  Scene *scene = t->scene;
 | 
						|
  int i;
 | 
						|
 | 
						|
  const short autosnap = getAnimEdit_SnapMode(t);
 | 
						|
  const double secf = FPS;
 | 
						|
 | 
						|
  FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 | 
						|
    TransData *td = tc->data;
 | 
						|
    TransData2D *td2d = tc->data_2d;
 | 
						|
    for (i = 0; i < tc->data_len; i++, td++, td2d++) {
 | 
						|
      /* it is assumed that td->extra is a pointer to the AnimData,
 | 
						|
       * whose active action is where this keyframe comes from
 | 
						|
       * (this is only valid when not in NLA)
 | 
						|
       */
 | 
						|
      AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL;
 | 
						|
      float startx = CFRA;
 | 
						|
      float fac = t->values[0];
 | 
						|
 | 
						|
      if (autosnap == SACTSNAP_TSTEP) {
 | 
						|
        fac = (float)(floor((double)fac / secf + 0.5) * secf);
 | 
						|
      }
 | 
						|
      else if (autosnap == SACTSNAP_STEP) {
 | 
						|
        fac = floorf(fac + 0.5f);
 | 
						|
      }
 | 
						|
 | 
						|
      /* take proportional editing into account */
 | 
						|
      fac = ((fac - 1.0f) * td->factor) + 1;
 | 
						|
 | 
						|
      /* check if any need to apply nla-mapping */
 | 
						|
      if (adt) {
 | 
						|
        startx = BKE_nla_tweakedit_remap(adt, startx, NLATIME_CONVERT_UNMAP);
 | 
						|
      }
 | 
						|
 | 
						|
      /* now, calculate the new value */
 | 
						|
      *(td->val) = ((td->ival - startx) * fac) + startx;
 | 
						|
 | 
						|
      /* apply nearest snapping */
 | 
						|
      doAnimEdit_SnapFrame(t, td, td2d, adt, autosnap);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void applyTimeScale(TransInfo *t, const int UNUSED(mval[2]))
 | 
						|
{
 | 
						|
  char str[UI_MAX_DRAW_STR];
 | 
						|
 | 
						|
  /* handle numeric-input stuff */
 | 
						|
  t->vec[0] = t->values[0];
 | 
						|
  applyNumInput(&t->num, &t->vec[0]);
 | 
						|
  t->values[0] = t->vec[0];
 | 
						|
  headerTimeScale(t, str);
 | 
						|
 | 
						|
  applyTimeScaleValue(t);
 | 
						|
 | 
						|
  recalcData(t);
 | 
						|
 | 
						|
  ED_area_status_text(t->sa, str);
 | 
						|
}
 | 
						|
/** \} */
 | 
						|
 | 
						|
/* TODO, move to: transform_query.c */
 | 
						|
bool checkUseAxisMatrix(TransInfo *t)
 | 
						|
{
 | 
						|
  /* currently only checks for editmode */
 | 
						|
  if (t->flag & T_EDIT) {
 | 
						|
    if ((t->around == V3D_AROUND_LOCAL_ORIGINS) &&
 | 
						|
        (ELEM(t->obedit_type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE))) {
 | 
						|
      /* not all editmode supports axis-matrix */
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 |