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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormano-wii <germano.costa@ig.com.br>2020-02-14 14:42:17 +0300
committermano-wii <germano.costa@ig.com.br>2020-02-14 14:42:59 +0300
commite277e8d085037414f34b27f9c1a26cbbf2507c3e (patch)
treecb37b0a9332a23e09a6717d68894f481542f3d67 /source/blender/editors/transform/transform_mode_edge_slide.c
parent897f943ca0c93a46bdb767e4c47c4bddfa7149c2 (diff)
Cleanup: Split transform.c in multiple files
Differential Revision: https://developer.blender.org/D5819
Diffstat (limited to 'source/blender/editors/transform/transform_mode_edge_slide.c')
-rw-r--r--source/blender/editors/transform/transform_mode_edge_slide.c1463
1 files changed, 1463 insertions, 0 deletions
diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c
new file mode 100644
index 00000000000..70f4c94876c
--- /dev/null
+++ b/source/blender/editors/transform/transform_mode_edge_slide.c
@@ -0,0 +1,1463 @@
+/*
+ * 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 "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_string.h"
+#include "BLI_utildefines_stack.h"
+
+#include "BKE_context.h"
+#include "BKE_editmesh.h"
+#include "BKE_editmesh_bvh.h"
+#include "BKE_unit.h"
+
+#include "GPU_immediate.h"
+#include "GPU_matrix.h"
+#include "GPU_state.h"
+
+#include "ED_mesh.h"
+#include "ED_screen.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "BLT_translation.h"
+
+#include "transform.h"
+#include "transform_convert.h"
+#include "transform_snap.h"
+#include "transform_mode.h"
+
+/* -------------------------------------------------------------------- */
+/* Transform (Edge Slide) */
+
+/** \name Transform Edge Slide
+ * \{ */
+
+/**
+ * Get the first valid EdgeSlideData.
+ *
+ * Note we cannot trust TRANS_DATA_CONTAINER_FIRST_OK because of multi-object that
+ * may leave items with invalid custom data in the transform data container.
+ */
+static EdgeSlideData *edgeSlideFirstGet(TransInfo *t)
+{
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ EdgeSlideData *sld = tc->custom.mode.data;
+ if (sld == NULL) {
+ continue;
+ }
+ return sld;
+ }
+ BLI_assert(!"Should never happen, at least one EdgeSlideData should be valid");
+ return NULL;
+}
+
+static void calcEdgeSlideCustomPoints(struct TransInfo *t)
+{
+ EdgeSlideData *sld = edgeSlideFirstGet(t);
+
+ 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;
+ BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
+ 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;
+
+ 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);
+ }
+
+ sv = &sld->sv[0];
+ for (int i = 0; i < sld->totsv; i++, sv++) {
+ BMIter iter_other;
+ BMEdge *e;
+ BMVert *v = sv->v;
+
+ UNUSED_VARS_NDEBUG(sv_table); /* silence warning */
+ BLI_assert(i == sv_table[BM_elem_index_get(v)]);
+
+ /* search cross edges for visible edge to the mouse cursor,
+ * then use the shared vertex to calculate screen vector*/
+ BM_ITER_ELEM (e, &iter_other, v, BM_EDGES_OF_VERT) {
+ /* screen-space coords */
+ float sco_a[3], sco_b[3];
+ float dist_sq;
+ int l_nr;
+
+ if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
+ continue;
+ }
+
+ /* This test is only relevant if object is not wire-drawn! See [#32068]. */
+ bool is_visible = !use_occlude_geometry ||
+ BMBVH_EdgeVisible(bmbvh, e, t->depsgraph, ar, v3d, tc->obedit);
+
+ if (!is_visible && !use_calc_direction) {
+ continue;
+ }
+
+ if (sv->v_side[1]) {
+ ED_view3d_project_float_v3_m4(ar, sv->v_side[1]->co, sco_b, projectMat);
+ }
+ else {
+ add_v3_v3v3(sco_b, v->co, sv->dir_side[1]);
+ ED_view3d_project_float_v3_m4(ar, sco_b, sco_b, projectMat);
+ }
+
+ if (sv->v_side[0]) {
+ ED_view3d_project_float_v3_m4(ar, sv->v_side[0]->co, sco_a, projectMat);
+ }
+ else {
+ add_v3_v3v3(sco_a, v->co, sv->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 (is_visible) {
+ 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->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 = &sld->sv[0];
+ for (i = 0; i < sld->totsv; i++, sv++) {
+ /* switch a/b if loop direction is different from global direction */
+ int l_nr = sv->loop_nr;
+ if (dot_v3v3(loop_dir[l_nr], mval_dir) < 0.0f) {
+ swap_v3_v3(sv->dir_side[0], sv->dir_side[1]);
+ SWAP(BMVert *, sv->v_side[0], sv->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;
+
+ 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);
+
+ if (rv3d) {
+ calcEdgeSlide_even(t, tc, sld, mval);
+ }
+
+ 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;
+ }
+
+ 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);
+
+ if (rv3d) {
+ calcEdgeSlide_even(t, tc, sld, mval);
+ }
+
+ 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;
+
+ if (sld == NULL) {
+ continue;
+ }
+
+ trans_mesh_customdata_correction_apply(tc, is_final);
+ }
+}
+
+static void freeEdgeSlideVerts(TransInfo *UNUSED(t),
+ TransDataContainer *UNUSED(tc),
+ TransCustomData *custom_data)
+{
+ EdgeSlideData *sld = custom_data->data;
+
+ if (sld == NULL) {
+ return;
+ }
+
+ MEM_freeN(sld->sv);
+ MEM_freeN(sld);
+
+ custom_data->data = NULL;
+}
+
+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;
+}
+
+void drawEdgeSlide(TransInfo *t)
+{
+ if ((t->mode == TFM_EDGE_SLIDE) && edgeSlideFirstGet(t)) {
+ const EdgeSlideParams *slp = t->custom.mode.data;
+ EdgeSlideData *sld = edgeSlideFirstGet(t);
+ 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();
+
+ {
+ float *co_test = NULL;
+ if (slp->flipped) {
+ if (curr_sv->v_side[1]) {
+ co_test = curr_sv->v_side[1]->co;
+ }
+ }
+ else {
+ if (curr_sv->v_side[0]) {
+ co_test = curr_sv->v_side[0]->co;
+ }
+ }
+
+ if (co_test != NULL) {
+ immUniformThemeColorShadeAlpha(TH_SELECT, -30, alpha_shade);
+ GPU_point_size(ctrl_size);
+ immBegin(GPU_PRIM_POINTS, 1);
+ immVertex3fv(pos, co_test);
+ 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);
+ }
+ }
+}
+
+void doEdgeSlide(TransInfo *t, float perc)
+{
+ EdgeSlideParams *slp = t->custom.mode.data;
+ EdgeSlideData *sld_active = edgeSlideFirstGet(t);
+
+ 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;
+
+ if (sld == NULL) {
+ continue;
+ }
+
+ 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;
+
+ if (sld == NULL) {
+ continue;
+ }
+
+ 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;
+
+ if (sld == NULL) {
+ continue;
+ }
+
+ 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_final[0] = final;
+
+ /* header string */
+ ofs += BLI_strncpy_rlen(str + ofs, TIP_("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, TIP_("(E)ven: %s, "), WM_bool_as_string(use_even));
+ if (use_even) {
+ ofs += BLI_snprintf(
+ str + ofs, sizeof(str) - ofs, TIP_("(F)lipped: %s, "), WM_bool_as_string(flipped));
+ }
+ ofs += BLI_snprintf(
+ str + ofs, sizeof(str) - ofs, TIP_("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);
+}
+
+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;
+ }
+
+ trans_mesh_customdata_correction_init(t);
+
+ /* 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;
+}
+
+void initEdgeSlide(TransInfo *t)
+{
+ initEdgeSlide_ex(t, true, false, false, true);
+}
+/** \} */