Blender  V3.3
transform_mode_edge_slide.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2001-2002 NaN Holding BV. All rights reserved. */
3 
8 #include <stdlib.h>
9 
10 #include "MEM_guardedalloc.h"
11 
12 #include "BLI_math.h"
13 #include "BLI_string.h"
14 #include "BLI_utildefines_stack.h"
15 
16 #include "BKE_context.h"
17 #include "BKE_editmesh.h"
18 #include "BKE_editmesh_bvh.h"
19 #include "BKE_unit.h"
20 
21 #include "GPU_immediate.h"
22 #include "GPU_matrix.h"
23 #include "GPU_state.h"
24 
25 #include "ED_mesh.h"
26 #include "ED_screen.h"
27 
28 #include "WM_api.h"
29 #include "WM_types.h"
30 
31 #include "UI_interface.h"
32 #include "UI_resources.h"
33 
34 #include "BLT_translation.h"
35 
36 #include "transform.h"
37 #include "transform_constraints.h"
38 #include "transform_convert.h"
39 #include "transform_mode.h"
40 #include "transform_snap.h"
41 
42 /* -------------------------------------------------------------------- */
46 typedef struct TransDataEdgeSlideVert {
48  struct BMVert *v;
50  float v_co_orig[3];
51  /* end generic */
52 
53  float edge_len;
54 
55  struct BMVert *v_side[2];
56 
57  /* add origvert.co to get the original locations */
58  float dir_side[2][3];
59 
60  int loop_nr;
62 
63 typedef struct EdgeSlideData {
65  int totsv;
66 
67  int mval_start[2], mval_end[2];
69 
73 
74 typedef struct EdgeSlideParams {
75  float perc;
76 
77  bool use_even;
78  bool flipped;
80 
88 {
90  if (tc->custom.mode.data) {
91  return tc;
92  }
93  }
94  BLI_assert_msg(0, "Should never happen, at least one EdgeSlideData should be valid");
95  return NULL;
96 }
97 
99 {
101  return tc->custom.mode.data;
102 }
103 
105 {
107 
108  setCustomPoints(t, &t->mouse, sld->mval_end, sld->mval_start);
109 
110  /* setCustomPoints isn't normally changing as the mouse moves,
111  * in this case apply mouse input immediately so we don't refresh
112  * with the value from the previous points */
113  applyMouseInput(t, &t->mouse, t->mval, t->values);
114 }
115 
117 {
118  BMIter iter;
119  BMEdge *e_iter;
120 
121  BM_ITER_ELEM (e_iter, &iter, v, BM_EDGES_OF_VERT) {
122  if (BM_elem_flag_test(e_iter, BM_ELEM_SELECT) && e_iter != e) {
123  return e_iter;
124  }
125  }
126 
127  return NULL;
128 }
129 
130 /* Interpolates along a line made up of 2 segments (used for edge slide). */
132  float p[3], const float v1[3], const float v2[3], const float v3[3], float t)
133 {
134  float t_mid, t_delta;
135 
136  /* could be pre-calculated */
137  t_mid = line_point_factor_v3(v2, v1, v3);
138 
139  t_delta = t - t_mid;
140  if (t_delta < 0.0f) {
141  if (UNLIKELY(fabsf(t_mid) < FLT_EPSILON)) {
142  copy_v3_v3(p, v2);
143  }
144  else {
145  interp_v3_v3v3(p, v1, v2, t / t_mid);
146  }
147  }
148  else {
149  t = t - t_mid;
150  t_mid = 1.0f - t_mid;
151 
152  if (UNLIKELY(fabsf(t_mid) < FLT_EPSILON)) {
153  copy_v3_v3(p, v3);
154  }
155  else {
156  interp_v3_v3v3(p, v2, v3, t / t_mid);
157  }
158  }
159 }
160 
165 static bool bm_loop_calc_opposite_co(BMLoop *l_tmp, const float plane_no[3], float r_co[3])
166 {
167  /* skip adjacent edges */
168  BMLoop *l_first = l_tmp->next;
169  BMLoop *l_last = l_tmp->prev;
170  BMLoop *l_iter;
171  float dist = FLT_MAX;
172  bool found = false;
173 
174  l_iter = l_first;
175  do {
176  float tvec[3];
177  if (isect_line_plane_v3(tvec, l_iter->v->co, l_iter->next->v->co, l_tmp->v->co, plane_no)) {
178  const float fac = line_point_factor_v3(tvec, l_iter->v->co, l_iter->next->v->co);
179  /* allow some overlap to avoid missing the intersection because of float precision */
180  if ((fac > -FLT_EPSILON) && (fac < 1.0f + FLT_EPSILON)) {
181  /* likelihood of multiple intersections per ngon is quite low,
182  * it would have to loop back on itself, but better support it
183  * so check for the closest opposite edge */
184  const float tdist = len_v3v3(l_tmp->v->co, tvec);
185  if (tdist < dist) {
186  copy_v3_v3(r_co, tvec);
187  dist = tdist;
188  found = true;
189  }
190  }
191  }
192  } while ((l_iter = l_iter->next) != l_last);
193 
194  return found;
195 }
196 
205  BMVert *v, BMLoop *l, BMEdge *e_prev, BMEdge *e_next, float r_slide_vec[3])
206 {
207  BMLoop *l_first;
208  float vec_accum[3] = {0.0f, 0.0f, 0.0f};
209  float vec_accum_len = 0.0f;
210  int i = 0;
211 
212  BLI_assert(BM_edge_share_vert(e_prev, e_next) == v);
214 
215  l_first = l;
216  do {
218 
219  if (l->e == e_next) {
220  if (i) {
221  normalize_v3_length(vec_accum, vec_accum_len / (float)i);
222  }
223  else {
224  /* When there is no edge to slide along,
225  * we must slide along the vector defined by the face we're attach to */
226  BMLoop *l_tmp = BM_face_vert_share_loop(l_first->f, v);
227 
228  BLI_assert(ELEM(l_tmp->e, e_prev, e_next) && ELEM(l_tmp->prev->e, e_prev, e_next));
229 
230  if (l_tmp->f->len == 4) {
231  /* we could use code below, but in this case
232  * sliding diagonally across the quad works well */
233  sub_v3_v3v3(vec_accum, l_tmp->next->next->v->co, v->co);
234  }
235  else {
236  float tdir[3];
237  BM_loop_calc_face_direction(l_tmp, tdir);
238  cross_v3_v3v3(vec_accum, l_tmp->f->no, tdir);
239 #if 0
240  /* rough guess, we can do better! */
241  normalize_v3_length(vec_accum,
242  (BM_edge_calc_length(e_prev) + BM_edge_calc_length(e_next)) / 2.0f);
243 #else
244  /* be clever, check the opposite ngon edge to slide into.
245  * this gives best results */
246  {
247  float tvec[3];
248  float dist;
249 
250  if (bm_loop_calc_opposite_co(l_tmp, tdir, tvec)) {
251  dist = len_v3v3(l_tmp->v->co, tvec);
252  }
253  else {
254  dist = (BM_edge_calc_length(e_prev) + BM_edge_calc_length(e_next)) / 2.0f;
255  }
256 
257  normalize_v3_length(vec_accum, dist);
258  }
259 #endif
260  }
261  }
262 
263  copy_v3_v3(r_slide_vec, vec_accum);
264  return l;
265  }
266 
267  /* accumulate the normalized edge vector,
268  * normalize so some edges don't skew the result */
269  float tvec[3];
270  sub_v3_v3v3(tvec, BM_edge_other_vert(l->e, v)->co, v->co);
271  vec_accum_len += normalize_v3(tvec);
272  add_v3_v3(vec_accum, tvec);
273  i += 1;
274 
275  if (BM_loop_other_edge_loop(l, v)->e == e_next) {
276  if (i) {
277  normalize_v3_length(vec_accum, vec_accum_len / (float)i);
278  }
279 
280  copy_v3_v3(r_slide_vec, vec_accum);
281  return BM_loop_other_edge_loop(l, v);
282  }
283 
284  } while ((l != l->radial_next) && ((l = l->radial_next) != l_first));
285 
286  if (i) {
287  normalize_v3_length(vec_accum, vec_accum_len / (float)i);
288  }
289 
290  copy_v3_v3(r_slide_vec, vec_accum);
291 
292  return NULL;
293 }
294 
295 static void edge_slide_projmat_get(TransInfo *t, TransDataContainer *tc, float r_projectMat[4][4])
296 {
297  RegionView3D *rv3d = NULL;
298 
299  if (t->spacetype == SPACE_VIEW3D) {
300  /* Background mode support. */
301  rv3d = t->region ? t->region->regiondata : NULL;
302  }
303 
304  if (!rv3d) {
305  /* Ok, let's try to survive this. */
306  unit_m4(r_projectMat);
307  }
308  else {
309  ED_view3d_ob_project_mat_get(rv3d, tc->obedit, r_projectMat);
310  }
311 }
312 
314  ARegion *region,
315  float projectMat[4][4],
316  float r_sco_a[3],
317  float r_sco_b[3])
318 {
319  BMVert *v = sv->v;
320 
321  if (sv->v_side[1]) {
322  ED_view3d_project_float_v3_m4(region, sv->v_side[1]->co, r_sco_b, projectMat);
323  }
324  else {
325  add_v3_v3v3(r_sco_b, v->co, sv->dir_side[1]);
326  ED_view3d_project_float_v3_m4(region, r_sco_b, r_sco_b, projectMat);
327  }
328 
329  if (sv->v_side[0]) {
330  ED_view3d_project_float_v3_m4(region, sv->v_side[0]->co, r_sco_a, projectMat);
331  }
332  else {
333  add_v3_v3v3(r_sco_a, v->co, sv->dir_side[0]);
334  ED_view3d_project_float_v3_m4(region, r_sco_a, r_sco_a, projectMat);
335  }
336 }
337 
338 static void edge_slide_data_init_mval(MouseInput *mi, EdgeSlideData *sld, float *mval_dir)
339 {
340  /* Possible all of the edge loops are pointing directly at the view. */
341  if (UNLIKELY(len_squared_v2(mval_dir) < 0.1f)) {
342  mval_dir[0] = 0.0f;
343  mval_dir[1] = 100.0f;
344  }
345 
346  float mval_start[2], mval_end[2];
347 
348  /* Zero out Start. */
349  zero_v2(mval_start);
350 
351  /* dir holds a vector along edge loop */
352  copy_v2_v2(mval_end, mval_dir);
353  mul_v2_fl(mval_end, 0.5f);
354 
355  sld->mval_start[0] = mi->imval[0] + mval_start[0];
356  sld->mval_start[1] = mi->imval[1] + mval_start[1];
357 
358  sld->mval_end[0] = mi->imval[0] + mval_end[0];
359  sld->mval_end[1] = mi->imval[1] + mval_end[1];
360 }
361 
366  TransDataContainer *tc,
367  EdgeSlideData *sld,
368  const int *sv_table,
369  const int loop_nr,
370  const float mval[2],
371  const bool use_occlude_geometry,
372  const bool use_calc_direction)
373 {
376  ARegion *region = t->region;
377  View3D *v3d = NULL;
378  float projectMat[4][4];
379  BMBVHTree *bmbvh;
380 
381  /* only for use_calc_direction */
382  float(*loop_dir)[3] = NULL, *loop_maxdist = NULL;
383 
384  float mval_dir[3], dist_best_sq;
385 
386  if (t->spacetype == SPACE_VIEW3D) {
387  /* background mode support */
388  v3d = t->area ? t->area->spacedata.first : NULL;
389  }
390 
391  edge_slide_projmat_get(t, tc, projectMat);
392 
393  if (use_occlude_geometry) {
395  }
396  else {
397  bmbvh = NULL;
398  }
399 
400  /* find mouse vectors, the global one, and one per loop in case we have
401  * multiple loops selected, in case they are oriented different */
402  zero_v3(mval_dir);
403  dist_best_sq = -1.0f;
404 
405  if (use_calc_direction) {
406  loop_dir = MEM_callocN(sizeof(float[3]) * loop_nr, "sv loop_dir");
407  loop_maxdist = MEM_mallocN(sizeof(float) * loop_nr, "sv loop_maxdist");
408  copy_vn_fl(loop_maxdist, loop_nr, -1.0f);
409  }
410 
411  sv = &sld->sv[0];
412  for (int i = 0; i < sld->totsv; i++, sv++) {
413  BMIter iter_other;
414  BMEdge *e;
415  BMVert *v = sv->v;
416 
417  UNUSED_VARS_NDEBUG(sv_table); /* silence warning */
418  BLI_assert(i == sv_table[BM_elem_index_get(v)]);
419 
420  /* Search cross edges for visible edge to the mouse cursor,
421  * then use the shared vertex to calculate screen vector. */
422  BM_ITER_ELEM (e, &iter_other, v, BM_EDGES_OF_VERT) {
423  /* screen-space coords */
424  float sco_a[3], sco_b[3];
425  float dist_sq;
426  int l_nr;
427 
429  continue;
430  }
431 
432  /* This test is only relevant if object is not wire-drawn! See T32068. */
433  bool is_visible = !use_occlude_geometry ||
434  BMBVH_EdgeVisible(bmbvh, e, t->depsgraph, region, v3d, tc->obedit);
435 
436  if (!is_visible && !use_calc_direction) {
437  continue;
438  }
439 
440  edge_slide_pair_project(sv, region, projectMat, sco_a, sco_b);
441 
442  /* global direction */
443  dist_sq = dist_squared_to_line_segment_v2(mval, sco_b, sco_a);
444  if (is_visible) {
445  if ((dist_best_sq == -1.0f) ||
446  /* intentionally use 2d size on 3d vector */
447  (dist_sq < dist_best_sq && (len_squared_v2v2(sco_b, sco_a) > 0.1f))) {
448  dist_best_sq = dist_sq;
449  sub_v3_v3v3(mval_dir, sco_b, sco_a);
450  }
451  }
452 
453  if (use_calc_direction) {
454  /* per loop direction */
455  l_nr = sv->loop_nr;
456  if (loop_maxdist[l_nr] == -1.0f || dist_sq < loop_maxdist[l_nr]) {
457  loop_maxdist[l_nr] = dist_sq;
458  sub_v3_v3v3(loop_dir[l_nr], sco_b, sco_a);
459  }
460  }
461  }
462  }
463 
464  if (use_calc_direction) {
465  int i;
466  sv = &sld->sv[0];
467  for (i = 0; i < sld->totsv; i++, sv++) {
468  /* switch a/b if loop direction is different from global direction */
469  int l_nr = sv->loop_nr;
470  if (dot_v3v3(loop_dir[l_nr], mval_dir) < 0.0f) {
471  swap_v3_v3(sv->dir_side[0], sv->dir_side[1]);
472  SWAP(BMVert *, sv->v_side[0], sv->v_side[1]);
473  }
474  }
475 
476  MEM_freeN(loop_dir);
477  MEM_freeN(loop_maxdist);
478  }
479 
480  edge_slide_data_init_mval(&t->mouse, sld, mval_dir);
481 
482  if (bmbvh) {
483  BKE_bmbvh_free(bmbvh);
484  }
485 }
486 
488  TransDataContainer *tc,
489  EdgeSlideData *sld,
490  const float mval[2])
491 {
492  TransDataEdgeSlideVert *sv = sld->sv;
493 
494  if (sld->totsv > 0) {
495  ARegion *region = t->region;
496  float projectMat[4][4];
497 
498  int i = 0;
499 
500  float v_proj[2];
501  float dist_sq = 0;
502  float dist_min_sq = FLT_MAX;
503 
504  edge_slide_projmat_get(t, tc, projectMat);
505 
506  for (i = 0; i < sld->totsv; i++, sv++) {
507  /* Set length */
508  sv->edge_len = len_v3v3(sv->dir_side[0], sv->dir_side[1]);
509 
510  ED_view3d_project_float_v2_m4(region, sv->v->co, v_proj, projectMat);
511  dist_sq = len_squared_v2v2(mval, v_proj);
512  if (dist_sq < dist_min_sq) {
513  dist_min_sq = dist_sq;
514  sld->curr_sv_index = i;
515  }
516  }
517  }
518  else {
519  sld->curr_sv_index = 0;
520  }
521 }
522 
524 {
526  BMesh *bm = em->bm;
527  BMIter iter;
528  BMEdge *e;
529  BMVert *v;
530  TransDataEdgeSlideVert *sv_array;
531  int sv_tot;
532  int *sv_table; /* BMVert -> sv_array index */
533  EdgeSlideData *sld = MEM_callocN(sizeof(*sld), "sld");
534  const float mval[2] = {(float)t->mval[0], (float)t->mval[1]};
535  int numsel, i, loop_nr;
536  bool use_occlude_geometry = false;
537  View3D *v3d = NULL;
538  RegionView3D *rv3d = NULL;
539 
540  sld->curr_sv_index = 0;
541 
542  /* Ensure valid selection. */
543  BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
545  BMIter iter2;
546  numsel = 0;
547  BM_ITER_ELEM (e, &iter2, v, BM_EDGES_OF_VERT) {
549  /* BMESH_TODO: this is probably very evil,
550  * set `v->e` to a selected edge. */
551  v->e = e;
552 
553  numsel++;
554  }
555  }
556 
557  if (numsel == 0 || numsel > 2) {
558  /* Invalid edge selection. */
559  MEM_freeN(sld);
560  return NULL;
561  }
562  }
563  }
564 
565  BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
567  /* NOTE: any edge with loops can work, but we won't get predictable results, so bail out. */
569  /* can edges with at least once face user */
570  MEM_freeN(sld);
571  return NULL;
572  }
573  }
574  }
575 
576  sv_table = MEM_mallocN(sizeof(*sv_table) * bm->totvert, __func__);
577 
578 #define INDEX_UNSET -1
579 #define INDEX_INVALID -2
580 
581  {
582  int j = 0;
583  BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
586  sv_table[i] = INDEX_UNSET;
587  j += 1;
588  }
589  else {
591  sv_table[i] = INDEX_INVALID;
592  }
593  BM_elem_index_set(v, i); /* set_inline */
594  }
596 
597  if (!j) {
598  MEM_freeN(sld);
599  MEM_freeN(sv_table);
600  return NULL;
601  }
602  sv_tot = j;
603  }
604 
605  sv_array = MEM_callocN(sizeof(TransDataEdgeSlideVert) * sv_tot, "sv_array");
606  loop_nr = 0;
607 
608  STACK_DECLARE(sv_array);
609  STACK_INIT(sv_array, sv_tot);
610 
611  while (1) {
612  float vec_a[3], vec_b[3];
613  BMLoop *l_a, *l_b;
614  BMLoop *l_a_prev, *l_b_prev;
615  BMVert *v_first;
616  /* If this succeeds call get_next_loop()
617  * which calculates the direction to slide based on clever checks.
618  *
619  * otherwise we simply use 'e_dir' as an edge-rail.
620  * (which is better when the attached edge is a boundary, see: T40422)
621  */
622 #define EDGESLIDE_VERT_IS_INNER(v, e_dir) \
623  ((BM_edge_is_boundary(e_dir) == false) && (BM_vert_edge_count_nonwire(v) == 2))
624 
625  v = NULL;
626  BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
628  break;
629  }
630  }
631 
632  if (!v) {
633  break;
634  }
635 
636  if (!v->e) {
637  continue;
638  }
639 
640  v_first = v;
641 
642  /* Walk along the edge loop. */
643  e = v->e;
644 
645  /* First, rewind. */
646  do {
647  e = get_other_edge(v, e);
648  if (!e) {
649  e = v->e;
650  break;
651  }
652 
654  break;
655  }
656 
657  v = BM_edge_other_vert(e, v);
658  } while (e != v_first->e);
659 
661 
662  l_a = e->l;
663  l_b = e->l->radial_next;
664 
665  /* regarding e_next, use get_next_loop()'s improved interpolation where possible */
666  {
667  BMEdge *e_next = get_other_edge(v, e);
668  if (e_next) {
669  get_next_loop(v, l_a, e, e_next, vec_a);
670  }
671  else {
672  BMLoop *l_tmp = BM_loop_other_edge_loop(l_a, v);
673  if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) {
674  get_next_loop(v, l_a, e, l_tmp->e, vec_a);
675  }
676  else {
677  sub_v3_v3v3(vec_a, BM_edge_other_vert(l_tmp->e, v)->co, v->co);
678  }
679  }
680  }
681 
682  /* !BM_edge_is_boundary(e); */
683  if (l_b != l_a) {
684  BMEdge *e_next = get_other_edge(v, e);
685  if (e_next) {
686  get_next_loop(v, l_b, e, e_next, vec_b);
687  }
688  else {
689  BMLoop *l_tmp = BM_loop_other_edge_loop(l_b, v);
690  if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) {
691  get_next_loop(v, l_b, e, l_tmp->e, vec_b);
692  }
693  else {
694  sub_v3_v3v3(vec_b, BM_edge_other_vert(l_tmp->e, v)->co, v->co);
695  }
696  }
697  }
698  else {
699  l_b = NULL;
700  }
701 
702  l_a_prev = NULL;
703  l_b_prev = NULL;
704 
705 #define SV_FROM_VERT(v) \
706  ((sv_table[BM_elem_index_get(v)] == INDEX_UNSET) ? \
707  ((void)(sv_table[BM_elem_index_get(v)] = STACK_SIZE(sv_array)), \
708  STACK_PUSH_RET_PTR(sv_array)) : \
709  (&sv_array[sv_table[BM_elem_index_get(v)]]))
710 
711  /* Iterate over the loop. */
712  v_first = v;
713  do {
714  bool l_a_ok_prev;
715  bool l_b_ok_prev;
717  BMVert *v_prev;
718  BMEdge *e_prev;
719 
720  /* XXX, 'sv' will initialize multiple times, this is suspicious. see T34024. */
721  BLI_assert(v != NULL);
723  sv = SV_FROM_VERT(v);
724  sv->v = v;
725  copy_v3_v3(sv->v_co_orig, v->co);
726  sv->loop_nr = loop_nr;
727 
728  if (l_a || l_a_prev) {
729  BMLoop *l_tmp = BM_loop_other_edge_loop(l_a ? l_a : l_a_prev, v);
730  sv->v_side[0] = BM_edge_other_vert(l_tmp->e, v);
731  copy_v3_v3(sv->dir_side[0], vec_a);
732  }
733 
734  if (l_b || l_b_prev) {
735  BMLoop *l_tmp = BM_loop_other_edge_loop(l_b ? l_b : l_b_prev, v);
736  sv->v_side[1] = BM_edge_other_vert(l_tmp->e, v);
737  copy_v3_v3(sv->dir_side[1], vec_b);
738  }
739 
740  v_prev = v;
741  v = BM_edge_other_vert(e, v);
742 
743  e_prev = e;
744  e = get_other_edge(v, e);
745 
746  if (!e) {
747  BLI_assert(v != NULL);
748 
750  sv = SV_FROM_VERT(v);
751 
752  sv->v = v;
753  copy_v3_v3(sv->v_co_orig, v->co);
754  sv->loop_nr = loop_nr;
755 
756  if (l_a) {
757  BMLoop *l_tmp = BM_loop_other_edge_loop(l_a, v);
758  sv->v_side[0] = BM_edge_other_vert(l_tmp->e, v);
759  if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) {
760  get_next_loop(v, l_a, e_prev, l_tmp->e, sv->dir_side[0]);
761  }
762  else {
763  sub_v3_v3v3(sv->dir_side[0], sv->v_side[0]->co, v->co);
764  }
765  }
766 
767  if (l_b) {
768  BMLoop *l_tmp = BM_loop_other_edge_loop(l_b, v);
769  sv->v_side[1] = BM_edge_other_vert(l_tmp->e, v);
770  if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) {
771  get_next_loop(v, l_b, e_prev, l_tmp->e, sv->dir_side[1]);
772  }
773  else {
774  sub_v3_v3v3(sv->dir_side[1], sv->v_side[1]->co, v->co);
775  }
776  }
777 
780 
781  break;
782  }
783  l_a_ok_prev = (l_a != NULL);
784  l_b_ok_prev = (l_b != NULL);
785 
786  l_a_prev = l_a;
787  l_b_prev = l_b;
788 
789  if (l_a) {
790  l_a = get_next_loop(v, l_a, e_prev, e, vec_a);
791  }
792  else {
793  zero_v3(vec_a);
794  }
795 
796  if (l_b) {
797  l_b = get_next_loop(v, l_b, e_prev, e, vec_b);
798  }
799  else {
800  zero_v3(vec_b);
801  }
802 
803  if (l_a && l_b) {
804  /* pass */
805  }
806  else {
807  if (l_a || l_b) {
808  /* find the opposite loop if it was missing previously */
809  if (l_a == NULL && l_b && (l_b->radial_next != l_b)) {
810  l_a = l_b->radial_next;
811  }
812  else if (l_b == NULL && l_a && (l_a->radial_next != l_a)) {
813  l_b = l_a->radial_next;
814  }
815  }
816  else if (e->l != NULL) {
817  /* if there are non-contiguous faces, we can still recover
818  * the loops of the new edges faces */
819 
820  /* NOTE:, the behavior in this case means edges may move in opposite directions,
821  * this could be made to work more usefully. */
822 
823  if (l_a_ok_prev) {
824  l_a = e->l;
825  l_b = (l_a->radial_next != l_a) ? l_a->radial_next : NULL;
826  }
827  else if (l_b_ok_prev) {
828  l_b = e->l;
829  l_a = (l_b->radial_next != l_b) ? l_b->radial_next : NULL;
830  }
831  }
832 
833  if (!l_a_ok_prev && l_a) {
834  get_next_loop(v, l_a, e, e_prev, vec_a);
835  }
836  if (!l_b_ok_prev && l_b) {
837  get_next_loop(v, l_b, e, e_prev, vec_b);
838  }
839  }
840 
843  } while ((e != v_first->e) && (l_a || l_b));
844 
845 #undef SV_FROM_VERT
846 #undef INDEX_UNSET
847 #undef INDEX_INVALID
848 
849  loop_nr++;
850 
851 #undef EDGESLIDE_VERT_IS_INNER
852  }
853 
854  // EDBM_flag_disable_all(em, BM_ELEM_SELECT);
855 
856  BLI_assert(STACK_SIZE(sv_array) == (uint)sv_tot);
857 
858  sld->sv = sv_array;
859  sld->totsv = sv_tot;
860 
861  /* use for visibility checks */
862  if (t->spacetype == SPACE_VIEW3D) {
863  v3d = t->area ? t->area->spacedata.first : NULL;
864  rv3d = t->region ? t->region->regiondata : NULL;
865  use_occlude_geometry = (v3d && TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->dt > OB_WIRE &&
866  !XRAY_ENABLED(v3d));
867  }
868 
869  calcEdgeSlide_mval_range(t, tc, sld, sv_table, loop_nr, mval, use_occlude_geometry, true);
870 
871  if (rv3d) {
872  calcEdgeSlide_even(t, tc, sld, mval);
873  }
874 
875  MEM_freeN(sv_table);
876 
877  return sld;
878 }
879 
885 {
887  BMesh *bm = em->bm;
888  BMIter iter;
889  BMEdge *e;
890  TransDataEdgeSlideVert *sv_array;
891  int sv_tot;
892  int *sv_table; /* BMVert -> sv_array index */
893  EdgeSlideData *sld = MEM_callocN(sizeof(*sld), "sld");
894  const float mval[2] = {(float)t->mval[0], (float)t->mval[1]};
895  int loop_nr;
896  bool use_occlude_geometry = false;
897  View3D *v3d = NULL;
898  RegionView3D *rv3d = NULL;
899 
900  if (t->spacetype == SPACE_VIEW3D) {
901  /* background mode support */
902  v3d = t->area ? t->area->spacedata.first : NULL;
903  rv3d = t->region ? t->region->regiondata : NULL;
904  }
905 
906  sld->curr_sv_index = 0;
907  /* ensure valid selection */
908  {
909  int i = 0, j = 0;
910  BMVert *v;
911 
912  BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
914  float len_sq_max = -1.0f;
915  BMIter iter2;
916  BM_ITER_ELEM (e, &iter2, v, BM_EDGES_OF_VERT) {
918  float len_sq = BM_edge_calc_length_squared(e);
919  if (len_sq > len_sq_max) {
920  len_sq_max = len_sq;
921  v->e = e;
922  }
923  }
924  }
925 
926  if (len_sq_max != -1.0f) {
927  j++;
928  }
929  }
930  BM_elem_index_set(v, i); /* set_inline */
931  }
933 
934  if (!j) {
935  MEM_freeN(sld);
936  return NULL;
937  }
938 
939  sv_tot = j;
940  }
941 
942  BLI_assert(sv_tot != 0);
943  /* over alloc */
944  sv_array = MEM_callocN(sizeof(TransDataEdgeSlideVert) * bm->totvertsel, "sv_array");
945 
946  /* Same loop for all loops, weak but we don't connect loops in this case. */
947  loop_nr = 1;
948 
949  sv_table = MEM_mallocN(sizeof(*sv_table) * bm->totvert, __func__);
950 
951  {
952  int i = 0, j = 0;
953  BMVert *v;
954 
955  BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
956  sv_table[i] = -1;
957  if ((v->e != NULL) && (BM_elem_flag_test(v, BM_ELEM_SELECT))) {
958  if (BM_elem_flag_test(v->e, BM_ELEM_SELECT) == 0) {
960  sv = &sv_array[j];
961  sv->v = v;
962  copy_v3_v3(sv->v_co_orig, v->co);
963  sv->v_side[0] = BM_edge_other_vert(v->e, v);
964  sub_v3_v3v3(sv->dir_side[0], sv->v_side[0]->co, v->co);
965  sv->loop_nr = 0;
966  sv_table[i] = j;
967  j += 1;
968  }
969  }
970  }
971  }
972 
973  /* check for wire vertices,
974  * interpolate the directions of wire verts between non-wire verts */
975  if (sv_tot != bm->totvert) {
976  const int sv_tot_nowire = sv_tot;
977  TransDataEdgeSlideVert *sv_iter = sv_array;
978 
979  for (int i = 0; i < sv_tot_nowire; i++, sv_iter++) {
980  BMIter eiter;
981  BM_ITER_ELEM (e, &eiter, sv_iter->v, BM_EDGES_OF_VERT) {
982  /* walk over wire */
983  TransDataEdgeSlideVert *sv_end = NULL;
984  BMEdge *e_step = e;
985  BMVert *v = sv_iter->v;
986  int j;
987 
988  j = sv_tot;
989 
990  while (1) {
991  BMVert *v_other = BM_edge_other_vert(e_step, v);
992  int endpoint = ((sv_table[BM_elem_index_get(v_other)] != -1) +
993  (BM_vert_is_edge_pair(v_other) == false));
994 
995  if ((BM_elem_flag_test(e_step, BM_ELEM_SELECT) &&
996  BM_elem_flag_test(v_other, BM_ELEM_SELECT)) &&
997  (endpoint == 0)) {
998  /* scan down the list */
1000  BLI_assert(sv_table[BM_elem_index_get(v_other)] == -1);
1001  sv_table[BM_elem_index_get(v_other)] = j;
1002  sv = &sv_array[j];
1003  sv->v = v_other;
1004  copy_v3_v3(sv->v_co_orig, v_other->co);
1005  copy_v3_v3(sv->dir_side[0], sv_iter->dir_side[0]);
1006  j++;
1007 
1008  /* advance! */
1009  v = v_other;
1010  e_step = BM_DISK_EDGE_NEXT(e_step, v_other);
1011  }
1012  else {
1013  if ((endpoint == 2) && (sv_tot != j)) {
1014  BLI_assert(BM_elem_index_get(v_other) != -1);
1015  sv_end = &sv_array[sv_table[BM_elem_index_get(v_other)]];
1016  }
1017  break;
1018  }
1019  }
1020 
1021  if (sv_end) {
1022  int sv_tot_prev = sv_tot;
1023  const float *co_src = sv_iter->v->co;
1024  const float *co_dst = sv_end->v->co;
1025  const float *dir_src = sv_iter->dir_side[0];
1026  const float *dir_dst = sv_end->dir_side[0];
1027  sv_tot = j;
1028 
1029  while (j-- != sv_tot_prev) {
1030  float factor;
1031  factor = line_point_factor_v3(sv_array[j].v->co, co_src, co_dst);
1032  interp_v3_v3v3(sv_array[j].dir_side[0], dir_src, dir_dst, factor);
1033  }
1034  }
1035  }
1036  }
1037  }
1038 
1039  // EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1040 
1041  sld->sv = sv_array;
1042  sld->totsv = sv_tot;
1043 
1044  /* use for visibility checks */
1045  if (t->spacetype == SPACE_VIEW3D) {
1046  v3d = t->area ? t->area->spacedata.first : NULL;
1047  rv3d = t->region ? t->region->regiondata : NULL;
1048  use_occlude_geometry = (v3d && TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->dt > OB_WIRE &&
1049  !XRAY_ENABLED(v3d));
1050  }
1051 
1052  calcEdgeSlide_mval_range(t, tc, sld, sv_table, loop_nr, mval, use_occlude_geometry, false);
1053 
1054  if (rv3d) {
1055  calcEdgeSlide_even(t, tc, sld, mval);
1056  }
1057 
1058  MEM_freeN(sv_table);
1059 
1060  return sld;
1061 }
1062 
1065  TransCustomData *custom_data)
1066 {
1067  EdgeSlideData *sld = custom_data->data;
1068 
1069  if (sld == NULL) {
1070  return;
1071  }
1072 
1073  MEM_freeN(sld->sv);
1074  MEM_freeN(sld);
1075 
1076  custom_data->data = NULL;
1077 }
1078 
1079 static eRedrawFlag handleEventEdgeSlide(struct TransInfo *t, const struct wmEvent *event)
1080 {
1081  if (t->mode == TFM_EDGE_SLIDE) {
1082  EdgeSlideParams *slp = t->custom.mode.data;
1083 
1084  if (slp) {
1085  switch (event->type) {
1086  case EVT_EKEY:
1087  if (event->val == KM_PRESS) {
1088  slp->use_even = !slp->use_even;
1090  return TREDRAW_HARD;
1091  }
1092  break;
1093  case EVT_FKEY:
1094  if (event->val == KM_PRESS) {
1095  slp->flipped = !slp->flipped;
1097  return TREDRAW_HARD;
1098  }
1099  break;
1100  case EVT_CKEY:
1101  /* use like a modifier key */
1102  if (event->val == KM_PRESS) {
1103  t->flag ^= T_ALT_TRANSFORM;
1105  return TREDRAW_HARD;
1106  }
1107  break;
1108  case EVT_MODAL_MAP:
1109 #if 0
1110  switch (event->val) {
1112  sld->curr_sv_index = ((sld->curr_sv_index - 1) + sld->totsv) % sld->totsv;
1113  return TREDRAW_HARD;
1115  sld->curr_sv_index = (sld->curr_sv_index + 1) % sld->totsv;
1116  return TREDRAW_HARD;
1117  }
1118 #endif
1119  break;
1120  case MOUSEMOVE:
1122  break;
1123  default:
1124  break;
1125  }
1126  }
1127  }
1128  return TREDRAW_NOTHING;
1129 }
1130 
1132 {
1133  if (t->mode != TFM_EDGE_SLIDE) {
1134  return;
1135  }
1136 
1138  if (sld == NULL) {
1139  return;
1140  }
1141 
1142  const EdgeSlideParams *slp = t->custom.mode.data;
1143  const bool is_clamp = !(t->flag & T_ALT_TRANSFORM);
1144 
1145  const float line_size = UI_GetThemeValuef(TH_OUTLINE_WIDTH) + 0.5f;
1146 
1148 
1150 
1151  GPU_matrix_push();
1153 
1155 
1157 
1158  if (slp->use_even == true) {
1159  /* Even mode */
1160  float co_a[3], co_b[3], co_mark[3];
1161  TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index];
1162  const float fac = (slp->perc + 1.0f) / 2.0f;
1163  const float ctrl_size = UI_GetThemeValuef(TH_FACEDOT_SIZE) + 1.5f;
1164  const float guide_size = ctrl_size - 0.5f;
1165  const int alpha_shade = -30;
1166 
1167  add_v3_v3v3(co_a, curr_sv->v_co_orig, curr_sv->dir_side[0]);
1168  add_v3_v3v3(co_b, curr_sv->v_co_orig, curr_sv->dir_side[1]);
1169 
1170  GPU_line_width(line_size);
1173  if (curr_sv->v_side[0]) {
1174  immVertex3fv(pos, curr_sv->v_side[0]->co);
1175  immVertex3fv(pos, curr_sv->v_co_orig);
1176  }
1177  if (curr_sv->v_side[1]) {
1178  immVertex3fv(pos, curr_sv->v_side[1]->co);
1179  immVertex3fv(pos, curr_sv->v_co_orig);
1180  }
1181  immEnd();
1182 
1183  {
1184  float *co_test = NULL;
1185  if (slp->flipped) {
1186  if (curr_sv->v_side[1]) {
1187  co_test = curr_sv->v_side[1]->co;
1188  }
1189  }
1190  else {
1191  if (curr_sv->v_side[0]) {
1192  co_test = curr_sv->v_side[0]->co;
1193  }
1194  }
1195 
1196  if (co_test != NULL) {
1197  immUniformThemeColorShadeAlpha(TH_SELECT, -30, alpha_shade);
1198  GPU_point_size(ctrl_size);
1200  immVertex3fv(pos, co_test);
1201  immEnd();
1202  }
1203  }
1204 
1205  immUniformThemeColorShadeAlpha(TH_SELECT, 255, alpha_shade);
1206  GPU_point_size(guide_size);
1208  interp_line_v3_v3v3v3(co_mark, co_b, curr_sv->v_co_orig, co_a, fac);
1209  immVertex3fv(pos, co_mark);
1210  immEnd();
1211  }
1212  else if (is_clamp == false) {
1213  const int side_index = sld->curr_side_unclamp;
1215  int i;
1216  const int alpha_shade = -160;
1217 
1218  GPU_line_width(line_size);
1220  immBegin(GPU_PRIM_LINES, sld->totsv * 2);
1221 
1222  /* TODO(campbell): Loop over all verts. */
1223  sv = sld->sv;
1224  for (i = 0; i < sld->totsv; i++, sv++) {
1225  float a[3], b[3];
1226 
1227  if (!is_zero_v3(sv->dir_side[side_index])) {
1228  copy_v3_v3(a, sv->dir_side[side_index]);
1229  }
1230  else {
1231  copy_v3_v3(a, sv->dir_side[!side_index]);
1232  }
1233 
1234  mul_v3_fl(a, 100.0f);
1235  negate_v3_v3(b, a);
1236  add_v3_v3(a, sv->v_co_orig);
1237  add_v3_v3(b, sv->v_co_orig);
1238 
1239  immVertex3fv(pos, a);
1240  immVertex3fv(pos, b);
1241  }
1242  immEnd();
1243  }
1244  else {
1245  /* Common case. */
1246  TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index];
1247  const int alpha_shade = -160;
1248 
1249  float co_dir[3];
1250  add_v3_v3v3(co_dir, curr_sv->v_co_orig, curr_sv->dir_side[sld->curr_side_unclamp]);
1251 
1252  GPU_line_width(line_size);
1255  immVertex3fv(pos, curr_sv->v_co_orig);
1256  immVertex3fv(pos, co_dir);
1257  immEnd();
1258  }
1259 
1260  immUnbindProgram();
1261 
1262  GPU_matrix_pop();
1263 
1265 
1267 }
1268 
1269 static void edge_slide_snap_apply(TransInfo *t, float *value)
1270 {
1272  EdgeSlideParams *slp = t->custom.mode.data;
1273  EdgeSlideData *sld_active = tc->custom.mode.data;
1274  TransDataEdgeSlideVert *sv = &sld_active->sv[sld_active->curr_sv_index];
1275  float snap_point[3], co_orig[3], co_dest[2][3], dvec[3];
1276 
1277  copy_v3_v3(co_orig, sv->v_co_orig);
1278  add_v3_v3v3(co_dest[0], co_orig, sv->dir_side[0]);
1279  add_v3_v3v3(co_dest[1], co_orig, sv->dir_side[1]);
1280  if (tc->use_local_mat) {
1281  mul_m4_v3(tc->mat, co_orig);
1282  mul_m4_v3(tc->mat, co_dest[0]);
1283  mul_m4_v3(tc->mat, co_dest[1]);
1284  }
1285 
1286  getSnapPoint(t, dvec);
1287  sub_v3_v3(dvec, t->tsnap.snapTarget);
1288  add_v3_v3v3(snap_point, co_orig, dvec);
1289 
1290  float perc = *value;
1291  int side_index;
1292  float t_mid;
1293  if (slp->use_even == false) {
1294  const bool is_clamp = !(t->flag & T_ALT_TRANSFORM);
1295  if (is_clamp) {
1296  side_index = perc < 0.0f;
1297  }
1298  else {
1299  side_index = sld_active->curr_side_unclamp;
1300  }
1301  }
1302  else {
1303  /* Could be pre-calculated. */
1304  t_mid = line_point_factor_v3((float[3]){0.0f, 0.0f, 0.0f}, sv->dir_side[0], sv->dir_side[1]);
1305 
1306  float t_snap = line_point_factor_v3(snap_point, co_dest[0], co_dest[1]);
1307  side_index = t_snap >= t_mid;
1308  }
1309 
1310  if (t->tsnap.snapElem & (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE_RAYCAST)) {
1311  float co_dir[3];
1312  sub_v3_v3v3(co_dir, co_dest[side_index], co_orig);
1313  normalize_v3(co_dir);
1314  if (t->tsnap.snapElem & SCE_SNAP_MODE_EDGE) {
1316  }
1317  else {
1319  }
1320  add_v3_v3v3(snap_point, co_orig, dvec);
1321  }
1322 
1323  perc = line_point_factor_v3(snap_point, co_orig, co_dest[side_index]);
1324  if (slp->use_even == false) {
1325  if (side_index) {
1326  perc *= -1;
1327  }
1328  }
1329  else {
1330  if (!side_index) {
1331  perc = (1.0f - perc) * t_mid;
1332  }
1333  else {
1334  perc = perc * (1.0f - t_mid) + t_mid;
1335  }
1336 
1337  if (slp->flipped) {
1338  perc = 1.0f - perc;
1339  }
1340 
1341  perc = (2 * perc) - 1.0f;
1342 
1343  if (!slp->flipped) {
1344  perc *= -1;
1345  }
1346  }
1347 
1348  *value = perc;
1349 }
1350 
1351 static void doEdgeSlide(TransInfo *t, float perc)
1352 {
1353  EdgeSlideParams *slp = t->custom.mode.data;
1354  EdgeSlideData *sld_active = edgeSlideFirstGet(t);
1355 
1356  slp->perc = perc;
1357 
1358  if (slp->use_even == false) {
1359  const bool is_clamp = !(t->flag & T_ALT_TRANSFORM);
1360  if (is_clamp) {
1361  const int side_index = (perc < 0.0f);
1362  const float perc_final = fabsf(perc);
1364  EdgeSlideData *sld = tc->custom.mode.data;
1365 
1366  if (sld == NULL) {
1367  continue;
1368  }
1369 
1370  TransDataEdgeSlideVert *sv = sld->sv;
1371  for (int i = 0; i < sld->totsv; i++, sv++) {
1372  madd_v3_v3v3fl(sv->v->co, sv->v_co_orig, sv->dir_side[side_index], perc_final);
1373  }
1374  sld->curr_side_unclamp = side_index;
1375  }
1376  }
1377  else {
1378  const float perc_init = fabsf(perc) *
1379  ((sld_active->curr_side_unclamp == (perc < 0.0f)) ? 1 : -1);
1380  const int side_index = sld_active->curr_side_unclamp;
1382  EdgeSlideData *sld = tc->custom.mode.data;
1383 
1384  if (sld == NULL) {
1385  continue;
1386  }
1387 
1388  TransDataEdgeSlideVert *sv = sld->sv;
1389  for (int i = 0; i < sld->totsv; i++, sv++) {
1390  float dir_flip[3];
1391  float perc_final = perc_init;
1392  if (!is_zero_v3(sv->dir_side[side_index])) {
1393  copy_v3_v3(dir_flip, sv->dir_side[side_index]);
1394  }
1395  else {
1396  copy_v3_v3(dir_flip, sv->dir_side[!side_index]);
1397  perc_final *= -1;
1398  }
1399  madd_v3_v3v3fl(sv->v->co, sv->v_co_orig, dir_flip, perc_final);
1400  }
1401  }
1402  }
1403  }
1404  else {
1415  TransDataEdgeSlideVert *curr_sv = &sld_active->sv[sld_active->curr_sv_index];
1416  const float curr_length_perc = curr_sv->edge_len *
1417  (((slp->flipped ? perc : -perc) + 1.0f) / 2.0f);
1418 
1419  float co_a[3];
1420  float co_b[3];
1421 
1423  EdgeSlideData *sld = tc->custom.mode.data;
1424 
1425  if (sld == NULL) {
1426  continue;
1427  }
1428 
1429  TransDataEdgeSlideVert *sv = sld->sv;
1430  for (int i = 0; i < sld->totsv; i++, sv++) {
1431  if (sv->edge_len > FLT_EPSILON) {
1432  const float fac = min_ff(sv->edge_len, curr_length_perc) / sv->edge_len;
1433 
1434  add_v3_v3v3(co_a, sv->v_co_orig, sv->dir_side[0]);
1435  add_v3_v3v3(co_b, sv->v_co_orig, sv->dir_side[1]);
1436 
1437  if (slp->flipped) {
1438  interp_line_v3_v3v3v3(sv->v->co, co_b, sv->v_co_orig, co_a, fac);
1439  }
1440  else {
1441  interp_line_v3_v3v3v3(sv->v->co, co_a, sv->v_co_orig, co_b, fac);
1442  }
1443  }
1444  }
1445  }
1446  }
1447 }
1448 
1449 static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2]))
1450 {
1451  char str[UI_MAX_DRAW_STR];
1452  size_t ofs = 0;
1453  float final;
1454  EdgeSlideParams *slp = t->custom.mode.data;
1455  bool flipped = slp->flipped;
1456  bool use_even = slp->use_even;
1457  const bool is_clamp = !(t->flag & T_ALT_TRANSFORM);
1458  const bool is_constrained = !(is_clamp == false || hasNumInput(&t->num));
1459 
1460  final = t->values[0] + t->values_modal_offset[0];
1461 
1462  applySnappingAsGroup(t, &final);
1463  if (!validSnap(t)) {
1464  transform_snap_increment(t, &final);
1465  }
1466 
1467  /* only do this so out of range values are not displayed */
1468  if (is_constrained) {
1469  CLAMP(final, -1.0f, 1.0f);
1470  }
1471 
1472  applyNumInput(&t->num, &final);
1473 
1474  t->values_final[0] = final;
1475 
1476  /* header string */
1477  ofs += BLI_strncpy_rlen(str + ofs, TIP_("Edge Slide: "), sizeof(str) - ofs);
1478  if (hasNumInput(&t->num)) {
1479  char c[NUM_STR_REP_LEN];
1480  outputNumInput(&(t->num), c, &t->scene->unit);
1481  ofs += BLI_strncpy_rlen(str + ofs, &c[0], sizeof(str) - ofs);
1482  }
1483  else {
1484  ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, "%.4f ", final);
1485  }
1486  ofs += BLI_snprintf_rlen(
1487  str + ofs, sizeof(str) - ofs, TIP_("(E)ven: %s, "), WM_bool_as_string(use_even));
1488  if (use_even) {
1489  ofs += BLI_snprintf_rlen(
1490  str + ofs, sizeof(str) - ofs, TIP_("(F)lipped: %s, "), WM_bool_as_string(flipped));
1491  }
1492  ofs += BLI_snprintf_rlen(
1493  str + ofs, sizeof(str) - ofs, TIP_("Alt or (C)lamp: %s"), WM_bool_as_string(is_clamp));
1494  /* done with header string */
1495 
1496  /* do stuff here */
1497  doEdgeSlide(t, final);
1498 
1499  recalcData(t);
1500 
1501  ED_area_status_text(t->area, str);
1502 }
1503 
1505  TransInfo *t, bool use_double_side, bool use_even, bool flipped, bool use_clamp)
1506 {
1507  EdgeSlideData *sld;
1508  bool ok = false;
1509 
1510  t->mode = TFM_EDGE_SLIDE;
1511  t->transform = applyEdgeSlide;
1512  t->handleEvent = handleEventEdgeSlide;
1513  t->tsnap.applySnap = edge_slide_snap_apply;
1514  t->tsnap.distance = transform_snap_distance_len_squared_fn;
1515 
1516  {
1517  EdgeSlideParams *slp = MEM_callocN(sizeof(*slp), __func__);
1518  slp->use_even = use_even;
1519  slp->flipped = flipped;
1520  /* happens to be best for single-sided */
1521  if (use_double_side == false) {
1522  slp->flipped = !flipped;
1523  }
1524  slp->perc = 0.0f;
1525 
1526  if (!use_clamp) {
1527  t->flag |= T_ALT_TRANSFORM;
1528  }
1529 
1530  t->custom.mode.data = slp;
1531  t->custom.mode.use_free = true;
1532  }
1533 
1535  sld = use_double_side ? createEdgeSlideVerts_double_side(t, tc) :
1537  if (sld) {
1538  tc->custom.mode.data = sld;
1539  tc->custom.mode.free_cb = freeEdgeSlideVerts;
1540  ok = true;
1541  }
1542  }
1543 
1544  if (!ok) {
1545  t->state = TRANS_CANCEL;
1546  return;
1547  }
1548 
1549  /* set custom point first if you want value to be initialized by init */
1552 
1553  t->idx_max = 0;
1554  t->num.idx_max = 0;
1555  t->snap[0] = 0.1f;
1556  t->snap[1] = t->snap[0] * 0.1f;
1557 
1558  copy_v3_fl(t->num.val_inc, t->snap[0]);
1559  t->num.unit_sys = t->scene->unit.system;
1560  t->num.unit_type[0] = B_UNIT_NONE;
1561 
1562  t->flag |= T_NO_CONSTRAINT | T_NO_PROJECT;
1563 }
1564 
1566 {
1567  initEdgeSlide_ex(t, true, false, false, true);
1568 }
1569 
1572 /* -------------------------------------------------------------------- */
1577 {
1578  ARegion *region = t->region;
1579 
1581  EdgeSlideData *sld = tc->custom.mode.data;
1582  if (sld) {
1583  float projectMat[4][4];
1584  edge_slide_projmat_get(t, tc, projectMat);
1585 
1586  TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index];
1587 
1588  float mval_dir[3], sco_a[3], sco_b[3];
1589  edge_slide_pair_project(curr_sv, region, projectMat, sco_a, sco_b);
1590  sub_v3_v3v3(mval_dir, sco_b, sco_a);
1591  edge_slide_data_init_mval(&t->mouse, sld, mval_dir);
1592  }
1593  }
1594 
1596  setCustomPoints(t, &t->mouse, sld->mval_end, sld->mval_start);
1597 }
1598 
typedef float(TangentPoint)[2]
BMEditMesh * BKE_editmesh_from_object(struct Object *ob)
Return the BMEditMesh for a given object.
Definition: editmesh.c:58
void BKE_bmbvh_free(BMBVHTree *tree)
Definition: editmesh_bvh.c:170
BMBVHTree * BKE_bmbvh_new_from_editmesh(struct BMEditMesh *em, int flag, const float(*cos_cage)[3], bool cos_cage_free)
Definition: editmesh_bvh.c:31
@ BMBVH_RESPECT_HIDDEN
@ B_UNIT_NONE
Definition: BKE_unit.h:100
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition: BLI_assert.h:53
MINLINE float min_ff(float a, float b)
float line_point_factor_v3(const float p[3], const float l1[3], const float l2[3])
Definition: math_geom.c:3254
bool isect_line_plane_v3(float r_isect_co[3], const float l1[3], const float l2[3], const float plane_co[3], const float plane_no[3]) ATTR_WARN_UNUSED_RESULT
Definition: math_geom.c:2078
float dist_squared_to_line_segment_v2(const float p[2], const float l1[2], const float l2[2])
Definition: math_geom.c:283
void unit_m4(float m[4][4])
Definition: rct.c:1090
void mul_m4_v3(const float M[4][4], float r[3])
Definition: math_matrix.c:729
MINLINE float len_squared_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v3(float r[3])
MINLINE void sub_v3_v3(float r[3], const float a[3])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v2_fl(float r[2], float f)
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void negate_v3_v3(float r[3], const float a[3])
MINLINE bool is_zero_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
void copy_vn_fl(float *array_tar, int size, float val)
Definition: math_vector.c:1259
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
Definition: math_vector.c:29
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float normalize_v3_length(float r[3], float unit_scale)
MINLINE void zero_v2(float r[2])
MINLINE void swap_v3_v3(float a[3], float b[3])
MINLINE void copy_v3_fl(float r[3], float f)
MINLINE void madd_v3_v3v3fl(float r[3], const float a[3], const float b[3], float f)
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
size_t BLI_strncpy_rlen(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:120
size_t BLI_snprintf_rlen(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
unsigned int uint
Definition: BLI_sys_types.h:67
#define SWAP(type, a, b)
#define UNUSED_VARS_NDEBUG(...)
#define UNUSED(x)
#define UNLIKELY(x)
#define ELEM(...)
#define STACK_DECLARE(stack)
#define STACK_SIZE(stack)
#define STACK_INIT(stack, stack_num)
#define TIP_(msgid)
@ OB_WIRE
@ SCE_SNAP_MODE_EDGE
@ SCE_SNAP_MODE_FACE_RAYCAST
@ SPACE_VIEW3D
bool BMBVH_EdgeVisible(struct BMBVHTree *tree, struct BMEdge *e, struct Depsgraph *depsgraph, struct ARegion *region, struct View3D *v3d, struct Object *obedit)
void outputNumInput(NumInput *n, char *str, struct UnitSettings *unit_settings)
Definition: numinput.c:87
#define NUM_STR_REP_LEN
Definition: ED_numinput.h:13
bool applyNumInput(NumInput *n, float *vec)
Definition: numinput.c:189
bool hasNumInput(const NumInput *n)
Definition: numinput.c:170
void ED_area_status_text(ScrArea *area, const char *str)
Definition: area.c:792
@ TFM_EDGE_SLIDE
Definition: ED_transform.h:59
#define XRAY_ENABLED(v3d)
Definition: ED_view3d.h:1299
void ED_view3d_project_float_v3_m4(const struct ARegion *region, const float co[3], float r_co[3], const float mat[4][4])
void ED_view3d_ob_project_mat_get(const struct RegionView3D *v3d, const struct Object *ob, float r_pmat[4][4])
void ED_view3d_project_float_v2_m4(const struct ARegion *region, const float co[3], float r_co[2], const float mat[4][4])
void immUniformThemeColorShadeAlpha(int color_id, int color_offset, int alpha_offset)
void immUnbindProgram(void)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
void immBeginAtMost(GPUPrimType, uint max_vertex_len)
GPUVertFormat * immVertexFormat(void)
void immVertex3fv(uint attr_id, const float data[3])
void immBegin(GPUPrimType, uint vertex_len)
void immEnd(void)
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble t
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble v1
void GPU_matrix_pop(void)
Definition: gpu_matrix.cc:126
#define GPU_matrix_mul(x)
Definition: GPU_matrix.h:224
void GPU_matrix_push(void)
Definition: gpu_matrix.cc:119
@ GPU_PRIM_LINES
Definition: GPU_primitive.h:20
@ GPU_PRIM_POINTS
Definition: GPU_primitive.h:19
@ GPU_SHADER_3D_UNIFORM_COLOR
Definition: GPU_shader.h:230
@ GPU_BLEND_NONE
Definition: GPU_state.h:60
@ GPU_BLEND_ALPHA
Definition: GPU_state.h:62
void GPU_blend(eGPUBlend blend)
Definition: gpu_state.cc:39
void GPU_line_width(float width)
Definition: gpu_state.cc:158
void GPU_point_size(float size)
Definition: gpu_state.cc:164
@ GPU_DEPTH_LESS_EQUAL
Definition: GPU_state.h:86
@ GPU_DEPTH_NONE
Definition: GPU_state.h:83
void GPU_depth_test(eGPUDepthTest test)
Definition: gpu_state.cc:65
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
Read Guarded memory(de)allocation.
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position CLAMP
#define UI_MAX_DRAW_STR
Definition: UI_interface.h:91
@ TH_FACEDOT_SIZE
Definition: UI_resources.h:96
@ TH_EDGE_SELECT
Definition: UI_resources.h:85
@ TH_OUTLINE_WIDTH
Definition: UI_resources.h:82
@ TH_SELECT
Definition: UI_resources.h:72
float UI_GetThemeValuef(int colorid)
Definition: resources.c:1141
@ KM_PRESS
Definition: WM_types.h:267
#define BM_DISK_EDGE_NEXT(e, v)
Definition: bmesh_class.h:625
@ BM_VERT
Definition: bmesh_class.h:383
@ BM_ELEM_SELECT
Definition: bmesh_class.h:471
@ BM_ELEM_TAG
Definition: bmesh_class.h:484
#define BM_elem_index_get(ele)
Definition: bmesh_inline.h:110
#define BM_elem_flag_disable(ele, hflag)
Definition: bmesh_inline.h:15
#define BM_elem_index_set(ele, index)
Definition: bmesh_inline.h:111
#define BM_elem_flag_test(ele, hflag)
Definition: bmesh_inline.h:12
#define BM_elem_flag_enable(ele, hflag)
Definition: bmesh_inline.h:14
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
#define BM_ITER_MESH_INDEX(ele, iter, bm, itype, indexvar)
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_EDGES_OF_VERT
ATTR_WARN_UNUSED_RESULT BMesh * bm
float BM_edge_calc_length_squared(const BMEdge *e)
Definition: bmesh_query.c:533
BMVert * BM_edge_share_vert(BMEdge *e1, BMEdge *e2)
Definition: bmesh_query.c:1079
void BM_loop_calc_face_direction(const BMLoop *l, float r_dir[3])
BM_loop_calc_face_direction.
Definition: bmesh_query.c:1284
float BM_edge_calc_length(const BMEdge *e)
Definition: bmesh_query.c:528
bool BM_vert_is_edge_pair(const BMVert *v)
Definition: bmesh_query.c:568
BMLoop * BM_loop_other_edge_loop(BMLoop *l, BMVert *v)
Definition: bmesh_query.c:33
BMLoop * BM_face_vert_share_loop(BMFace *f, BMVert *v)
Return the Loop Shared by Face and Vertex.
Definition: bmesh_query.c:1100
BLI_INLINE bool BM_edge_is_manifold(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_edge_is_boundary(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE BMVert * BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_vert_in_edge(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMLoop * l_b
ATTR_WARN_UNUSED_RESULT const BMVert * v
#define str(s)
uint pos
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
#define fabsf(x)
Definition: metal/compat.h:219
static unsigned c
Definition: RandGen.cpp:83
static unsigned a[3]
Definition: RandGen.cpp:78
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
#define TRANS_DATA_CONTAINER_FIRST_OK(t)
void applyMouseInput(struct TransInfo *t, struct MouseInput *mi, const int mval[2], float output[3])
void setCustomPoints(TransInfo *t, MouseInput *mi, const int start[2], const int end[2])
void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode)
#define FOREACH_TRANS_DATA_CONTAINER(t, th)
struct BMesh * bm
Definition: BKE_editmesh.h:40
int len
Definition: bmesh_class.h:267
float no[3]
Definition: bmesh_class.h:271
struct BMVert * v
Definition: bmesh_class.h:153
struct BMEdge * e
Definition: bmesh_class.h:164
struct BMLoop * radial_next
Definition: bmesh_class.h:204
struct BMLoop * prev
Definition: bmesh_class.h:233
struct BMFace * f
Definition: bmesh_class.h:171
struct BMLoop * next
Definition: bmesh_class.h:233
float co[3]
Definition: bmesh_class.h:87
struct BMEdge * e
Definition: bmesh_class.h:97
int totvert
Definition: bmesh_class.h:297
char elem_index_dirty
Definition: bmesh_class.h:305
int totvertsel
Definition: bmesh_class.h:298
TransDataEdgeSlideVert * sv
short val
Definition: WM_types.h:680
short type
Definition: WM_types.h:678
void transform_constraint_snap_axis_to_face(const TransInfo *t, const float axis[3], float r_out[3])
void transform_constraint_snap_axis_to_edge(const TransInfo *t, const float axis[3], float r_out[3])
void recalcData(TransInfo *t)
conversion and adaptation of different datablocks to a common struct.
transform modes used by different operators.
static BMEdge * get_other_edge(BMVert *v, BMEdge *e)
struct TransDataEdgeSlideVert TransDataEdgeSlideVert
#define EDGESLIDE_VERT_IS_INNER(v, e_dir)
#define INDEX_INVALID
static void edge_slide_snap_apply(TransInfo *t, float *value)
static bool bm_loop_calc_opposite_co(BMLoop *l_tmp, const float plane_no[3], float r_co[3])
static EdgeSlideData * createEdgeSlideVerts_single_side(TransInfo *t, TransDataContainer *tc)
static void interp_line_v3_v3v3v3(float p[3], const float v1[3], const float v2[3], const float v3[3], float t)
static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2]))
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)
static void edge_slide_projmat_get(TransInfo *t, TransDataContainer *tc, float r_projectMat[4][4])
void transform_mode_edge_slide_reproject_input(TransInfo *t)
static void calcEdgeSlideCustomPoints(struct TransInfo *t)
static void edge_slide_data_init_mval(MouseInput *mi, EdgeSlideData *sld, float *mval_dir)
static EdgeSlideData * edgeSlideFirstGet(TransInfo *t)
#define INDEX_UNSET
static void calcEdgeSlide_even(TransInfo *t, TransDataContainer *tc, EdgeSlideData *sld, const float mval[2])
void initEdgeSlide_ex(TransInfo *t, bool use_double_side, bool use_even, bool flipped, bool use_clamp)
struct EdgeSlideParams EdgeSlideParams
static TransDataContainer * edge_slide_container_first_ok(TransInfo *t)
static void freeEdgeSlideVerts(TransInfo *UNUSED(t), TransDataContainer *UNUSED(tc), TransCustomData *custom_data)
static EdgeSlideData * createEdgeSlideVerts_double_side(TransInfo *t, TransDataContainer *tc)
static eRedrawFlag handleEventEdgeSlide(struct TransInfo *t, const struct wmEvent *event)
void drawEdgeSlide(TransInfo *t)
#define SV_FROM_VERT(v)
static BMLoop * get_next_loop(BMVert *v, BMLoop *l, BMEdge *e_prev, BMEdge *e_next, float r_slide_vec[3])
static void edge_slide_pair_project(TransDataEdgeSlideVert *sv, ARegion *region, float projectMat[4][4], float r_sco_a[3], float r_sco_b[3])
static void doEdgeSlide(TransInfo *t, float perc)
void initEdgeSlide(TransInfo *t)
struct EdgeSlideData EdgeSlideData
float transform_snap_distance_len_squared_fn(TransInfo *UNUSED(t), const float p1[3], const float p2[3])
bool validSnap(const TransInfo *t)
bool transform_snap_increment(const TransInfo *t, float *r_val)
void applySnappingAsGroup(TransInfo *t, float *vec)
void getSnapPoint(const TransInfo *t, float vec[3])
@ EVT_EKEY
@ EVT_MODAL_MAP
@ EVT_FKEY
@ EVT_CKEY
@ MOUSEMOVE
const char * WM_bool_as_string(bool test)
Definition: wm_keymap.c:2052