Blender V4.3
transform_mode_edge_slide.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BLI_math_matrix.h"
10#include "BLI_string.h"
11
12#include "BKE_editmesh.hh"
13#include "BKE_editmesh_bvh.hh"
14#include "BKE_unit.hh"
15
16#include "GPU_immediate.hh"
17#include "GPU_matrix.hh"
18
20
21#include "ED_mesh.hh"
22#include "ED_screen.hh"
23
24#include "WM_api.hh"
25
26#include "RNA_access.hh"
27
28#include "UI_interface.hh"
29#include "UI_view2d.hh"
30
31#include "BLT_translation.hh"
32
33#include "transform.hh"
35#include "transform_convert.hh"
36#include "transform_mode.hh"
37#include "transform_snap.hh"
38
39using namespace blender;
40
41/* -------------------------------------------------------------------- */
47
48 int mval_start[2], mval_end[2];
50
51 private:
52 float4x4 proj_mat;
53 float2 win_half;
54
55 public:
57 {
58 ARegion *region = t->region;
59 this->win_half = {region->winx / 2.0f, region->winy / 2.0f};
60
61 if (t->spacetype == SPACE_VIEW3D) {
62 RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
63 this->proj_mat = ED_view3d_ob_project_mat_get(rv3d, tc->obedit);
64
65 for (int i = 0; i < 4; i++) {
66 this->proj_mat[i][0] *= this->win_half[0];
67 this->proj_mat[i][1] *= this->win_half[1];
68 }
69 }
70 else {
71 const View2D *v2d = static_cast<View2D *>(t->view);
72 UI_view2d_view_to_region_m4(v2d, this->proj_mat.ptr());
73 this->proj_mat.location()[0] -= this->win_half[0];
74 this->proj_mat.location()[1] -= this->win_half[1];
75 }
76 }
77
78 void project(const TransDataEdgeSlideVert *svert, float2 &r_sco_a, float2 &r_sco_b) const
79 {
80 float3 iloc = svert->v_co_orig();
81 r_sco_a = math::project_point(this->proj_mat, iloc + svert->dir_side[0]).xy() + this->win_half;
82 r_sco_b = math::project_point(this->proj_mat, iloc + svert->dir_side[1]).xy() + this->win_half;
83 }
84};
85
87 wmOperator *op = nullptr;
88 float perc;
89
92
94 bool flipped;
95};
96
104{
106 if (tc->custom.mode.data) {
107 return tc;
108 }
109 }
110 BLI_assert_msg(0, "Should never happen, at least one EdgeSlideData should be valid");
111 return nullptr;
112}
113
119
121{
123
124 setCustomPoints(t, &t->mouse, sld->mval_end, sld->mval_start);
125
126 /* #setCustomPoints isn't normally changing as the mouse moves,
127 * in this case apply mouse input immediately so we don't refresh
128 * with the value from the previous points. */
129 applyMouseInput(t, &t->mouse, t->mval, t->values);
130}
131
132/* Interpolates along a line made up of 2 segments (used for edge slide). */
134 float p[3], const float v1[3], const float v2[3], const float v3[3], float t)
135{
136 float t_mid, t_delta;
137
138 /* Could be pre-calculated. */
139 t_mid = line_point_factor_v3(v2, v1, v3);
140
141 t_delta = t - t_mid;
142 if (t_delta < 0.0f) {
143 if (UNLIKELY(fabsf(t_mid) < FLT_EPSILON)) {
144 copy_v3_v3(p, v2);
145 }
146 else {
147 interp_v3_v3v3(p, v1, v2, t / t_mid);
148 }
149 }
150 else {
151 t = t - t_mid;
152 t_mid = 1.0f - t_mid;
153
154 if (UNLIKELY(fabsf(t_mid) < FLT_EPSILON)) {
155 copy_v3_v3(p, v3);
156 }
157 else {
158 interp_v3_v3v3(p, v2, v3, t / t_mid);
159 }
160 }
161}
162
163static void edge_slide_data_init_mval(MouseInput *mi, EdgeSlideData *sld, float *mval_dir)
164{
165 /* Possible all of the edge loops are pointing directly at the view. */
166 if (UNLIKELY(len_squared_v2(mval_dir) < 0.1f)) {
167 mval_dir[0] = 0.0f;
168 mval_dir[1] = 100.0f;
169 }
170
171 float mval_start[2], mval_end[2];
172
173 /* Zero out Start. */
174 zero_v2(mval_start);
175
176 /* `mval_dir` holds a vector along edge loop. */
177 copy_v2_v2(mval_end, mval_dir);
178 mul_v2_fl(mval_end, 0.5f);
179
180 sld->mval_start[0] = mi->imval[0] + mval_start[0];
181 sld->mval_start[1] = mi->imval[1] + mval_start[1];
182
183 sld->mval_end[0] = mi->imval[0] + mval_end[0];
184 sld->mval_end[1] = mi->imval[1] + mval_end[1];
185}
186
189 const View3D *v3d,
190 const BMBVHTree *bmbvh,
192{
193 /* NOTE: */
194 BMIter iter_other;
195 BMEdge *e;
196
197 BMVert *v = static_cast<BMVert *>(sv->td->extra);
198 BM_ITER_ELEM (e, &iter_other, v, BM_EDGES_OF_VERT) {
200 continue;
201 }
202
203 if (BMBVH_EdgeVisible(bmbvh, e, t->depsgraph, t->region, v3d, tc->obedit)) {
204 return true;
205 }
206 }
207 return false;
208}
209
215 EdgeSlideData *sld,
216 const int loop_nr,
217 const float2 &mval,
218 const bool use_calc_direction)
219{
220 View3D *v3d = nullptr;
221
222 /* Use for visibility checks. */
223 bool use_occlude_geometry = false;
224 if (t->spacetype == SPACE_VIEW3D) {
225 v3d = static_cast<View3D *>(t->area ? t->area->spacedata.first : nullptr);
226 if (v3d) {
227 if (tc->obedit->type == OB_MESH) {
228 use_occlude_geometry = (tc->obedit->dt > OB_WIRE && !XRAY_ENABLED(v3d));
229 }
230 }
231 }
232
233 /* NOTE(@ideasman42): At the moment this is only needed for meshes.
234 * In principle we could use a generic ray-cast test.
235 *
236 * Prefer #BMBVHTree over generic snap: #SnapObjectContext
237 * or any method that considers all other objects in the scene.
238 *
239 * While generic snapping is technically "correct" there are multiple reasons not to use this.
240 *
241 * - Performance, where generic snapping would consider all other objects for every-vertex.
242 * This can cause lockups when #DupliObject have to be created multiple times for each vertex.
243 * - In practice it's acceptable (even preferable) to skip back-facing vertices
244 * based on each meshes own faces that doesn't take other scene objects into account,
245 * especially since this includes instances objects from particles or nodes.
246 * - The #BMBVH_EdgeVisible check skips faces that the edge is connected to,
247 * unlike generic ray-casts where an edge can (under some conditions) overlap it self.
248 *
249 * See: #125646 for details.
250 */
251 BMBVHTree *bmbvh = nullptr;
252 Array<float3> bmbvh_coord_storage;
253 if (use_occlude_geometry) {
254 Scene *scene_eval = (Scene *)DEG_get_evaluated_id(t->depsgraph, &t->scene->id);
255 Object *obedit_eval = DEG_get_evaluated_object(t->depsgraph, tc->obedit);
257
259 t->depsgraph, em, scene_eval, obedit_eval, bmbvh_coord_storage);
260
263 vert_positions.is_empty() ? nullptr :
264 vert_positions.data(),
265 false);
266 }
267
268 /* Find mouse vectors, the global one, and one per loop in case we have
269 * multiple loops selected, in case they are oriented different. */
270 float2 mval_dir = float2(0);
271 float dist_best_sq = FLT_MAX;
272
273 /* Only for use_calc_direction. */
274 float2 *loop_dir = nullptr;
275 float *loop_maxdist = nullptr;
276
277 if (use_calc_direction) {
278 loop_dir = static_cast<float2 *>(MEM_callocN(sizeof(float2) * loop_nr, "sv loop_dir"));
279 loop_maxdist = static_cast<float *>(MEM_mallocN(sizeof(float) * loop_nr, "sv loop_maxdist"));
280 copy_vn_fl(loop_maxdist, loop_nr, FLT_MAX);
281 }
282
283 for (int i : sld->sv.index_range()) {
284 TransDataEdgeSlideVert *sv = &sld->sv[i];
285 bool is_visible = !use_occlude_geometry || is_vert_slide_visible_bmesh(t, tc, v3d, bmbvh, sv);
286
287 /* This test is only relevant if object is not wire-drawn! See #32068. */
288 if (!is_visible && !use_calc_direction) {
289 continue;
290 }
291
292 /* Search cross edges for visible edge to the mouse cursor,
293 * then use the shared vertex to calculate screen vector. */
294 /* Screen-space coords. */
295 float2 sco_a, sco_b;
296 sld->project(sv, sco_a, sco_b);
297
298 /* Global direction. */
299 float dist_sq = dist_squared_to_line_segment_v2(mval, sco_b, sco_a);
300 if (is_visible) {
301 if (dist_sq < dist_best_sq && (len_squared_v2v2(sco_b, sco_a) > 0.1f)) {
302 dist_best_sq = dist_sq;
303 mval_dir = sco_b - sco_a;
304 sld->curr_sv_index = i;
305 }
306 }
307
308 if (use_calc_direction) {
309 /* Per loop direction. */
310 int l_nr = sv->loop_nr;
311 if (dist_sq < loop_maxdist[l_nr]) {
312 loop_maxdist[l_nr] = dist_sq;
313 loop_dir[l_nr] = sco_b - sco_a;
314 }
315 }
316 }
317
318 if (use_calc_direction) {
319 for (TransDataEdgeSlideVert &sv : sld->sv) {
320 /* Switch a/b if loop direction is different from global direction. */
321 int l_nr = sv.loop_nr;
322 if (math::dot(loop_dir[l_nr], mval_dir) < 0.0f) {
323 swap_v3_v3(sv.dir_side[0], sv.dir_side[1]);
324 }
325 }
326
327 MEM_freeN(loop_dir);
328 MEM_freeN(loop_maxdist);
329 }
330
331 edge_slide_data_init_mval(&t->mouse, sld, mval_dir);
332
333 if (bmbvh) {
334 BKE_bmbvh_free(bmbvh);
335 }
336}
337
340 const bool use_double_side)
341{
342 int group_len;
343 EdgeSlideData *sld = MEM_new<EdgeSlideData>("sld");
345 sld->sv = transform_mesh_uv_edge_slide_data_create(t, tc, &group_len);
346 }
347 else {
348 sld->sv = transform_mesh_edge_slide_data_create(tc, &group_len);
349 }
350
351 if (sld->sv.is_empty()) {
352 MEM_delete(sld);
353 return nullptr;
354 }
355
356 if (!use_double_side) {
357 /* Single Side Case.
358 * Used by #MESH_OT_offset_edge_loops_slide.
359 * It only slides to the side with the longest length. */
360 struct TMP {
361 float2 accum;
362 int count;
363 } zero{};
364
365 Array<TMP> array_len(group_len, zero);
366 for (TransDataEdgeSlideVert &sv : sld->sv) {
367 array_len[sv.loop_nr].accum += float2(math::length(sv.dir_side[0]),
368 math::length(sv.dir_side[1]));
369 array_len[sv.loop_nr].count++;
370 }
371
372 for (TMP &accum : array_len) {
373 accum.accum /= accum.count;
374 }
375
376 for (TransDataEdgeSlideVert &sv : sld->sv) {
377 if (array_len[sv.loop_nr].accum[1] > array_len[sv.loop_nr].accum[0]) {
378 sv.dir_side[0] = sv.dir_side[1];
379 }
380 sv.dir_side[1] = float3(0);
381 sv.edge_len = math::length(sv.dir_side[0]);
382 }
383 }
384
385 sld->curr_sv_index = 0;
386 sld->update_proj_mat(t, tc);
387
388 calcEdgeSlide_mval_range(t, tc, sld, group_len, t->mval, use_double_side);
389
390 return sld;
391}
392
393static void freeEdgeSlideVerts(TransInfo * /*t*/,
394 TransDataContainer * /*tc*/,
395 TransCustomData *custom_data)
396{
397 EdgeSlideData *sld = static_cast<EdgeSlideData *>(custom_data->data);
398
399 if (sld == nullptr) {
400 return;
401 }
402
403 MEM_delete(sld);
404
405 custom_data->data = nullptr;
406}
407
409{
410 EdgeSlideParams *slp = static_cast<EdgeSlideParams *>(t->custom.mode.data);
411
412 if (slp) {
413 switch (event->type) {
414 case EVT_EKEY:
415 if (event->val == KM_PRESS) {
416 slp->use_even = !slp->use_even;
418 return TREDRAW_HARD;
419 }
420 break;
421 case EVT_FKEY:
422 if (event->val == KM_PRESS) {
423 slp->flipped = !slp->flipped;
425 return TREDRAW_HARD;
426 }
427 break;
428 case EVT_CKEY:
429 /* Use like a modifier key. */
430 if (event->val == KM_PRESS) {
431 t->flag ^= T_ALT_TRANSFORM;
433 return TREDRAW_HARD;
434 }
435 break;
436 case MOUSEMOVE:
438 break;
439 default:
440 break;
441 }
442 }
443 return TREDRAW_NOTHING;
444}
445
447{
449 if (sld == nullptr) {
450 return;
451 }
452
453 const EdgeSlideParams *slp = static_cast<const EdgeSlideParams *>(t->custom.mode.data);
454 const bool is_clamp = !(t->flag & T_ALT_TRANSFORM);
455
456 const float line_size = UI_GetThemeValuef(TH_OUTLINE_WIDTH) + 0.5f;
457
459
461
462 if (t->spacetype == SPACE_VIEW3D) {
464 GPU_matrix_mul(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->object_to_world().ptr());
465 }
466
468
470
471 TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index];
472 const float3 &curr_sv_co_orig = curr_sv->v_co_orig();
473
474 if (slp->use_even == true) {
475 /* Even mode. */
476 float co_a[3], co_b[3], co_mark[3];
477 const float fac = (slp->perc + 1.0f) / 2.0f;
478 const float ctrl_size = UI_GetThemeValuef(TH_FACEDOT_SIZE) + 1.5f;
479 const float guide_size = ctrl_size - 0.5f;
480 const int alpha_shade = -30;
481
482 add_v3_v3v3(co_a, curr_sv_co_orig, curr_sv->dir_side[0]);
483 add_v3_v3v3(co_b, curr_sv_co_orig, curr_sv->dir_side[1]);
484
485 GPU_line_width(line_size);
488 if (!math::is_zero(curr_sv->dir_side[0])) {
489 immVertex3fv(pos, co_a);
490 immVertex3fv(pos, curr_sv_co_orig);
491 }
492 if (!math::is_zero(curr_sv->dir_side[1])) {
493 immVertex3fv(pos, co_b);
494 immVertex3fv(pos, curr_sv_co_orig);
495 }
496 immEnd();
497
498 {
499 float *co_test = nullptr;
500 if (slp->flipped) {
501 if (!math::is_zero(curr_sv->dir_side[1])) {
502 co_test = co_b;
503 }
504 }
505 else {
506 if (!math::is_zero(curr_sv->dir_side[0])) {
507 co_test = co_a;
508 }
509 }
510
511 if (co_test != nullptr) {
512 immUniformThemeColorShadeAlpha(TH_SELECT, -30, alpha_shade);
513 GPU_point_size(ctrl_size);
515 immVertex3fv(pos, co_test);
516 immEnd();
517 }
518 }
519
520 immUniformThemeColorShadeAlpha(TH_SELECT, 255, alpha_shade);
521 GPU_point_size(guide_size);
523 interp_line_v3_v3v3v3(co_mark, co_b, curr_sv_co_orig, co_a, fac);
524 immVertex3fv(pos, co_mark);
525 immEnd();
526 }
527 else if (is_clamp == false) {
528 const int side_index = slp->curr_side_unclamp;
529 const int alpha_shade = -160;
530
531 GPU_line_width(line_size);
533 immBegin(GPU_PRIM_LINES, sld->sv.size() * 2);
534
535 /* TODO(@ideasman42): Loop over all verts. */
536 for (TransDataEdgeSlideVert &sv : sld->sv) {
537 float a[3], b[3];
538
539 if (!is_zero_v3(sv.dir_side[side_index])) {
540 copy_v3_v3(a, sv.dir_side[side_index]);
541 }
542 else {
543 copy_v3_v3(a, sv.dir_side[!side_index]);
544 }
545
546 mul_v3_fl(a, 100.0f);
547 negate_v3_v3(b, a);
548
549 const float3 &sv_co_orig = sv.v_co_orig();
550 add_v3_v3(a, sv_co_orig);
551 add_v3_v3(b, sv_co_orig);
552
553 immVertex3fv(pos, a);
555 }
556 immEnd();
557 }
558 else {
559 /* Common case. */
560 const int alpha_shade = -160;
561
562 float co_dir[3];
563 add_v3_v3v3(co_dir, curr_sv_co_orig, curr_sv->dir_side[slp->curr_side_unclamp]);
564
565 GPU_line_width(line_size);
568 immVertex3fv(pos, curr_sv_co_orig);
569 immVertex3fv(pos, co_dir);
570 immEnd();
571 }
572
574
575 if (t->spacetype == SPACE_VIEW3D) {
578 }
579
581}
582
583static void edge_slide_snap_apply(TransInfo *t, float *value)
584{
586 EdgeSlideParams *slp = static_cast<EdgeSlideParams *>(t->custom.mode.data);
587 EdgeSlideData *sld_active = static_cast<EdgeSlideData *>(tc->custom.mode.data);
588 TransDataEdgeSlideVert *sv = &sld_active->sv[sld_active->curr_sv_index];
589 float3 co_orig, co_dest[2], dvec, snap_point;
590 co_orig = sv->v_co_orig();
591 co_dest[0] = co_orig + sv->dir_side[0];
592 co_dest[1] = co_orig + sv->dir_side[1];
593
594 if (tc->use_local_mat) {
595 mul_m4_v3(tc->mat, co_orig);
596 mul_m4_v3(tc->mat, co_dest[0]);
597 mul_m4_v3(tc->mat, co_dest[1]);
598 }
599
600 getSnapPoint(t, dvec);
601 sub_v3_v3(dvec, t->tsnap.snap_source);
602 add_v3_v3v3(snap_point, co_orig, dvec);
603
604 float perc = *value;
605 int side_index;
606 float t_mid;
607 if (slp->use_even == false) {
608 const bool is_clamp = !(t->flag & T_ALT_TRANSFORM);
609 if (is_clamp) {
610 side_index = perc < 0.0f;
611 }
612 else {
613 /* Use the side indicated in `EdgeSlideParams::curr_side_unclamp` as long as that side is not
614 * zero length. */
615 side_index = int(slp->curr_side_unclamp ==
617 }
618 }
619 else {
620 /* Could be pre-calculated. */
621 t_mid = line_point_factor_v3(
622 blender::float3{0.0f, 0.0f, 0.0f}, sv->dir_side[0], sv->dir_side[1]);
623
624 float t_snap = line_point_factor_v3(snap_point, co_dest[0], co_dest[1]);
625 side_index = t_snap >= t_mid;
626 }
627
629 float co_dir[3];
630 sub_v3_v3v3(co_dir, co_dest[side_index], co_orig);
631 normalize_v3(co_dir);
634 }
635 else {
637 }
638 add_v3_v3v3(snap_point, co_orig, dvec);
639 }
640
641 perc = line_point_factor_v3(snap_point, co_orig, co_dest[side_index]);
642 if (slp->use_even == false) {
643 if (side_index) {
644 perc *= -1;
645 }
646 }
647 else {
648 if (!side_index) {
649 perc = (1.0f - perc) * t_mid;
650 }
651 else {
652 perc = perc * (1.0f - t_mid) + t_mid;
653 }
654
655 if (slp->flipped) {
656 perc = 1.0f - perc;
657 }
658
659 perc = (2 * perc) - 1.0f;
660
661 if (!slp->flipped) {
662 perc *= -1;
663 }
664 }
665
666 *value = perc;
667}
668
670 const float fac,
671 const float curr_length_fac,
672 const int curr_side_unclamp,
673 const bool use_clamp,
674 const bool use_even,
675 const bool use_flip,
676 float r_co[3])
677{
678 copy_v3_v3(r_co, sv.v_co_orig());
679
680 if (use_even == false) {
681 if (use_clamp) {
682 const int side_index = (fac < 0.0f);
683 const float fac_final = fabsf(fac);
684 madd_v3_v3fl(r_co, sv.dir_side[side_index], fac_final);
685 }
686 else {
687 int side_index = curr_side_unclamp;
688 if (is_zero_v3(sv.dir_side[side_index])) {
689 side_index = int(!side_index);
690 }
691 const float fac_final = (side_index == (fac < 0.0f) ? fabsf(fac) : -fabsf(fac));
692 madd_v3_v3fl(r_co, sv.dir_side[side_index], fac_final);
693 }
694 }
695 else {
706 if (sv.edge_len > FLT_EPSILON) {
707 float co_a[3], co_b[3];
708 const float fac_final = min_ff(sv.edge_len, curr_length_fac) / sv.edge_len;
709
710 add_v3_v3v3(co_a, r_co, sv.dir_side[0]);
711 add_v3_v3v3(co_b, r_co, sv.dir_side[1]);
712
713 if (use_flip) {
714 interp_line_v3_v3v3v3(r_co, co_b, r_co, co_a, fac_final);
715 }
716 else {
717 interp_line_v3_v3v3v3(r_co, co_a, r_co, co_b, fac_final);
718 }
719 }
720 }
721}
722
723static void doEdgeSlide(TransInfo *t, float perc)
724{
725 EdgeSlideParams *slp = static_cast<EdgeSlideParams *>(t->custom.mode.data);
726 EdgeSlideData *sld_active = edgeSlideFirstGet(t);
727
728 slp->perc = perc;
729
730 const bool use_clamp = !(t->flag & T_ALT_TRANSFORM);
731 const bool use_even = slp->use_even;
732 const bool use_flip = slp->flipped;
733
734 const int curr_side_unclamp = slp->curr_side_unclamp;
735 float curr_length_fac = 0.0f;
736 if (use_even) {
737 TransDataEdgeSlideVert *sv_active = &sld_active->sv[sld_active->curr_sv_index];
738 curr_length_fac = sv_active->edge_len * (((use_flip ? perc : -perc) + 1.0f) / 2.0f);
739 }
740 else if (use_clamp) {
741 slp->curr_side_unclamp = (perc < 0.0f);
742 }
743
745 EdgeSlideData *sld = static_cast<EdgeSlideData *>(tc->custom.mode.data);
746
747 if (sld == nullptr) {
748 continue;
749 }
750
751 for (TransDataEdgeSlideVert &sv : sld->sv) {
753 sv, perc, curr_length_fac, curr_side_unclamp, use_clamp, use_even, use_flip, sv.td->loc);
754 }
755 }
756}
757
759{
760 char str[UI_MAX_DRAW_STR];
761 size_t ofs = 0;
762 float final;
763 EdgeSlideParams *slp = static_cast<EdgeSlideParams *>(t->custom.mode.data);
764 bool flipped = slp->flipped;
765 bool use_even = slp->use_even;
766 const bool is_clamp = !(t->flag & T_ALT_TRANSFORM);
767 const bool is_constrained = !(is_clamp == false || hasNumInput(&t->num));
768 const bool is_precision = t->modifiers & MOD_PRECISION;
769 const bool is_snap = t->modifiers & MOD_SNAP;
770 const bool is_snap_invert = t->modifiers & MOD_SNAP_INVERT;
771
772 final = t->values[0] + t->values_modal_offset[0];
773
775 if (!validSnap(t)) {
776 transform_snap_increment(t, &final);
777 }
778
779 /* Only do this so out of range values are not displayed. */
780 if (is_constrained) {
781 CLAMP(final, -1.0f, 1.0f);
782 }
783
784 applyNumInput(&t->num, &final);
785
786 t->values_final[0] = final;
787
788 /* Header string. */
789 ofs += BLI_strncpy_rlen(str + ofs, RPT_("Edge Slide: "), sizeof(str) - ofs);
790 if (hasNumInput(&t->num)) {
791 char c[NUM_STR_REP_LEN];
792 outputNumInput(&(t->num), c, &t->scene->unit);
793 ofs += BLI_strncpy_rlen(str + ofs, &c[0], sizeof(str) - ofs);
794 }
795 else {
796 ofs += BLI_snprintf_rlen(str + ofs, sizeof(str) - ofs, "%.4f ", final);
797 }
798 /* Done with header string. */
799
800 /* Do stuff here. */
801 doEdgeSlide(t, final);
802
803 recalc_data(t);
804
806
807 wmOperator *op = slp->op;
808 if (!op) {
809 return;
810 }
811
812 WorkspaceStatus status(t->context);
813 status.opmodal(IFACE_("Confirm"), op->type, TFM_MODAL_CONFIRM);
814 status.opmodal(IFACE_("Cancel"), op->type, TFM_MODAL_CONFIRM);
815 status.opmodal(IFACE_("Snap"), op->type, TFM_MODAL_SNAP_TOGGLE, is_snap);
816 status.opmodal(IFACE_("Snap Invert"), op->type, TFM_MODAL_SNAP_INV_ON, is_snap_invert);
817 status.opmodal(IFACE_("Set Snap Base"), op->type, TFM_MODAL_EDIT_SNAP_SOURCE_ON);
818 status.opmodal(IFACE_("Move"), op->type, TFM_MODAL_TRANSLATE);
819 status.opmodal(IFACE_("Rotate"), op->type, TFM_MODAL_ROTATE);
820 status.opmodal(IFACE_("Resize"), op->type, TFM_MODAL_RESIZE);
821 status.opmodal(IFACE_("Precision Mode"), op->type, TFM_MODAL_PRECISION, is_precision);
822 status.item_bool(IFACE_("Clamp"), is_clamp, ICON_EVENT_C, ICON_EVENT_ALT);
823 status.item_bool(IFACE_("Even"), use_even, ICON_EVENT_E);
824 if (use_even) {
825 status.item_bool(IFACE_("Flipped"), flipped, ICON_EVENT_F);
826 }
827}
828
829static void edge_slide_transform_matrix_fn(TransInfo *t, float mat_xform[4][4])
830{
831 float delta[3], orig_co[3], final_co[3];
832
833 EdgeSlideParams *slp = static_cast<EdgeSlideParams *>(t->custom.mode.data);
835 EdgeSlideData *sld_active = static_cast<EdgeSlideData *>(tc->custom.mode.data);
836 TransDataEdgeSlideVert &sv_active = sld_active->sv[sld_active->curr_sv_index];
837
838 copy_v3_v3(orig_co, sv_active.v_co_orig());
839
840 const float fac = t->values_final[0];
841 float curr_length_fac = 0.0f;
842 if (slp->use_even) {
843 curr_length_fac = sv_active.edge_len * (((slp->flipped ? fac : -fac) + 1.0f) / 2.0f);
844 }
845
846 edge_slide_apply_elem(sv_active,
847 fac,
848 curr_length_fac,
850 !(t->flag & T_ALT_TRANSFORM),
851 slp->use_even,
852 slp->flipped,
853 final_co);
854
855 if (tc->use_local_mat) {
856 mul_m4_v3(tc->mat, orig_co);
857 mul_m4_v3(tc->mat, final_co);
858 }
859
860 sub_v3_v3v3(delta, final_co, orig_co);
861 add_v3_v3(mat_xform[3], delta);
862}
863
865 wmOperator *op,
866 bool use_double_side,
867 bool use_even,
868 bool flipped,
869 bool use_clamp)
870{
871 EdgeSlideData *sld;
872 bool ok = false;
873
874 t->mode = TFM_EDGE_SLIDE;
875
876 {
877 EdgeSlideParams *slp = static_cast<EdgeSlideParams *>(MEM_callocN(sizeof(*slp), __func__));
878 slp->op = op;
879 slp->use_even = use_even;
880 slp->flipped = flipped;
881 /* Happens to be best for single-sided. */
882 if (use_double_side == false) {
883 slp->flipped = !flipped;
884 }
885 slp->perc = 0.0f;
886
887 if (!use_clamp) {
888 t->flag |= T_ALT_TRANSFORM;
889 }
890
891 t->custom.mode.data = slp;
892 t->custom.mode.use_free = true;
893 }
894
896 sld = createEdgeSlideVerts(t, tc, use_double_side);
897 if (sld) {
898 tc->custom.mode.data = sld;
899 tc->custom.mode.free_cb = freeEdgeSlideVerts;
900 ok = true;
901 }
902 }
903
904 if (!ok) {
905 t->state = TRANS_CANCEL;
906 return;
907 }
908
909 /* Set custom point first if you want value to be initialized by init. */
912
913 t->idx_max = 0;
914 t->num.idx_max = 0;
915 t->snap[0] = 0.1f;
916 t->snap[1] = t->snap[0] * 0.1f;
917
918 copy_v3_fl(t->num.val_inc, t->snap[0]);
919 t->num.unit_sys = t->scene->unit.system;
920 t->num.unit_type[0] = B_UNIT_NONE;
921}
922
924{
925 bool use_double_side = true;
926 bool use_even = false;
927 bool flipped = false;
928 bool use_clamp = true;
929 if (op) {
930 PropertyRNA *prop;
931 /* The following properties could be unset when transitioning from this
932 * operator to another and back. For example pressing "G" to move, and
933 * then "G" again to go back to edge slide. */
934 prop = RNA_struct_find_property(op->ptr, "single_side");
935 use_double_side = (prop) ? !RNA_property_boolean_get(op->ptr, prop) : true;
936 prop = RNA_struct_find_property(op->ptr, "use_even");
937 use_even = (prop) ? RNA_property_boolean_get(op->ptr, prop) : false;
938 prop = RNA_struct_find_property(op->ptr, "flipped");
939 flipped = (prop) ? RNA_property_boolean_get(op->ptr, prop) : false;
940 prop = RNA_struct_find_property(op->ptr, "use_clamp");
941 use_clamp = (prop) ? RNA_property_boolean_get(op->ptr, prop) : true;
942 }
943 initEdgeSlide_ex(t, op, use_double_side, use_even, flipped, use_clamp);
944}
945
948/* -------------------------------------------------------------------- */
953{
955 EdgeSlideData *sld = static_cast<EdgeSlideData *>(tc->custom.mode.data);
956 if (sld) {
957 sld->update_proj_mat(t, tc);
958 TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index];
959
960 float2 sco_a, sco_b;
961 sld->project(curr_sv, sco_a, sco_b);
962 float2 mval_dir = sco_b - sco_a;
963 edge_slide_data_init_mval(&t->mouse, sld, mval_dir);
964 }
965 }
966
968 setCustomPoints(t, &t->mouse, sld->mval_end, sld->mval_start);
969}
970
974 /*flags*/ T_NO_CONSTRAINT,
975 /*init_fn*/ initEdgeSlide,
976 /*transform_fn*/ applyEdgeSlide,
977 /*transform_matrix_fn*/ edge_slide_transform_matrix_fn,
978 /*handle_event_fn*/ handleEventEdgeSlide,
979 /*snap_distance_fn*/ transform_snap_distance_len_squared_fn,
980 /*snap_apply_fn*/ edge_slide_snap_apply,
981 /*draw_fn*/ drawEdgeSlide,
982};
blender::Span< blender::float3 > BKE_editmesh_vert_coords_when_deformed(Depsgraph *depsgraph, BMEditMesh *em, Scene *scene, Object *obedit, blender::Array< blender::float3 > &r_alloc)
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:63
BMBVHTree * BKE_bmbvh_new_from_editmesh(struct BMEditMesh *em, int flag, const blender::float3 *cos_cage, bool cos_cage_free)
@ BMBVH_RESPECT_HIDDEN
void BKE_bmbvh_free(BMBVHTree *tree)
@ B_UNIT_NONE
Definition BKE_unit.hh:106
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
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])
float dist_squared_to_line_segment_v2(const float p[2], const float l1[2], const float l2[2])
Definition math_geom.cc:289
void mul_m4_v3(const float M[4][4], float r[3])
MINLINE float len_squared_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void madd_v3_v3fl(float r[3], const float a[3], float f)
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
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])
void copy_vn_fl(float *array_tar, int size, float val)
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
Definition math_vector.c:36
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE bool is_zero_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
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 add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
size_t BLI_snprintf_rlen(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
char char size_t BLI_strncpy_rlen(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
unsigned int uint
#define CLAMP(a, b, c)
#define UNLIKELY(x)
#define RPT_(msgid)
#define IFACE_(msgid)
ID * DEG_get_evaluated_id(const Depsgraph *depsgraph, ID *id)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ OB_WIRE
@ OB_MESH
@ SCE_SNAP_TO_EDGE
@ SCE_SNAP_TO_FACE
@ SPACE_VIEW3D
bool BMBVH_EdgeVisible(const BMBVHTree *tree, const BMEdge *e, const Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const Object *obedit)
#define NUM_STR_REP_LEN
void outputNumInput(NumInput *n, char *str, const UnitSettings *unit_settings)
Definition numinput.cc:88
bool applyNumInput(NumInput *n, float *vec)
Definition numinput.cc:190
bool hasNumInput(const NumInput *n)
Definition numinput.cc:171
void ED_area_status_text(ScrArea *area, const char *str)
Definition area.cc:803
@ TFM_EDGE_SLIDE
#define XRAY_ENABLED(v3d)
blender::float4x4 ED_view3d_ob_project_mat_get(const RegionView3D *rv3d, const Object *ob)
void immEnd()
void immUnbindProgram()
void immUniformThemeColorShadeAlpha(int color_id, int color_offset, int alpha_offset)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
void immBeginAtMost(GPUPrimType, uint max_vertex_len)
GPUVertFormat * immVertexFormat()
void immVertex3fv(uint attr_id, const float data[3])
void immBegin(GPUPrimType, uint vertex_len)
void GPU_matrix_push()
#define GPU_matrix_mul(x)
void GPU_matrix_pop()
@ GPU_PRIM_LINES
@ GPU_PRIM_POINTS
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(eGPUBlend blend)
Definition gpu_state.cc:42
void GPU_line_width(float width)
Definition gpu_state.cc:161
void GPU_point_size(float size)
Definition gpu_state.cc:167
@ GPU_DEPTH_LESS_EQUAL
Definition GPU_state.hh:111
@ GPU_DEPTH_NONE
Definition GPU_state.hh:108
void GPU_depth_test(eGPUDepthTest test)
Definition gpu_state.cc:68
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
#define UI_MAX_DRAW_STR
@ TH_FACEDOT_SIZE
@ TH_EDGE_SELECT
@ TH_OUTLINE_WIDTH
@ TH_SELECT
float UI_GetThemeValuef(int colorid)
void UI_view2d_view_to_region_m4(const View2D *v2d, float matrix[4][4]) ATTR_NONNULL()
Definition view2d.cc:1803
@ KM_PRESS
Definition WM_types.hh:284
@ BM_ELEM_HIDDEN
@ BM_ELEM_SELECT
#define BM_elem_flag_test(ele, hflag)
#define BM_ITER_ELEM(ele, iter, data, itype)
@ BM_EDGES_OF_VERT
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
void item_bool(std::string text, bool inverted, int icon1, int icon2=0)
Definition area.cc:924
void opmodal(std::string text, const wmOperatorType *ot, int propvalue, bool inverted=false)
Definition area.cc:934
int64_t size() const
Definition BLI_array.hh:245
IndexRange index_range() const
Definition BLI_array.hh:349
bool is_empty() const
Definition BLI_array.hh:253
constexpr const T * data() const
Definition BLI_span.hh:216
constexpr bool is_empty() const
Definition BLI_span.hh:261
local_group_size(16, 16) .push_constant(Type b
#define fabsf(x)
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
#define str(s)
int count
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
T length(const VecBase< T, Size > &a)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
bool is_zero(const T &a)
VectorT project_point(const MatT &mat, const VectorT &point)
VecBase< float, 2 > float2
VecBase< float, 3 > float3
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
bool RNA_property_boolean_get(PointerRNA *ptr, PropertyRNA *prop)
#define FLT_MAX
Definition stdcycles.h:14
void project(const TransDataEdgeSlideVert *svert, float2 &r_sco_a, float2 &r_sco_b) const
Array< TransDataEdgeSlideVert > sv
void update_proj_mat(TransInfo *t, const TransDataContainer *tc)
void * first
blender::float2 imval
Definition transform.hh:383
short idx_max
float val_inc[NUM_MAX_ELEMENTS]
int unit_type[NUM_MAX_ELEMENTS]
struct UnitSettings unit
ListBase spacedata
TransCustomData mode
Definition transform.hh:423
unsigned int use_free
Definition transform.hh:410
TransCustomDataContainer custom
Definition transform.hh:501
float mat[4][4]
Definition transform.hh:462
const float * v_co_orig() const
blender::float3 dir_side[2]
eTfmMode mode
Definition transform.hh:517
void * view
Definition transform.hh:647
char spacetype
Definition transform.hh:582
float snap[2]
Definition transform.hh:561
float values[4]
Definition transform.hh:624
TransSnap tsnap
Definition transform.hh:537
short idx_max
Definition transform.hh:559
float values_modal_offset[4]
Definition transform.hh:627
eTState state
Definition transform.hh:527
eTModifier modifiers
Definition transform.hh:525
NumInput num
Definition transform.hh:540
TransCustomDataContainer custom
Definition transform.hh:676
Scene * scene
Definition transform.hh:654
eTFlag flag
Definition transform.hh:523
ARegion * region
Definition transform.hh:652
MouseInput mouse
Definition transform.hh:543
Depsgraph * depsgraph
Definition transform.hh:653
float values_final[4]
Definition transform.hh:632
bContext * context
Definition transform.hh:649
blender::float2 mval
Definition transform.hh:663
TransConvertTypeInfo * data_type
Definition transform.hh:513
ScrArea * area
Definition transform.hh:651
float snap_source[3]
Definition transform.hh:325
eSnapMode target_type
Definition transform.hh:321
const c_style_mat & ptr() const
VecBase< T, 2 > xy() const
short val
Definition WM_types.hh:724
short type
Definition WM_types.hh:722
struct wmOperatorType * type
struct PointerRNA * ptr
@ INPUT_CUSTOM_RATIO_FLIP
Definition transform.hh:754
void applyMouseInput(TransInfo *t, MouseInput *mi, const blender::float2 &mval, float output[3])
@ MOD_SNAP_INVERT
Definition transform.hh:164
@ MOD_PRECISION
Definition transform.hh:162
@ MOD_SNAP
Definition transform.hh:163
#define TRANS_DATA_CONTAINER_FIRST_OK(t)
Definition transform.hh:849
eRedrawFlag
Definition transform.hh:214
@ TREDRAW_NOTHING
Definition transform.hh:215
@ TREDRAW_HARD
Definition transform.hh:217
@ TFM_MODAL_RESIZE
Definition transform.hh:251
@ TFM_MODAL_CONFIRM
Definition transform.hh:248
@ TFM_MODAL_SNAP_TOGGLE
Definition transform.hh:254
@ TFM_MODAL_ROTATE
Definition transform.hh:250
@ TFM_MODAL_SNAP_INV_ON
Definition transform.hh:252
@ TFM_MODAL_PRECISION
Definition transform.hh:285
@ TFM_MODAL_TRANSLATE
Definition transform.hh:249
@ TFM_MODAL_EDIT_SNAP_SOURCE_ON
Definition transform.hh:291
@ T_ALT_TRANSFORM
Definition transform.hh:126
@ T_NO_CONSTRAINT
Definition transform.hh:95
@ TRANS_CANCEL
Definition transform.hh:210
void setCustomPoints(TransInfo *t, MouseInput *mi, const int mval_start[2], const int mval_end[2])
void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode)
#define FOREACH_TRANS_DATA_CONTAINER(t, th)
Definition transform.hh:854
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 recalc_data(TransInfo *t)
conversion and adaptation of different datablocks to a common struct.
blender::Array< TransDataEdgeSlideVert > transform_mesh_edge_slide_data_create(const TransDataContainer *tc, int *r_group_len)
TransConvertTypeInfo TransConvertType_MeshUV
blender::Array< TransDataEdgeSlideVert > transform_mesh_uv_edge_slide_data_create(const TransInfo *t, TransDataContainer *tc, int *r_group_len)
transform modes used by different operators.
static void drawEdgeSlide(TransInfo *t)
static void applyEdgeSlide(TransInfo *t)
static void calcEdgeSlide_mval_range(TransInfo *t, TransDataContainer *tc, EdgeSlideData *sld, const int loop_nr, const float2 &mval, const bool use_calc_direction)
static TransDataContainer * edge_slide_container_first_ok(TransInfo *t)
static void edge_slide_snap_apply(TransInfo *t, float *value)
static void freeEdgeSlideVerts(TransInfo *, TransDataContainer *, TransCustomData *custom_data)
static void interp_line_v3_v3v3v3(float p[3], const float v1[3], const float v2[3], const float v3[3], float t)
void transform_mode_edge_slide_reproject_input(TransInfo *t)
static void edge_slide_data_init_mval(MouseInput *mi, EdgeSlideData *sld, float *mval_dir)
static eRedrawFlag handleEventEdgeSlide(TransInfo *t, const wmEvent *event)
static void edge_slide_transform_matrix_fn(TransInfo *t, float mat_xform[4][4])
static bool is_vert_slide_visible_bmesh(TransInfo *t, TransDataContainer *tc, const View3D *v3d, const BMBVHTree *bmbvh, TransDataEdgeSlideVert *sv)
static void edge_slide_apply_elem(const TransDataEdgeSlideVert &sv, const float fac, const float curr_length_fac, const int curr_side_unclamp, const bool use_clamp, const bool use_even, const bool use_flip, float r_co[3])
static void calcEdgeSlideCustomPoints(TransInfo *t)
static EdgeSlideData * edgeSlideFirstGet(TransInfo *t)
static EdgeSlideData * createEdgeSlideVerts(TransInfo *t, TransDataContainer *tc, const bool use_double_side)
TransModeInfo TransMode_edgeslide
static void initEdgeSlide_ex(TransInfo *t, wmOperator *op, bool use_double_side, bool use_even, bool flipped, bool use_clamp)
static void doEdgeSlide(TransInfo *t, float perc)
static void initEdgeSlide(TransInfo *t, wmOperator *op)
void transform_snap_mixed_apply(TransInfo *t, float *vec)
bool validSnap(const TransInfo *t)
bool transform_snap_increment(const TransInfo *t, float *r_val)
float transform_snap_distance_len_squared_fn(TransInfo *, const float p1[3], const float p2[3])
void getSnapPoint(const TransInfo *t, float vec[3])
@ EVT_EKEY
@ EVT_FKEY
@ EVT_CKEY
@ MOUSEMOVE