Blender V5.0
transform_convert_mesh.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
8
9#include "DNA_mesh_types.h"
10
11#include "MEM_guardedalloc.h"
12
13#include "BLI_alloca.h"
14#include "BLI_linklist_stack.h"
15#include "BLI_math_geom.h"
16#include "BLI_math_matrix.h"
17#include "BLI_math_rotation.h"
18#include "BLI_math_vector.h"
19#include "BLI_memarena.h"
20
21#include "BKE_context.hh"
22#include "BKE_crazyspace.hh"
23#include "BKE_editmesh.hh"
24#include "BKE_mesh.hh"
25#include "BKE_modifier.hh"
26#include "BKE_scene.hh"
27
28#include "ED_mesh.hh"
29#include "ED_object.hh"
30
32
33#include "transform.hh"
35#include "transform_snap.hh"
36
37#include "transform_convert.hh"
38
39namespace blender::ed::transform {
40
41/* -------------------------------------------------------------------- */
44
47 TransCustomData *custom_data);
48
51
60
82
83#define PARTIAL_TYPE_MAX 2
84
93
99
101{
102 TransCustomDataMesh *tcmd = static_cast<TransCustomDataMesh *>(tc->custom.type.data);
103 BLI_assert(tc->custom.type.data == nullptr ||
105 if (tc->custom.type.data == nullptr) {
106 tc->custom.type.data = MEM_callocN(sizeof(TransCustomDataMesh), __func__);
108 tcmd = static_cast<TransCustomDataMesh *>(tc->custom.type.data);
111 }
112 return tcmd;
113}
114
116{
117 if (tcmd->cd_layer_correct != nullptr) {
119 }
120
121 for (int i = 0; i < ARRAY_SIZE(tcmd->partial_update); i++) {
122 if (tcmd->partial_update[i].cache != nullptr) {
124 }
125 }
126
127 MEM_freeN(tcmd);
128}
129
131 TransDataContainer * /*tc*/,
132 TransCustomData *custom_data)
133{
134 TransCustomDataMesh *tcmd = static_cast<TransCustomDataMesh *>(custom_data->data);
136 custom_data->data = nullptr;
137}
138
140
141/* -------------------------------------------------------------------- */
144
149
153
156
157 /* Special handle for multi-resolution. */
159
160 /* Optionally merge custom-data groups (this keeps UVs connected for example). */
161 struct {
172
174};
175
176#define USE_FACE_SUBSTITUTE
177#ifdef USE_FACE_SUBSTITUTE
178# define FACE_SUBSTITUTE_INDEX INT_MIN
179
185{
186 BMFace *best_face = nullptr;
187 BMLoop *l;
188 BMIter liter;
189 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
190 BMLoop *l_radial_next = l->radial_next;
191 BMFace *f_test = l_radial_next->f;
192 if (f_test == f) {
193 continue;
194 }
195 if (is_zero_v3(f_test->no)) {
196 continue;
197 }
198
199 /* Check the loops edge isn't selected. */
200 if (!BM_elem_flag_test(l_radial_next->v, BM_ELEM_SELECT) &&
201 !BM_elem_flag_test(l_radial_next->next->v, BM_ELEM_SELECT))
202 {
203 /* Prefer edges with unselected vertices.
204 * Useful for extrude. */
205 best_face = f_test;
206 break;
207 }
208 if (best_face == nullptr) {
209 best_face = f_test;
210 }
211 }
212 return best_face;
213}
214
216 BMFace *f,
217 BMFace *f_copy)
218{
220 BMesh *bm = tcld->bm;
221
223 bm->pdata, tcld->bm_origfaces->pdata);
225 bm->ldata, tcld->bm_origfaces->ldata);
226
227 /* It is impossible to calculate the loops weights of a face without area.
228 * Find a substitute. */
230 if (f_substitute) {
231 /* Copy the custom-data from the substitute face. */
232 BMLoop *l_iter, *l_first;
233 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
234 do {
235 BM_loop_interp_from_face(bm, l_iter, f_substitute, false, false);
236 } while ((l_iter = l_iter->next) != l_first);
237
238 /* Use the substitute face as the reference during the transformation. */
239 BMFace *f_substitute_copy = BM_face_copy(
240 tcld->bm_origfaces, cd_face_map, cd_loop_map, f_substitute, true, true);
241
242 /* Hack: reference substitute face in `f_copy->no`.
243 * `tcld->origfaces` is already used to restore the initial value. */
245 *((BMFace **)&f_copy->no[0]) = f_substitute_copy;
246 }
247}
248
250{
252 return *((BMFace **)&f_copy->no[0]);
253}
254
255#endif /* USE_FACE_SUBSTITUTE */
256
258 TransDataBasic *td,
259 const int index)
260{
261 BMesh *bm = tcld->bm;
262 BMVert *v = static_cast<BMVert *>(td->extra);
263 BMIter liter;
264 int j, l_num;
265 float *loop_weights;
266
268 bm->pdata, tcld->bm_origfaces->pdata);
270 bm->ldata, tcld->bm_origfaces->ldata);
271
272 // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) {
274 l_num = liter.count;
275 loop_weights = tcld->use_merge_group ?
276 static_cast<float *>(BLI_array_alloca(loop_weights, l_num)) :
277 nullptr;
278 for (j = 0; j < l_num; j++) {
279 BMLoop *l = static_cast<BMLoop *>(BM_iter_step(&liter));
280 BMLoop *l_prev, *l_next;
281
282 /* Generic custom-data correction. Copy face data. */
283 void **val_p;
284 if (!BLI_ghash_ensure_p(tcld->origfaces, l->f, &val_p)) {
285 BMFace *f_copy = BM_face_copy(
286 tcld->bm_origfaces, cd_face_map, cd_loop_map, l->f, true, true);
287 *val_p = f_copy;
288#ifdef USE_FACE_SUBSTITUTE
289 if (is_zero_v3(l->f->no)) {
291 }
292#endif
293 }
294
295 if (tcld->use_merge_group) {
296 if ((l_prev = BM_loop_find_prev_nodouble(l, l->next, FLT_EPSILON)) &&
297 (l_next = BM_loop_find_next_nodouble(l, l_prev, FLT_EPSILON)))
298 {
299 loop_weights[j] = angle_v3v3v3(l_prev->v->co, l->v->co, l_next->v->co);
300 }
301 else {
302 loop_weights[j] = 0.0f;
303 }
304 }
305 }
306
307 if (tcld->use_merge_group) {
308 /* Store cd_loop_groups. */
309 TransCustomDataMergeGroup *merge_data = &tcld->merge_group.data[index];
310 if (l_num != 0) {
311 merge_data->cd_loop_groups = static_cast<LinkNode **>(BLI_memarena_alloc(
312 tcld->arena, tcld->merge_group.customdatalayer_map_len * sizeof(void *)));
313 for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
314 const int layer_nr = tcld->merge_group.customdatalayer_map[j];
316 bm, v, layer_nr, loop_weights, tcld->arena);
317 }
318 }
319 else {
320 merge_data->cd_loop_groups = nullptr;
321 }
322
324 }
325}
326
329{
330 BMesh *bm = tcld->bm;
331
332 GHash *origfaces = BLI_ghash_ptr_new(__func__);
334 params.use_toolflags = false;
336
337 /* We need to have matching loop custom-data. */
338 BM_mesh_copy_init_customdata_all_layers(bm_origfaces, bm, BM_LOOP, nullptr);
339
340 tcld->origfaces = origfaces;
341 tcld->bm_origfaces = bm_origfaces;
342
345}
346
349{
350 BMesh *bm = tcld->bm;
352
353 /* TODO: We don't need `layer_math_map` when there are no loops linked
354 * to one of the sliding vertices. */
355
356 /* Over allocate, only 'math' layers are indexed. */
357 int *customdatalayer_map = MEM_malloc_arrayN<int>(bm->ldata.totlayer, __func__);
358 int layer_math_map_len = 0;
359 for (int i = 0; i < bm->ldata.totlayer; i++) {
360 if (CustomData_layer_has_math(&bm->ldata, i)) {
361 customdatalayer_map[layer_math_map_len++] = i;
362 }
363 }
364 BLI_assert(layer_math_map_len != 0);
365
367 tcld->merge_group.customdatalayer_map = customdatalayer_map;
368 tcld->merge_group.customdatalayer_map_len = layer_math_map_len;
371 tcld->arena, tcld->merge_group.data_len * sizeof(*tcld->merge_group.data)));
372}
373
375 const bool use_merge_group)
376{
378 BMesh *bm = em->bm;
379
380 if (bm->shapenr > 1) {
381 /* Don't do this at all for non-basis shape keys, too easy to
382 * accidentally break uv maps or vertex colors then. */
383 /* Create copies of faces for custom-data projection. */
384 return nullptr;
385 }
386 if (!CustomData_has_math(&bm->ldata) && !CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
387 /* There is no custom-data to correct. */
388 return nullptr;
389 }
390
391 TransCustomDataLayer *tcld = static_cast<TransCustomDataLayer *>(
392 MEM_callocN(sizeof(*tcld), __func__));
393 tcld->bm = bm;
395
396 /* Init `cd_loop_mdisp_offset` to -1 to avoid problems with a valid index. */
397 tcld->cd_loop_mdisp_offset = -1;
398 tcld->use_merge_group = use_merge_group;
399
401
402 if (tcld->use_merge_group) {
404 }
405
406 {
407 /* Setup Verts. */
408 int i = 0;
409
410 TransData *tob = tc->data;
411 for (int j = tc->data_len; j--; tob++, i++) {
413 }
414
415 TransDataMirror *td_mirror = tc->data_mirror;
416 for (int j = tc->data_mirror_len; j--; td_mirror++, i++) {
418 }
419 }
420
421 return tcld;
422}
423
424static void mesh_customdatacorrect_create(TransDataContainer *tc, const bool use_merge_group)
425{
426 TransCustomDataLayer *customdatacorrect;
427 customdatacorrect = mesh_customdatacorrect_create_impl(tc, use_merge_group);
428
429 if (!customdatacorrect) {
430 return;
431 }
432
434 BLI_assert(tcmd->cd_layer_correct == nullptr);
435 tcmd->cd_layer_correct = customdatacorrect;
436}
437
439{
441
442 if (tcld->bm_origfaces) {
444 }
445 if (tcld->origfaces) {
446 BLI_ghash_free(tcld->origfaces, nullptr, nullptr);
447 }
448 if (tcld->merge_group.origverts) {
449 BLI_ghash_free(tcld->merge_group.origverts, nullptr, nullptr);
450 }
451 if (tcld->arena) {
453 }
456 }
457
458 MEM_freeN(tcld);
459}
460
462{
463 bool use_merge_group = false;
466 /* No custom-data correction. */
467 return;
468 }
469 use_merge_group = true;
470 }
471 else if (ELEM(t->mode,
476 TFM_SHEAR,
477 TFM_BEND,
481 TFM_ALIGN))
482 {
483 {
485 /* No custom-data correction. */
486 return;
487 }
488 use_merge_group = (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_KEEP_CONNECTED) != 0;
489 }
490 }
491 else {
492 return;
493 }
494
496 if (tc->custom.type.data != nullptr) {
497 TransCustomDataMesh *tcmd = static_cast<TransCustomDataMesh *>(tc->custom.type.data);
498 if (tcmd && tcmd->cd_layer_correct) {
500 tcmd->cd_layer_correct = nullptr;
501 }
502 }
503
504 mesh_customdatacorrect_create(tc, use_merge_group);
505 }
506}
507
509
510/* -------------------------------------------------------------------- */
513
518{
519 TransDataBasic *td = static_cast<TransDataBasic *>(
521 return td ? td->iloc : v->co;
522}
523
525 TransDataBasic *td,
526 TransCustomDataMergeGroup *merge_data,
527 bool do_loop_mdisps)
528{
529 BMesh *bm = tcld->bm;
530 BMVert *v = static_cast<BMVert *>(td->extra);
531 const float *co_orig_3d = td->iloc;
532
533 BMIter liter;
534 int j, l_num;
535 float *loop_weights;
536 const bool is_moved = (len_squared_v3v3(v->co, co_orig_3d) > FLT_EPSILON);
537 const bool do_loop_weight = is_moved && tcld->merge_group.customdatalayer_map_len;
538 const float *v_proj_axis = v->no;
539 /* Original (`l->prev`, `l`, `l->next`) projections for each loop ('l' remains unchanged). */
540 float v_proj[3][3];
541
542 if (do_loop_weight) {
543 project_plane_normalized_v3_v3v3(v_proj[1], co_orig_3d, v_proj_axis);
544 }
545
546 // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT)
548 l_num = liter.count;
549 loop_weights = do_loop_weight ? static_cast<float *>(BLI_array_alloca(loop_weights, l_num)) :
550 nullptr;
551 for (j = 0; j < l_num; j++) {
552 BMFace *f_copy; /* The copy of 'f'. */
553 BMLoop *l = static_cast<BMLoop *>(BM_iter_step(&liter));
554
555 f_copy = static_cast<BMFace *>(BLI_ghash_lookup(tcld->origfaces, l->f));
556
557#ifdef USE_FACE_SUBSTITUTE
558 /* In some faces it is not possible to calculate interpolation,
559 * so we use a substitute. */
562 }
563#endif
564
565 /* Only loop data, no vertex data since that contains shape keys,
566 * and we do not want to mess up other shape keys. */
567 BM_loop_interp_from_face(bm, l, f_copy, false, false);
568
569 /* Weight the loop. */
570 if (do_loop_weight) {
571 const float eps = 1.0e-8f;
572 const BMLoop *l_prev = l->prev;
573 const BMLoop *l_next = l->next;
574 const float *co_prev = mesh_vert_orig_co_get(tcld, l_prev->v);
575 const float *co_next = mesh_vert_orig_co_get(tcld, l_next->v);
576 bool co_prev_ok;
577 bool co_next_ok;
578
579 /* In the unlikely case that we're next to a zero length edge - walk around to the next.
580 *
581 * Since we only need to check if the vertex is in this corner,
582 * its not important _which_ loop - as long as its not overlapping
583 * `sv->co_orig_3d`, see: #45096. */
584 project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis);
585 while (UNLIKELY(((co_prev_ok = (len_squared_v3v3(v_proj[1], v_proj[0]) > eps)) == false) &&
586 ((l_prev = l_prev->prev) != l->next)))
587 {
588 co_prev = mesh_vert_orig_co_get(tcld, l_prev->v);
589 project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis);
590 }
591 project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis);
592 while (UNLIKELY(((co_next_ok = (len_squared_v3v3(v_proj[1], v_proj[2]) > eps)) == false) &&
593 ((l_next = l_next->next) != l->prev)))
594 {
595 co_next = mesh_vert_orig_co_get(tcld, l_next->v);
596 project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis);
597 }
598
599 if (co_prev_ok && co_next_ok) {
600 const float dist = dist_signed_squared_to_corner_v3v3v3(
601 v->co, UNPACK3(v_proj), v_proj_axis);
602
603 loop_weights[j] = (dist >= 0.0f) ? 1.0f : ((dist <= -eps) ? 0.0f : (1.0f + (dist / eps)));
604 if (UNLIKELY(!isfinite(loop_weights[j]))) {
605 loop_weights[j] = 0.0f;
606 }
607 }
608 else {
609 loop_weights[j] = 0.0f;
610 }
611 }
612 }
613
614 if (tcld->use_merge_group) {
615 LinkNode **cd_loop_groups = merge_data->cd_loop_groups;
616 if (tcld->merge_group.customdatalayer_map_len && cd_loop_groups) {
617 if (do_loop_weight) {
618 for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
620 bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j], loop_weights);
621 }
622 }
623 else {
624 for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
626 bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j]);
627 }
628 }
629 }
630 }
631
632 /* Special handling for multires
633 *
634 * Interpolate from every other loop (not ideal)
635 * However values will only be taken from loops which overlap other mdisps.
636 */
637 const bool update_loop_mdisps = is_moved && do_loop_mdisps && (tcld->cd_loop_mdisp_offset != -1);
638 if (update_loop_mdisps) {
639 float (*faces_center)[3] = static_cast<float (*)[3]>(BLI_array_alloca(faces_center, l_num));
640 BMLoop *l;
641
642 BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) {
643 BM_face_calc_center_median(l->f, faces_center[j]);
644 }
645
646 BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) {
647 BMFace *f_copy = static_cast<BMFace *>(BLI_ghash_lookup(tcld->origfaces, l->f));
648 float f_copy_center[3];
649 BMIter liter_other;
650 BMLoop *l_other;
651 int j_other;
652
653 BM_face_calc_center_median(f_copy, f_copy_center);
654
655 BM_ITER_ELEM_INDEX (l_other, &liter_other, v, BM_LOOPS_OF_VERT, j_other) {
657 l_other->f,
658 f_copy,
659 faces_center[j_other],
660 f_copy_center,
662 }
663 }
664 }
665}
666
668{
669 TransCustomDataMesh *tcmd = static_cast<TransCustomDataMesh *>(tc->custom.type.data);
670 TransCustomDataLayer *tcld = tcmd ? tcmd->cd_layer_correct : nullptr;
671 if (tcld == nullptr) {
672 return;
673 }
674 const bool use_merge_group = tcld->use_merge_group;
675
676 TransCustomDataMergeGroup *merge_data = tcld->merge_group.data;
677 TransData *tob = tc->data;
678 for (int i = tc->data_len; i--; tob++) {
679 mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)tob, merge_data, is_final);
680
681 if (use_merge_group) {
682 merge_data++;
683 }
684 }
685
686 TransDataMirror *td_mirror = tc->data_mirror;
687 for (int i = tc->data_mirror_len; i--; td_mirror++) {
688 mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)td_mirror, merge_data, is_final);
689
690 if (use_merge_group) {
691 merge_data++;
692 }
693 }
694}
695
697
698/* -------------------------------------------------------------------- */
701
703{
705 TransCustomDataMesh *tcmd = static_cast<TransCustomDataMesh *>(tc->custom.type.data);
706 TransCustomDataLayer *tcld = tcmd ? tcmd->cd_layer_correct : nullptr;
707 if (!tcld) {
708 continue;
709 }
710
711 BMesh *bm = tcld->bm;
712 BMesh *bm_copy = tcld->bm_origfaces;
713 const BMCustomDataCopyMap cd_loop_map = CustomData_bmesh_copy_map_calc(bm_copy->ldata,
714 bm->ldata);
715
716 GHashIterator gh_iter;
717 GHASH_ITER (gh_iter, tcld->origfaces) {
718 BMFace *f = static_cast<BMFace *>(BLI_ghashIterator_getKey(&gh_iter));
719 BMFace *f_copy = static_cast<BMFace *>(BLI_ghashIterator_getValue(&gh_iter));
720 BLI_assert(f->len == f_copy->len);
721
722 BMLoop *l_iter, *l_first, *l_copy;
723 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
724 l_copy = BM_FACE_FIRST_LOOP(f_copy);
725 do {
726 /* TODO: Restore only the elements that transform. */
727 BM_elem_attrs_copy(bm, cd_loop_map, l_copy, l_iter);
728 l_copy = l_copy->next;
729 } while ((l_iter = l_iter->next) != l_first);
730 }
731 }
732}
733
735
736/* -------------------------------------------------------------------- */
739
741 const bool calc_single_islands,
742 const bool calc_island_center,
743 const bool calc_island_axismtx,
744 TransIslandData *r_island_data)
745{
746 TransIslandData data = {nullptr};
747
748 BMesh *bm = em->bm;
749 char htype;
750 char itype;
751 int i;
752
753 /* Group variables. */
754 int *groups_array = nullptr;
755 int (*group_index)[2] = nullptr;
756
757 bool has_only_single_islands = bm->totedgesel == 0 && bm->totfacesel == 0;
758 if (has_only_single_islands && !calc_single_islands) {
759 return;
760 }
761
762 data.island_vert_map = static_cast<int *>(
763 MEM_mallocN(sizeof(*data.island_vert_map) * bm->totvert, __func__));
764 /* We shouldn't need this, but with incorrect selection flushing
765 * its possible we have a selected vertex that's not in a face,
766 * for now best not crash in that case. */
767 copy_vn_i(data.island_vert_map, bm->totvert, -1);
768
769 if (!has_only_single_islands) {
771 groups_array = MEM_malloc_arrayN<int>(bm->totedgesel, __func__);
772 data.island_tot = BM_mesh_calc_edge_groups(
773 bm, groups_array, &group_index, nullptr, nullptr, BM_ELEM_SELECT);
774
775 htype = BM_EDGE;
777 }
778 else { /* `bm->selectmode & SCE_SELECT_FACE`. */
779 groups_array = MEM_malloc_arrayN<int>(bm->totfacesel, __func__);
780 data.island_tot = BM_mesh_calc_face_groups(
781 bm, groups_array, &group_index, nullptr, nullptr, nullptr, BM_ELEM_SELECT, BM_VERT);
782
783 htype = BM_FACE;
785 }
786
787 BLI_assert(data.island_tot);
788 if (calc_island_center) {
789 data.center = static_cast<float (*)[3]>(
790 MEM_mallocN(sizeof(*data.center) * data.island_tot, __func__));
791 }
792
793 if (calc_island_axismtx) {
794 data.axismtx = static_cast<float (*)[3][3]>(
795 MEM_mallocN(sizeof(*data.axismtx) * data.island_tot, __func__));
796 }
797
799
800 void **ele_array;
801 ele_array = (htype == BM_FACE) ? (void **)bm->ftable : (void **)bm->etable;
802
804
805 /* May be an edge OR a face array. */
806 for (i = 0; i < data.island_tot; i++) {
807 BMEditSelection ese = {nullptr};
808
809 const int fg_sta = group_index[i][0];
810 const int fg_len = group_index[i][1];
811 float co[3], no[3], tangent[3];
812 int j;
813
814 zero_v3(co);
815 zero_v3(no);
816 zero_v3(tangent);
817
818 ese.htype = htype;
819
820 /* Loop on each face or edge in this group:
821 * - Assign `r_vert_map`.
822 * - Calculate (`co`, `no`).
823 */
824 for (j = 0; j < fg_len; j++) {
825 ese.ele = static_cast<BMElem *>(ele_array[groups_array[fg_sta + j]]);
826
827 if (data.center) {
828 float tmp_co[3];
829 BM_editselection_center(&ese, tmp_co);
830 add_v3_v3(co, tmp_co);
831 }
832
833 if (data.axismtx) {
834 float tmp_no[3], tmp_tangent[3];
835 BM_editselection_normal(&ese, tmp_no);
836 BM_editselection_plane(&ese, tmp_tangent);
837 add_v3_v3(no, tmp_no);
838 add_v3_v3(tangent, tmp_tangent);
839 }
840
841 {
842 /* Setup vertex map. */
843 BMIter iter;
844 BMVert *v;
845
846 /* Connected edge-verts. */
847 BM_ITER_ELEM (v, &iter, ese.ele, itype) {
848 data.island_vert_map[BM_elem_index_get(v)] = i;
849 }
850 }
851 }
852
853 if (data.center) {
854 mul_v3_v3fl(data.center[i], co, 1.0f / float(fg_len));
855 }
856
857 if (data.axismtx) {
858 normalize_v3(no);
859 normalize_v3(tangent);
860
861 createSpaceNormalTangent_or_fallback(data.axismtx[i], no, tangent);
862 }
863 }
864
865 MEM_freeN(groups_array);
866 MEM_freeN(group_index);
867 }
868
869 /* For proportional editing we need islands of 1 so connected vertices can use it with
870 * #V3D_AROUND_LOCAL_ORIGINS. */
871 if (calc_single_islands) {
872 BMIter viter;
873 BMVert *v;
874 int group_tot_single = 0;
875
877 if (BM_elem_flag_test(v, BM_ELEM_SELECT) && (data.island_vert_map[i] == -1)) {
878 group_tot_single += 1;
879 }
880 }
881
882 if (group_tot_single != 0) {
883 if (calc_island_center) {
884 data.center = static_cast<float (*)[3]>(MEM_reallocN(
885 data.center, sizeof(*data.center) * (data.island_tot + group_tot_single)));
886 }
887 if (calc_island_axismtx) {
888 data.axismtx = static_cast<float (*)[3][3]>(MEM_reallocN(
889 data.axismtx, sizeof(*data.axismtx) * (data.island_tot + group_tot_single)));
890 }
891
893 if (BM_elem_flag_test(v, BM_ELEM_SELECT) && (data.island_vert_map[i] == -1)) {
894 data.island_vert_map[i] = data.island_tot;
895 if (data.center) {
896 copy_v3_v3(data.center[data.island_tot], v->co);
897 }
898 if (data.axismtx) {
899 if (is_zero_v3(v->no) == false) {
900 axis_dominant_v3_to_m3(data.axismtx[data.island_tot], v->no);
901 invert_m3(data.axismtx[data.island_tot]);
902 }
903 else {
904 unit_m3(data.axismtx[data.island_tot]);
905 }
906 }
907
908 data.island_tot += 1;
909 }
910 }
911 }
912 }
913
914 *r_island_data = data;
915}
916
918{
919 if (island_data->center) {
920 MEM_freeN(island_data->center);
921 }
922 if (island_data->axismtx) {
923 MEM_freeN(island_data->axismtx);
924 }
925 if (island_data->island_vert_map) {
926 MEM_freeN(island_data->island_vert_map);
927 }
928}
929
931
932/* -------------------------------------------------------------------- */
935
936/* Propagate distance from v1 and v2 to v0. */
938 BMVert *v1,
939 BMVert *v2,
940 float *dists, /* Optionally track original index. */
941 int *index,
942 const float mtx[3][3])
943{
945 {
946 const int i0 = BM_elem_index_get(v0);
947 const int i1 = BM_elem_index_get(v1);
948
949 BLI_assert(dists[i1] != FLT_MAX);
950 if (dists[i0] <= dists[i1]) {
951 return false;
952 }
953
954 float dist0;
955
956 if (v2) {
957 /* Distance across triangle. */
958 const int i2 = BM_elem_index_get(v2);
959 BLI_assert(dists[i2] != FLT_MAX);
960 if (dists[i0] <= dists[i2]) {
961 return false;
962 }
963
964 float vm0[3], vm1[3], vm2[3];
965 mul_v3_m3v3(vm0, mtx, v0->co);
966 mul_v3_m3v3(vm1, mtx, v1->co);
967 mul_v3_m3v3(vm2, mtx, v2->co);
968
969 dist0 = geodesic_distance_propagate_across_triangle(vm0, vm1, vm2, dists[i1], dists[i2]);
970 }
971 else {
972 /* Distance along edge. */
973 float vec[3];
974 sub_v3_v3v3(vec, v1->co, v0->co);
975 mul_m3_v3(mtx, vec);
976
977 dist0 = dists[i1] + len_v3(vec);
978 }
979
980 if (dist0 < dists[i0]) {
981 dists[i0] = dist0;
982 if (index != nullptr) {
983 index[i0] = index[i1];
984 }
985 return true;
986 }
987 }
988
989 return false;
990}
991
993{
994 /* Actual loose edge. */
995 if (edge->l == nullptr) {
996 return true;
997 }
998
999 /* Loose edge due to hidden adjacent faces. */
1000 BMIter iter;
1001 BMFace *face;
1002 BM_ITER_ELEM (face, &iter, edge, BM_FACES_OF_EDGE) {
1003 if (BM_elem_flag_test(face, BM_ELEM_HIDDEN) == 0) {
1004 return false;
1005 }
1006 }
1007 return true;
1008}
1009
1011 const float mtx[3][3],
1012 float *dists,
1013 int *index)
1014{
1015 BLI_LINKSTACK_DECLARE(queue, BMEdge *);
1016
1017 /* Any BM_ELEM_TAG'd edge is in 'queue_next', so we don't add in twice. */
1018 const int tag_queued = BM_ELEM_TAG;
1019 const int tag_loose = BM_ELEM_TAG_ALT;
1020
1021 BLI_LINKSTACK_DECLARE(queue_next, BMEdge *);
1022
1023 BLI_LINKSTACK_INIT(queue);
1024 BLI_LINKSTACK_INIT(queue_next);
1025
1026 {
1027 /* Set indexes and initial distances for selected vertices. */
1028 BMIter viter;
1029 BMVert *v;
1030 int i;
1031
1033 float dist;
1034 BM_elem_index_set(v, i); /* set_inline */
1035
1037 dist = FLT_MAX;
1038 if (index != nullptr) {
1039 index[i] = i;
1040 }
1041 }
1042 else {
1043 dist = 0.0f;
1044 if (index != nullptr) {
1045 index[i] = i;
1046 }
1047 }
1048
1049 dists[i] = dist;
1050 }
1051 bm->elem_index_dirty &= ~BM_VERT;
1052 }
1053
1054 {
1055 /* Add edges with at least one selected vertex to the queue. */
1056 BMIter eiter;
1057 BMEdge *e;
1058
1059 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
1060
1061 /* Always clear to satisfy the assert, also predictable to leave in cleared state. */
1062 BM_elem_flag_disable(e, tag_queued);
1063
1065 continue;
1066 }
1067
1068 BMVert *v1 = e->v1;
1069 BMVert *v2 = e->v2;
1070 int i1 = BM_elem_index_get(v1);
1071 int i2 = BM_elem_index_get(v2);
1072
1073 if (dists[i1] != FLT_MAX || dists[i2] != FLT_MAX) {
1074 BLI_LINKSTACK_PUSH(queue, e);
1075 }
1077 }
1078 }
1079
1080 do {
1081 BMEdge *e;
1082
1083 while ((e = BLI_LINKSTACK_POP(queue))) {
1084 BMVert *v1 = e->v1;
1085 BMVert *v2 = e->v2;
1086 int i1 = BM_elem_index_get(v1);
1087 int i2 = BM_elem_index_get(v2);
1088
1089 if (BM_elem_flag_test(e, tag_loose) || (dists[i1] == FLT_MAX || dists[i2] == FLT_MAX)) {
1090 /* Propagate along edge from vertex with smallest to largest distance. */
1091 if (dists[i1] > dists[i2]) {
1092 std::swap(i1, i2);
1093 std::swap(v1, v2);
1094 }
1095
1096 if (bmesh_test_dist_add(v2, v1, nullptr, dists, index, mtx)) {
1097 /* Add adjacent edges to the queue if:
1098 * - Adjacent edge is loose
1099 * - Edge itself is loose
1100 * - Edge has vertex that was originally selected
1101 * In all these cases a direct distance along the edge is accurate and
1102 * required to make sure we visit all edges. Other edges are handled by
1103 * propagation across edges below. */
1104 const bool need_direct_distance = BM_elem_flag_test(e, tag_loose) ||
1107 BMEdge *e_other;
1108 BMIter eiter;
1109 BM_ITER_ELEM (e_other, &eiter, v2, BM_EDGES_OF_VERT) {
1110 if (e_other != e && BM_elem_flag_test(e_other, tag_queued) == 0 &&
1111 !BM_elem_flag_test(e_other, BM_ELEM_HIDDEN) &&
1112 (need_direct_distance || BM_elem_flag_test(e_other, tag_loose)))
1113 {
1114 BM_elem_flag_enable(e_other, tag_queued);
1115 BLI_LINKSTACK_PUSH(queue_next, e_other);
1116 }
1117 }
1118 }
1119 }
1120
1121 if (!BM_elem_flag_test(e, tag_loose)) {
1122 /* Propagate across edge to vertices in adjacent faces. */
1123 BMLoop *l;
1124 BMIter liter;
1125 BM_ITER_ELEM (l, &liter, e, BM_LOOPS_OF_EDGE) {
1127 continue;
1128 }
1129 /* Don't check hidden edges or vertices in this loop
1130 * since any hidden edge causes the face to be hidden too. */
1131 for (BMLoop *l_other = l->next->next; l_other != l; l_other = l_other->next) {
1132 BMVert *v_other = l_other->v;
1133 BLI_assert(!ELEM(v_other, v1, v2));
1134
1135 if (bmesh_test_dist_add(v_other, v1, v2, dists, index, mtx)) {
1136 /* Add adjacent edges to the queue, if they are ready to propagate across/along.
1137 * Always propagate along loose edges, and for other edges only propagate across
1138 * if both vertices have a known distances. */
1139 BMEdge *e_other;
1140 BMIter eiter;
1141 BM_ITER_ELEM (e_other, &eiter, v_other, BM_EDGES_OF_VERT) {
1142 if (e_other != e && BM_elem_flag_test(e_other, tag_queued) == 0 &&
1143 !BM_elem_flag_test(e_other, BM_ELEM_HIDDEN) &&
1144 (BM_elem_flag_test(e_other, tag_loose) ||
1145 dists[BM_elem_index_get(BM_edge_other_vert(e_other, v_other))] != FLT_MAX))
1146 {
1147 BM_elem_flag_enable(e_other, tag_queued);
1148 BLI_LINKSTACK_PUSH(queue_next, e_other);
1149 }
1150 }
1151 }
1152 }
1153 }
1154 }
1155 }
1156
1157 /* Clear for the next loop. */
1158 for (LinkNode *lnk = queue_next; lnk; lnk = lnk->next) {
1159 BMEdge *e_link = static_cast<BMEdge *>(lnk->link);
1160
1161 BM_elem_flag_disable(e_link, tag_queued);
1162 }
1163
1164 BLI_LINKSTACK_SWAP(queue, queue_next);
1165
1166 /* None should be tagged now since 'queue_next' is empty. */
1167 BLI_assert(BM_iter_mesh_count_flag(BM_EDGES_OF_MESH, bm, tag_queued, true) == 0);
1168 } while (BLI_LINKSTACK_SIZE(queue));
1169
1170 BLI_LINKSTACK_FREE(queue);
1171 BLI_LINKSTACK_FREE(queue_next);
1172}
1173
1175
1176/* -------------------------------------------------------------------- */
1179
1180/* Used for both mirror epsilon and TD_MIRROR_EDGE_ */
1181#define TRANSFORM_MAXDIST_MIRROR 0.00002f
1182
1183static bool is_in_quadrant_v3(const float co[3], const int quadrant[3], const float epsilon)
1184{
1185 if (quadrant[0] && ((co[0] * quadrant[0]) < -epsilon)) {
1186 return false;
1187 }
1188 if (quadrant[1] && ((co[1] * quadrant[1]) < -epsilon)) {
1189 return false;
1190 }
1191 if (quadrant[2] && ((co[2] * quadrant[2]) < -epsilon)) {
1192 return false;
1193 }
1194 return true;
1195}
1196
1198 const bool use_select,
1199 const bool use_topology,
1200 const bool mirror_axis[3],
1201 TransMirrorData *r_mirror_data)
1202{
1203 MirrorDataVert *vert_map;
1204
1205 BMesh *bm = em->bm;
1206 BMVert *eve;
1207 BMIter iter;
1208 int i, flag, totvert = bm->totvert;
1209
1210 vert_map = MEM_calloc_arrayN<MirrorDataVert>(totvert, __func__);
1211
1212 float select_sum[3] = {0};
1213 BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
1214 vert_map[i] = MirrorDataVert{-1, 0};
1216 continue;
1217 }
1219 add_v3_v3(select_sum, eve->co);
1220 }
1221 }
1222
1223 /* Tag only elements that will be transformed within the quadrant. */
1224 int quadrant[3];
1225 for (int a = 0; a < 3; a++) {
1226 if (mirror_axis[a]) {
1227 quadrant[a] = select_sum[a] >= 0.0f ? 1 : -1;
1228 }
1229 else {
1230 quadrant[a] = 0;
1231 }
1232 }
1233
1234 uint mirror_elem_len = 0;
1235 int *index[3] = {nullptr, nullptr, nullptr};
1236 bool is_single_mirror_axis = (mirror_axis[0] + mirror_axis[1] + mirror_axis[2]) == 1;
1237 bool test_selected_only = use_select && is_single_mirror_axis;
1238 for (int a = 0; a < 3; a++) {
1239 if (!mirror_axis[a]) {
1240 continue;
1241 }
1242
1243 index[a] = static_cast<int *>(MEM_mallocN(totvert * sizeof(*index[a]), __func__));
1245 em, a, false, test_selected_only, true, use_topology, TRANSFORM_MAXDIST_MIRROR, index[a]);
1246
1247 flag = TD_MIRROR_X << a;
1248 BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
1249 int i_mirr = index[a][i];
1250 if (i_mirr < 0) {
1251 continue;
1252 }
1254 continue;
1255 }
1256 if (use_select && !BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
1257 continue;
1258 }
1259 if (!is_in_quadrant_v3(eve->co, quadrant, TRANSFORM_MAXDIST_MIRROR)) {
1260 continue;
1261 }
1262 if (vert_map[i_mirr].flag != 0) {
1263 /* One mirror per element.
1264 * It can happen when vertices occupy the same position. */
1265 continue;
1266 }
1267 if (vert_map[i].flag & flag) {
1268 /* It's already a mirror.
1269 * Avoid a mirror vertex dependency cycle.
1270 * This can happen when the vertices are within the mirror threshold. */
1271 continue;
1272 }
1273
1274 vert_map[i_mirr] = MirrorDataVert{i, flag};
1275 mirror_elem_len++;
1276 }
1277 }
1278
1279 if (!mirror_elem_len) {
1280 MEM_freeN(vert_map);
1281 vert_map = nullptr;
1282 }
1283 else if (!is_single_mirror_axis) {
1284 /* Adjustment for elements that are mirrors of mirrored elements. */
1285 for (int a = 0; a < 3; a++) {
1286 if (!mirror_axis[a]) {
1287 continue;
1288 }
1289
1290 flag = TD_MIRROR_X << a;
1291 for (i = 0; i < totvert; i++) {
1292 int i_mirr = index[a][i];
1293 if (i_mirr < 0) {
1294 continue;
1295 }
1296 if (vert_map[i].index != -1 && !(vert_map[i].flag & flag)) {
1297 if (vert_map[i_mirr].index == -1) {
1298 mirror_elem_len++;
1299 }
1300 vert_map[i_mirr].index = vert_map[i].index;
1301 vert_map[i_mirr].flag |= vert_map[i].flag | flag;
1302 }
1303 }
1304 }
1305 }
1306
1307 MEM_SAFE_FREE(index[0]);
1308 MEM_SAFE_FREE(index[1]);
1309 MEM_SAFE_FREE(index[2]);
1310
1311 r_mirror_data->vert_map = vert_map;
1312 r_mirror_data->mirror_elem_len = mirror_elem_len;
1313}
1314
1316{
1317 if (mirror_data->vert_map) {
1318 MEM_freeN(mirror_data->vert_map);
1319 }
1320}
1321
1323
1324/* -------------------------------------------------------------------- */
1327
1330 BMEditMesh *em,
1331 TransMeshDataCrazySpace *r_crazyspace_data)
1332{
1333 float (*quats)[4] = nullptr;
1334 const int prop_mode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0;
1335 if (BKE_modifiers_get_cage_index(t->scene, tc->obedit, nullptr, true) != -1) {
1336 Array<float3, 0> defcos;
1337 int totleft = -1;
1340
1341 /* Use evaluated state because we need b-bone cache. */
1342 Scene *scene_eval = DEG_get_evaluated(t->depsgraph, t->scene);
1343 Object *obedit_eval = DEG_get_evaluated(t->depsgraph, tc->obedit);
1344 BMEditMesh *em_eval = BKE_editmesh_from_object(obedit_eval);
1345 /* Check if we can use deform matrices for modifier from the
1346 * start up to stack, they are more accurate than quats. */
1348 t->depsgraph, scene_eval, obedit_eval, em_eval, r_crazyspace_data->defmats, defcos);
1349 }
1350
1351 /* If we still have more modifiers, also do crazy-space
1352 * correction with \a quats, relative to the coordinates after
1353 * the modifiers that support deform matrices \a defcos. */
1354
1355#if 0 /* TODO(@ideasman42): fix crazy-space & extrude so it can be enabled for general use. */
1356 if ((totleft > 0) || (totleft == -1))
1357#else
1358 if (totleft > 0)
1359#endif
1360 {
1362 tc->obedit);
1363 quats = static_cast<float (*)[4]>(
1364 MEM_mallocN(em->bm->totvert * sizeof(*quats), "crazy quats"));
1365 BKE_crazyspace_set_quats_editmesh(em, defcos, mappedcos, quats, !prop_mode);
1366 }
1367 }
1368 r_crazyspace_data->quats = quats;
1369}
1370
1372 const float smtx[3][3],
1373 const float defmat[3][3],
1374 const float quat[4],
1375 TransData *r_td)
1376{
1377 /* CrazySpace. */
1378 if (quat || defmat) {
1379 float mat[3][3], qmat[3][3], imat[3][3];
1380
1381 /* Use both or either quat and defmat correction. */
1382 if (quat) {
1383 quat_to_mat3(qmat, quat);
1384
1385 if (defmat) {
1386 mul_m3_series(mat, defmat, qmat, mtx);
1387 }
1388 else {
1389 mul_m3_m3m3(mat, mtx, qmat);
1390 }
1391 }
1392 else {
1393 mul_m3_m3m3(mat, mtx, defmat);
1394 }
1395
1396 invert_m3_m3(imat, mat);
1397
1398 copy_m3_m3(r_td->smtx, imat);
1399 copy_m3_m3(r_td->mtx, mat);
1400 }
1401 else {
1402 copy_m3_m3(r_td->smtx, smtx);
1403 copy_m3_m3(r_td->mtx, mtx);
1404 }
1405}
1406
1408{
1409 if (r_crazyspace_data->quats) {
1410 MEM_freeN(r_crazyspace_data->quats);
1411 }
1412}
1413
1415
1416/* -------------------------------------------------------------------- */
1419
1420static void mesh_transdata_center_copy(const TransIslandData *island_data,
1421 const int island_index,
1422 const float iloc[3],
1423 float r_center[3])
1424{
1425 if (island_data->center && island_index != -1) {
1426 copy_v3_v3(r_center, island_data->center[island_index]);
1427 }
1428 else {
1429 copy_v3_v3(r_center, iloc);
1430 }
1431}
1432
1433/* Way to overwrite what data is edited with transform. */
1435 TransData *td,
1437 BMEditMesh *em,
1438 BMVert *eve,
1439 const TransIslandData *island_data,
1440 const int island_index)
1441{
1442 float *no, _no[3];
1444
1445 td->flag = 0;
1446 // if (key)
1447 // td->loc = key->co;
1448 // else
1449 td->loc = eve->co;
1450 copy_v3_v3(td->iloc, td->loc);
1451
1452 if ((t->mode == TFM_SHRINKFATTEN) && (em->selectmode & SCE_SELECT_FACE) &&
1454 {
1455 no = _no;
1456 }
1457 else {
1458 no = eve->no;
1459 }
1460
1461 mesh_transdata_center_copy(island_data, island_index, td->iloc, td->center);
1462
1463 if ((island_index != -1) && island_data->axismtx) {
1464 copy_m3_m3(td->axismtx, island_data->axismtx[island_index]);
1465 }
1466 else if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
1467 createSpaceNormal(td->axismtx, no);
1468 }
1469 else {
1470 /* Setting normals. */
1471 copy_v3_v3(td->axismtx[2], no);
1472 td->axismtx[0][0] = td->axismtx[0][1] = td->axismtx[0][2] = td->axismtx[1][0] =
1473 td->axismtx[1][1] = td->axismtx[1][2] = 0.0f;
1474 }
1475
1476 td->val = nullptr;
1477 td->extra = eve;
1478 if (t->mode == TFM_SHRINKFATTEN) {
1480 }
1481}
1482
1484{
1486 if (t->mode == TFM_NORMAL_ROTATION) {
1487 /* Avoid freeing the container by creating a dummy TransData. The Rotate Normal mode uses a
1488 * custom array and ignores any elements created for the mesh in transData and similar
1489 * structures. */
1490 tc->data_len = 1;
1491 tc->data = MEM_calloc_arrayN<TransData>(tc->data_len, "TransData Dummy");
1492 continue;
1493 }
1494
1495 TransDataExtension *tx = nullptr;
1496 BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
1497 Mesh *mesh = static_cast<Mesh *>(tc->obedit->data);
1498 BMesh *bm = em->bm;
1499 BMVert *eve;
1500 BMIter iter;
1501 float mtx[3][3], smtx[3][3];
1502 int a;
1503 const int prop_mode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0;
1504
1505 TransIslandData island_data = {nullptr};
1506 TransMirrorData mirror_data = {nullptr};
1507 TransMeshDataCrazySpace crazyspace_data = {};
1508
1509 /* Avoid editing locked shapes. */
1510 if (t->mode != TFM_DUMMY && object::shape_key_report_if_locked(tc->obedit, t->reports)) {
1511 continue;
1512 }
1513
1520
1521 /* Support other objects using proportional editing to adjust these, unless connected is
1522 * enabled. */
1523 if ((!prop_mode || (prop_mode & T_PROP_CONNECTED)) && (bm->totvertsel == 0)) {
1524 continue;
1525 }
1526
1527 int data_len = 0;
1528 if (prop_mode) {
1529 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
1530 if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
1531 data_len++;
1532 }
1533 }
1534 }
1535 else {
1536 data_len = bm->totvertsel;
1537 }
1538
1539 if (data_len == 0) {
1540 continue;
1541 }
1542
1543 /* Snap rotation along normal needs a common axis for whole islands,
1544 * otherwise one get random crazy results, see #59104.
1545 * However, we do not want to use the island center for the pivot/translation reference. */
1546 const bool is_snap_rotate = ((t->mode == TFM_TRANSLATION) &&
1547 /* There is not guarantee that snapping
1548 * is initialized yet at this point... */
1549 (usingSnappingNormal(t) ||
1550 (t->settings->snap_flag & SCE_SNAP_ROTATE) != 0) &&
1552
1553 /* Even for translation this is needed because of island-orientation, see: #51651. */
1554 const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS) || is_snap_rotate;
1555 if (is_island_center) {
1556 /* In this specific case, near-by vertices will need to know
1557 * the island of the nearest connected vertex. */
1558 const bool calc_single_islands = ((prop_mode & T_PROP_CONNECTED) &&
1561
1562 const bool calc_island_center = !is_snap_rotate;
1563 /* The island axismtx is only necessary in some modes.
1564 * TODO(Germano): Extend the list to exclude other modes. */
1565 const bool calc_island_axismtx = !ELEM(t->mode, TFM_SHRINKFATTEN);
1566
1568 em, calc_single_islands, calc_island_center, calc_island_axismtx, &island_data);
1569 }
1570
1571 copy_m3_m4(mtx, tc->obedit->object_to_world().ptr());
1572 /* We use a pseudo-inverse so that when one of the axes is scaled to 0,
1573 * matrix inversion still works and we can still moving along the other. */
1575
1576 /* Original index of our connected vertex when connected distances are calculated.
1577 * Optional, allocate if needed. */
1578 int *dists_index = nullptr;
1579 float *dists = nullptr;
1580 if (prop_mode & T_PROP_CONNECTED) {
1581 dists = MEM_malloc_arrayN<float>(bm->totvert, __func__);
1582 if (is_island_center) {
1583 dists_index = MEM_malloc_arrayN<int>(bm->totvert, __func__);
1584 }
1585 transform_convert_mesh_connectivity_distance(em->bm, mtx, dists, dists_index);
1586 }
1587
1588 /* Create TransDataMirror. */
1589 if (tc->use_mirror_axis_any) {
1590 bool use_topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
1591 bool use_select = (t->flag & T_PROP_EDIT) == 0;
1592 const bool mirror_axis[3] = {
1593 bool(tc->use_mirror_axis_x), bool(tc->use_mirror_axis_y), bool(tc->use_mirror_axis_z)};
1595 em, use_select, use_topology, mirror_axis, &mirror_data);
1596
1597 if (mirror_data.vert_map) {
1598 tc->data_mirror_len = mirror_data.mirror_elem_len;
1599 tc->data_mirror = static_cast<TransDataMirror *>(
1600 MEM_callocN(mirror_data.mirror_elem_len * sizeof(*tc->data_mirror), __func__));
1601
1602 BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) {
1603 if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
1604 if (mirror_data.vert_map[a].index != -1) {
1605 data_len--;
1606 }
1607 }
1608 }
1609 }
1610 }
1611
1612 /* Detect CrazySpace [tm]. */
1613 transform_convert_mesh_crazyspace_detect(t, tc, em, &crazyspace_data);
1614
1615 /* Create TransData. */
1616 BLI_assert(data_len >= 1);
1617 tc->data_len = data_len;
1618 tc->data = MEM_calloc_arrayN<TransData>(data_len, "TransObData(Mesh EditMode)");
1619 if (t->mode == TFM_SHRINKFATTEN) {
1620 /* Warning: this is overkill, we only need 2 extra floats,
1621 * but this stores loads of extra stuff, for TFM_SHRINKFATTEN its even more overkill
1622 * since we may not use the 'alt' transform mode to maintain shell thickness,
1623 * but with generic transform code its hard to lazy init variables. */
1624 tx = tc->data_ext = MEM_calloc_arrayN<TransDataExtension>(tc->data_len, "TransObData ext");
1625 }
1626
1627 TransData *tob = tc->data;
1628 TransDataMirror *td_mirror = tc->data_mirror;
1629 BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) {
1631 continue;
1632 }
1633
1634 int island_index = -1;
1635 if (island_data.island_vert_map) {
1636 const int connected_index = (dists_index && dists_index[a] != -1) ? dists_index[a] : a;
1637 island_index = island_data.island_vert_map[connected_index];
1638 }
1639
1640 if (mirror_data.vert_map && mirror_data.vert_map[a].index != -1) {
1641 int elem_index = mirror_data.vert_map[a].index;
1642 BMVert *v_src = BM_vert_at_index(bm, elem_index);
1643
1645 mirror_data.vert_map[a].flag |= TD_SELECTED;
1646 }
1647
1648 td_mirror->extra = eve;
1649 td_mirror->loc = eve->co;
1650 copy_v3_v3(td_mirror->iloc, eve->co);
1651 td_mirror->flag = mirror_data.vert_map[a].flag;
1652 td_mirror->loc_src = v_src->co;
1653 mesh_transdata_center_copy(&island_data, island_index, td_mirror->iloc, td_mirror->center);
1654
1655 td_mirror++;
1656 }
1657 else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
1658 /* Do not use the island center in case we are using islands
1659 * only to get axis for snap/rotate to normal... */
1660 VertsToTransData(t, tob, tx, em, eve, &island_data, island_index);
1661 if (tx) {
1662 tx++;
1663 }
1664
1665 /* Selected. */
1667 tob->flag |= TD_SELECTED;
1668 }
1669
1670 if (prop_mode) {
1671 if (prop_mode & T_PROP_CONNECTED) {
1672 tob->dist = dists[a];
1673 }
1674 else {
1675 tob->dist = FLT_MAX;
1676 }
1677 }
1678
1679 /* CrazySpace. */
1681 mtx,
1682 smtx,
1683 !crazyspace_data.defmats.is_empty() ? crazyspace_data.defmats[a].ptr() : nullptr,
1684 crazyspace_data.quats && BM_elem_flag_test(eve, BM_ELEM_TAG) ?
1685 crazyspace_data.quats[a] :
1686 nullptr,
1687 tob);
1688
1689 if (tc->use_mirror_axis_any) {
1690 if (tc->use_mirror_axis_x && fabsf(tob->loc[0]) < TRANSFORM_MAXDIST_MIRROR) {
1691 tob->flag |= TD_MIRROR_EDGE_X;
1692 }
1693 if (tc->use_mirror_axis_y && fabsf(tob->loc[1]) < TRANSFORM_MAXDIST_MIRROR) {
1694 tob->flag |= TD_MIRROR_EDGE_Y;
1695 }
1696 if (tc->use_mirror_axis_z && fabsf(tob->loc[2]) < TRANSFORM_MAXDIST_MIRROR) {
1697 tob->flag |= TD_MIRROR_EDGE_Z;
1698 }
1699 }
1700
1701 tob++;
1702 }
1703 }
1704
1708 if (dists) {
1709 MEM_freeN(dists);
1710 }
1711 if (dists_index) {
1712 MEM_freeN(dists_index);
1713 }
1714
1715 /* WORKAROUND: The transform operators rely on looptris being up-to-date.
1716 * However, this is not always the case, especially when called from scripts.
1717 * If this happens, to prevent update issues, make sure the size of #BMEditMesh::looptris
1718 * arrays aligns with the number looptris to update. */
1719 const bool looptri_is_dirty = em->looptris.size() !=
1720 poly_to_tri_count(bm->totface, bm->totloop);
1721 if (looptri_is_dirty) {
1723 }
1724 }
1725}
1726
1728
1729/* -------------------------------------------------------------------- */
1732
1735 enum ePartialType partial_type)
1736{
1738
1739 TransCustomData_PartialUpdate *pupdate = &tcmd->partial_update[partial_type];
1740
1741 if (pupdate->cache) {
1742
1743 /* Recalculate partial update data when the proportional editing size changes.
1744 *
1745 * Note that decreasing the proportional editing size requires the existing
1746 * partial data is used before recreating this partial data at the smaller size.
1747 * Since excluding geometry from being transformed requires an update.
1748 *
1749 * Extra logic is needed to account for this situation. */
1750
1751 bool recalc;
1752 if (pupdate->prop_size_prev < t->prop_size) {
1753 /* Size increase, simply recalculate. */
1754 recalc = true;
1755 }
1756 else if (pupdate->prop_size_prev > t->prop_size) {
1757 /* Size decreased, first use this partial data since reducing the size will transform
1758 * geometry which needs recalculating. */
1759 pupdate->prop_size_prev = t->prop_size;
1760 recalc = false;
1761 }
1762 else if (pupdate->prop_size != t->prop_size) {
1763 BLI_assert(pupdate->prop_size > pupdate->prop_size_prev);
1764 recalc = true;
1765 }
1766 else {
1767 BLI_assert(t->prop_size == pupdate->prop_size_prev);
1768 recalc = false;
1769 }
1770
1771 if (!recalc) {
1772 return pupdate->cache;
1773 }
1774
1776 pupdate->cache = nullptr;
1777 }
1778
1780
1782
1783 /* Only use `verts_group` or `verts_mask`. */
1784 Array<int> verts_group;
1785 int verts_group_count = 0; /* Number of non-zero elements in `verts_group`. */
1786
1787 blender::BitVector<> verts_mask;
1788 int verts_mask_count = 0; /* Number of elements enabled in `verts_mask`. */
1789
1790 if ((partial_type == PARTIAL_TYPE_GROUP) && ((t->flag & T_PROP_EDIT) || tc->use_mirror_axis_any))
1791 {
1792 verts_group = Array<int>(em->bm->totvert, 0);
1793 int i;
1794 TransData *td;
1795 for (i = 0, td = tc->data; i < tc->data_len; i++, td++) {
1796 if (td->factor == 0.0f) {
1797 continue;
1798 }
1799 const BMVert *v = (BMVert *)td->extra;
1800 const int v_index = BM_elem_index_get(v);
1801 BLI_assert(verts_group[v_index] == 0);
1802 if (td->factor < 1.0f) {
1803 /* Don't use grouping logic with the factor is under 1.0. */
1804 verts_group[v_index] = -1;
1805 }
1806 else {
1807 BLI_assert(td->factor == 1.0f);
1808 verts_group[v_index] = 1;
1809 if (tc->use_mirror_axis_any) {
1810 /* Use bits 2-4 for central alignment (don't overlap the first bit). */
1812 verts_group[v_index] |= (flag >> TD_MIRROR_EDGE_AXIS_SHIFT) << 1;
1813 }
1814 }
1815 verts_mask_count += 1;
1816 }
1817
1818 TransDataMirror *td_mirror = tc->data_mirror;
1819 for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) {
1820 BMVert *v_mirr = (BMVert *)POINTER_OFFSET(td_mirror->loc_src, -offsetof(BMVert, co));
1821 /* The equality check is to account for the case when topology mirror moves
1822 * the vertex from it's original location to match it's symmetrical position,
1823 * with proportional editing enabled. */
1824 const int v_mirr_index = BM_elem_index_get(v_mirr);
1825 if (verts_group[v_mirr_index] == 0 && equals_v3v3(td_mirror->loc, td_mirror->iloc)) {
1826 continue;
1827 }
1828
1829 BMVert *v_mirr_other = (BMVert *)td_mirror->extra;
1830 /* This assert should never fail since there is no overlap
1831 * between mirrored vertices and non-mirrored. */
1832 BLI_assert(verts_group[BM_elem_index_get(v_mirr_other)] == 0);
1833 const int v_mirr_other_index = BM_elem_index_get(v_mirr_other);
1834
1835 if (verts_group[v_mirr_index] == -1) {
1836 verts_group[v_mirr_other_index] = -1;
1837 }
1838 else {
1839 /* Use bits 5-8 for mirror (don't overlap previous bits). */
1840 const int flag = td_mirror->flag & (TD_MIRROR_X | TD_MIRROR_Y | TD_MIRROR_Z);
1841 verts_group[v_mirr_other_index] |= (flag >> TD_MIRROR_EDGE_AXIS_SHIFT) << 4;
1842 }
1843 verts_mask_count += 1;
1844 }
1845 }
1846 else {
1847 /* See the body of the comments in the previous block for details. */
1848 verts_mask.resize(em->bm->totvert);
1849 int i;
1850 TransData *td;
1851 for (i = 0, td = tc->data; i < tc->data_len; i++, td++) {
1852 if (td->factor == 0.0f) {
1853 continue;
1854 }
1855 const BMVert *v = (BMVert *)td->extra;
1856 const int v_index = BM_elem_index_get(v);
1857 BLI_assert(!verts_mask[v_index]);
1858 verts_mask[v_index].set();
1859 verts_mask_count += 1;
1860 }
1861
1862 TransDataMirror *td_mirror = tc->data_mirror;
1863 for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) {
1864 BMVert *v_mirr = (BMVert *)POINTER_OFFSET(td_mirror->loc_src, -offsetof(BMVert, co));
1865 if (!verts_mask[BM_elem_index_get(v_mirr)] && equals_v3v3(td_mirror->loc, td_mirror->iloc)) {
1866 continue;
1867 }
1868
1869 BMVert *v_mirr_other = (BMVert *)td_mirror->extra;
1870 BLI_assert(!verts_mask[BM_elem_index_get(v_mirr_other)]);
1871 const int v_mirr_other_index = BM_elem_index_get(v_mirr_other);
1872 verts_mask[v_mirr_other_index].set();
1873 verts_mask_count += 1;
1874 }
1875 }
1876
1877 switch (partial_type) {
1878 case PARTIAL_TYPE_ALL: {
1880 params.do_tessellate = true;
1881 params.do_normals = true;
1883 *em->bm, params, verts_mask, verts_mask_count);
1884 break;
1885 }
1886 case PARTIAL_TYPE_GROUP: {
1888 params.do_tessellate = true;
1889 params.do_normals = true;
1890 pupdate->cache = (!verts_group.is_empty() ?
1892 *em->bm, params, verts_group, verts_group_count) :
1894 *em->bm, params, verts_mask, verts_mask_count));
1895 break;
1896 }
1897 case PARTIAL_NONE: {
1899 }
1900 }
1901
1902 pupdate->prop_size_prev = t->prop_size;
1903 pupdate->prop_size = t->prop_size;
1904
1905 return pupdate->cache;
1906}
1907
1908static void mesh_partial_types_calc(TransInfo *t, PartialTypeState *r_partial_state)
1909{
1910 /* Calculate the kind of partial updates which can be performed. */
1911 enum ePartialType partial_for_normals = PARTIAL_NONE;
1912 enum ePartialType partial_for_looptris = PARTIAL_NONE;
1913
1914 /* Note that operations such as #TFM_CREASE are not handled here
1915 * (if they were, leaving as #PARTIAL_NONE would be appropriate). */
1916 switch (t->mode) {
1917 case TFM_TRANSLATION: {
1918 partial_for_looptris = PARTIAL_TYPE_GROUP;
1919 partial_for_normals = PARTIAL_TYPE_GROUP;
1920 /* Translation can rotate when snapping to normal. */
1922 partial_for_normals = PARTIAL_TYPE_ALL;
1923 }
1924 break;
1925 }
1926 case TFM_ROTATION: {
1927 partial_for_looptris = PARTIAL_TYPE_GROUP;
1928 partial_for_normals = PARTIAL_TYPE_ALL;
1929 break;
1930 }
1931 case TFM_RESIZE: {
1932 partial_for_looptris = PARTIAL_TYPE_GROUP;
1933 partial_for_normals = PARTIAL_TYPE_GROUP;
1934 /* Non-uniform scale needs to recalculate all normals
1935 * since their relative locations change.
1936 * Uniform negative scale can keep normals as-is since the faces are flipped,
1937 * normals remain unchanged. */
1938 if ((t->con.mode & CON_APPLY) ||
1939 (t->values_final[0] != t->values_final[1] || t->values_final[0] != t->values_final[2]))
1940 {
1941 partial_for_normals = PARTIAL_TYPE_ALL;
1942 }
1943 break;
1944 }
1945 default: {
1946 partial_for_looptris = PARTIAL_TYPE_ALL;
1947 partial_for_normals = PARTIAL_TYPE_ALL;
1948 break;
1949 }
1950 }
1951
1952 /* With projection, transform isn't affine. */
1954 if (partial_for_looptris == PARTIAL_TYPE_GROUP) {
1955 partial_for_looptris = PARTIAL_TYPE_ALL;
1956 }
1957 if (partial_for_normals == PARTIAL_TYPE_GROUP) {
1958 partial_for_normals = PARTIAL_TYPE_ALL;
1959 }
1960 }
1961
1962 r_partial_state->for_looptris = partial_for_looptris;
1963 r_partial_state->for_normals = partial_for_normals;
1964}
1965
1968 const PartialTypeState *partial_state)
1969{
1971
1973
1974 const PartialTypeState *partial_state_prev = &tcmd->partial_update_state_prev;
1975
1976 /* Promote the partial update types based on the previous state
1977 * so the values that no longer modified are reset before being left as-is.
1978 * Needed for translation which can toggle snap-to-normal during transform. */
1979 const enum ePartialType partial_for_looptris = std::max(partial_state->for_looptris,
1980 partial_state_prev->for_looptris);
1981 const enum ePartialType partial_for_normals = std::max(partial_state->for_normals,
1982 partial_state_prev->for_normals);
1983
1984 if ((partial_for_looptris == PARTIAL_TYPE_ALL) && (partial_for_normals == PARTIAL_TYPE_ALL) &&
1985 (em->bm->totvert == em->bm->totvertsel))
1986 {
1987 /* The additional cost of generating the partial connectivity data isn't justified
1988 * when all data needs to be updated.
1989 *
1990 * While proportional editing can cause all geometry to need updating with a partial
1991 * selection. It's impractical to calculate this ahead of time. Further, the down side of
1992 * using partial updates when their not needed is negligible. */
1994 }
1995 else {
1996 if (partial_for_looptris != PARTIAL_NONE) {
1997 BMPartialUpdate *bmpinfo = mesh_partial_ensure(t, tc, partial_for_looptris);
1999 params.face_normals = true;
2001 }
2002
2003 if (partial_for_normals != PARTIAL_NONE) {
2004 BMPartialUpdate *bmpinfo = mesh_partial_ensure(t, tc, partial_for_normals);
2005 /* While not a large difference, take advantage of existing normals where possible. */
2006 const bool face_normals = !((partial_for_looptris == PARTIAL_TYPE_ALL) ||
2007 ((partial_for_looptris == PARTIAL_TYPE_GROUP) &&
2008 (partial_for_normals == PARTIAL_TYPE_GROUP)));
2010 params.face_normals = face_normals;
2012 }
2013 }
2014
2015 /* Store the previous requested (not the previous used),
2016 * since the values used may have been promoted based on the previous types. */
2017 tcmd->partial_update_state_prev = *partial_state;
2018}
2019
2021
2022/* -------------------------------------------------------------------- */
2025
2027{
2028 if (tc->use_mirror_axis_any) {
2029 int i;
2030 TransData *td;
2031 for (i = 0, td = tc->data; i < tc->data_len; i++, td++) {
2033 if (td->flag & TD_MIRROR_EDGE_X) {
2034 td->loc[0] = 0.0f;
2035 }
2036 if (td->flag & TD_MIRROR_EDGE_Y) {
2037 td->loc[1] = 0.0f;
2038 }
2039 if (td->flag & TD_MIRROR_EDGE_Z) {
2040 td->loc[2] = 0.0f;
2041 }
2042 }
2043 }
2044
2045 TransDataMirror *td_mirror = tc->data_mirror;
2046 for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) {
2047 copy_v3_v3(td_mirror->loc, td_mirror->loc_src);
2048 if (td_mirror->flag & TD_MIRROR_X) {
2049 td_mirror->loc[0] *= -1;
2050 }
2051 if (td_mirror->flag & TD_MIRROR_Y) {
2052 td_mirror->loc[1] *= -1;
2053 }
2054 if (td_mirror->flag & TD_MIRROR_Z) {
2055 td_mirror->loc[2] *= -1;
2056 }
2057 }
2058 }
2059}
2060
2062{
2063 if (t->mode == TFM_NORMAL_ROTATION) {
2065 /* The Rotate Normal mode uses a custom array and ignores any elements created for the mesh
2066 * in transData and similar structures. */
2067 DEG_id_tag_update(static_cast<ID *>(tc->obedit->data), ID_RECALC_GEOMETRY);
2068 }
2069 return;
2070 }
2071
2072 bool is_canceling = t->state == TRANS_CANCEL;
2073 /* Apply corrections. */
2074 if (!is_canceling) {
2076
2077 bool do_mirror = !(t->flag & T_NO_MIRROR);
2079 /* Apply clipping after so we never project past the clip plane #25423. */
2081
2082 if (do_mirror) {
2084 }
2085
2087 }
2088 }
2089 else {
2091 }
2092
2093 PartialTypeState partial_state;
2094 mesh_partial_types_calc(t, &partial_state);
2095
2097 DEG_id_tag_update(static_cast<ID *>(tc->obedit->data), ID_RECALC_GEOMETRY);
2098
2099 mesh_partial_update(t, tc, &partial_state);
2100 }
2101}
2102
2104
2105/* -------------------------------------------------------------------- */
2108
2110{
2111 const bool is_canceling = (t->state == TRANS_CANCEL);
2112 const bool use_automerge = !is_canceling && (t->flag & (T_AUTOMERGE | T_AUTOSPLIT)) != 0;
2113
2114 if (!is_canceling && ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
2115 /* NOTE(joeedh): Handle multi-res re-projection,
2116 * done on transform completion since it's really slow. */
2119 }
2120 }
2121
2122 if (use_automerge) {
2124 BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
2125 BMesh *bm = em->bm;
2126 char hflag;
2127 bool has_face_sel = (bm->totfacesel != 0);
2128
2129 if (tc->use_mirror_axis_any) {
2130 /* Rather than adjusting the selection (which the user would notice)
2131 * tag all mirrored verts, then auto-merge those. */
2133
2134 TransDataMirror *td_mirror = tc->data_mirror;
2135 for (int i = tc->data_mirror_len; i--; td_mirror++) {
2137 }
2138
2139 hflag = BM_ELEM_SELECT | BM_ELEM_TAG;
2140 }
2141 else {
2142 hflag = BM_ELEM_SELECT;
2143 }
2144
2145 if (t->flag & T_AUTOSPLIT) {
2147 tc->obedit, true, true, true, hflag, t->scene->toolsettings->doublimit);
2148 }
2149 else {
2150 EDBM_automerge(tc->obedit, true, hflag, t->scene->toolsettings->doublimit);
2151 }
2152
2153 /* Special case, this is needed or faces won't re-select.
2154 * Flush selected edges to faces. */
2155 if (has_face_sel && (em->selectmode == SCE_SELECT_FACE)) {
2157 }
2158 }
2159 }
2160
2162 /* Table needs to be created for each edit command, since vertices can move etc. */
2164 /* TODO(@ideasman42): xform: We need support for many mirror objects at once! */
2165 break;
2166 }
2167}
2168
2170
2171/* -------------------------------------------------------------------- */
2174
2176 const TransDataContainer *tc, Vector<float3> &r_loc_dst_buffer)
2177{
2178 int td_selected_len = 0;
2179 tc->foreach_index_selected([&](const int /*i*/) { td_selected_len++; });
2180
2181 Array<TransDataVertSlideVert> sv_array(td_selected_len);
2182
2183 r_loc_dst_buffer.reserve(sv_array.size() * 4);
2184 int sv_array_index = 0;
2185 tc->foreach_index_selected([&](const int i) {
2186 TransData *td = &tc->data[i];
2187 const int size_prev = r_loc_dst_buffer.size();
2188
2189 BMVert *v = static_cast<BMVert *>(td->extra);
2190 if (!v->e) {
2191 r_loc_dst_buffer.append(td->iloc);
2192 }
2193 else {
2194 BMIter eiter;
2195 BMEdge *e;
2196 BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
2198 continue;
2199 }
2200 BMVert *v_other = BM_edge_other_vert(e, v);
2201 r_loc_dst_buffer.append(v_other->co);
2202 }
2203 }
2204
2205 TransDataVertSlideVert &sv = sv_array[sv_array_index];
2206 sv.td = &tc->data[i];
2207 /* The buffer address may change as the vector is resized. Avoid setting #Span. */
2208 // sv.targets = r_loc_dst_buffer.as_span().drop_front(size_prev);
2209
2210 /* Store the buffer size temporarily in `target_curr`. */
2211 sv.co_link_curr = r_loc_dst_buffer.size() - size_prev;
2212
2213 sv_array_index++;
2214 });
2215
2216 int start = 0;
2217 for (TransDataVertSlideVert &sv : sv_array) {
2218 int size = sv.co_link_curr;
2219 sv.co_link_orig_3d = r_loc_dst_buffer.as_span().slice(start, size);
2220 sv.co_link_curr = 0;
2221 start += size;
2222 }
2223
2224 return sv_array;
2225}
2226
2228
2229/* -------------------------------------------------------------------- */
2232
2234{
2236}
2237
2242static bool bm_loop_calc_opposite_co(const BMLoop *l_tmp, const float plane_no[3], float r_co[3])
2243{
2244 /* Skip adjacent edges. */
2245 BMLoop *l_first = l_tmp->next;
2246 BMLoop *l_last = l_tmp->prev;
2247 BMLoop *l_iter;
2248 float dist_sq_best = FLT_MAX;
2249 bool found = false;
2250
2251 l_iter = l_first;
2252 do {
2253 float tvec[3];
2254 if (isect_line_plane_v3(tvec, l_iter->v->co, l_iter->next->v->co, l_tmp->v->co, plane_no)) {
2255 const float fac = line_point_factor_v3(tvec, l_iter->v->co, l_iter->next->v->co);
2256 /* Allow some overlap to avoid missing the intersection because of float precision. */
2257 if ((fac > -FLT_EPSILON) && (fac < 1.0f + FLT_EPSILON)) {
2258 /* Likelihood of multiple intersections per ngon is quite low,
2259 * it would have to loop back on itself, but better support it
2260 * so check for the closest opposite edge. */
2261 const float dist_sq_test = len_squared_v3v3(l_tmp->v->co, tvec);
2262 if (dist_sq_test < dist_sq_best) {
2263 copy_v3_v3(r_co, tvec);
2264 dist_sq_best = dist_sq_test;
2265 found = true;
2266 }
2267 }
2268 }
2269 } while ((l_iter = l_iter->next) != l_last);
2270
2271 return found;
2272}
2273
2275{
2276 BMFace *f = l->f;
2277 BMLoop *l_next = l->next;
2278 if (f->len == 4) {
2279 /* We could use code below, but in this case
2280 * sliding diagonally across the quad works well. */
2281 return l_next->next->v->co;
2282 }
2283
2284 float3 plane_no;
2285 BM_loop_calc_face_direction(l, plane_no);
2286
2287 float3 isect_co;
2288 if (!bm_loop_calc_opposite_co(l, plane_no, isect_co)) {
2289 /* Rare case. */
2290 mid_v3_v3v3(isect_co, l->prev->v->co, l_next->v->co);
2291 }
2292 return isect_co;
2293}
2294
2296 int *r_group_len)
2297{
2299 BMesh *bm = em->bm;
2300
2301 int td_selected_len = 0;
2302
2303 /* Ensure valid selection. */
2304 BMIter iter;
2305 BMVert *v;
2306 bool found_invalid_edge_selection = false;
2307 tc->foreach_index_selected([&](const int i) {
2308 TransData *td = &tc->data[i];
2309 v = static_cast<BMVert *>(td->extra);
2311 if (numsel == 0 || numsel > 2) {
2312 /* Invalid edge selection. */
2313 found_invalid_edge_selection = true;
2314 return;
2315 }
2316 td_selected_len++;
2317 });
2318
2319 if (found_invalid_edge_selection) {
2320 return {};
2321 }
2322
2323 BMEdge *e;
2324 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
2326 continue;
2327 }
2329 /* Can edges with at least once face user. */
2330 return {};
2331 }
2332 }
2333
2334 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
2335 BM_elem_index_set(v, -1);
2336 }
2337 bm->elem_index_dirty |= BM_VERT;
2338
2339 /* Alloc and initialize the #TransDataEdgeSlideVert. */
2340
2341 Array<TransDataEdgeSlideVert> sv_array(td_selected_len);
2342 TransDataEdgeSlideVert *sv = sv_array.data();
2343 int sv_index = 0;
2344 tc->foreach_index_selected([&](const int i) {
2345 TransData *td = &tc->data[i];
2346
2347 sv->td = td;
2348 sv->loop_nr = -1;
2349 sv->dir_side[0] = float3(0);
2350 sv->dir_side[1] = float3(0);
2351
2352 /* Identify the #TransDataEdgeSlideVert by the vertex index. */
2353 v = static_cast<BMVert *>(td->extra);
2354 BM_elem_index_set(v, sv_index);
2355 sv_index++;
2356 sv++;
2357 });
2358
2359 /* Map indicating the indexes of #TransData connected by edge. */
2360 Array<int2> td_connected(tc->data_len, int2(-1, -1));
2361 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
2363 continue;
2364 }
2365 int td_index_1 = BM_elem_index_get(e->v1);
2366 int td_index_2 = BM_elem_index_get(e->v2);
2367
2368 /* This can occur when the mesh has symmetry enabled but is not symmetrical. See #120811. */
2369 if (ELEM(-1, td_index_1, td_index_2)) {
2370 continue;
2371 }
2372
2373 int slot_1 = int(td_connected[td_index_1][0] != -1);
2374 int slot_2 = int(td_connected[td_index_2][0] != -1);
2375
2376 td_connected[td_index_1][slot_1] = td_index_2;
2377 td_connected[td_index_2][slot_2] = td_index_1;
2378 }
2379
2380 /* Compute the sliding groups. */
2381 int loop_nr = 0;
2382 for (int i : sv_array.index_range()) {
2383 TransDataEdgeSlideVert *sv = &sv_array[i];
2384 if (sv->loop_nr != -1) {
2385 /* This vertex has already been computed. */
2386 continue;
2387 }
2388
2389 /* Start from a vertex connected to just a single edge or any if it doesn't exist. */
2390 int i_curr = i;
2391 int i_prev = td_connected[i][1];
2392 while (!ELEM(i_prev, -1, i)) {
2393 int tmp = td_connected[i_prev][0] != i_curr ? td_connected[i_prev][0] :
2394 td_connected[i_prev][1];
2395 i_curr = i_prev;
2396 i_prev = tmp;
2397 }
2398
2408 struct SlideTempDataMesh {
2409 int i; /* The #TransDataEdgeSlideVert index. */
2411 BMVert *v;
2412 BMEdge *e;
2413 struct {
2414 BMFace *f;
2415 BMVert *v_dst;
2416 float3 dst;
2417 } fdata[2];
2418 bool vert_is_edge_pair;
2427 int find_best_dir(const SlideTempDataMesh *curr_side_other,
2428 const BMFace *f_curr,
2429 const BMLoop *l_src,
2430 const BMVert *v_dst,
2431 bool *r_do_isect_curr_dirs) const
2432 {
2433 *r_do_isect_curr_dirs = false;
2434
2435 if (f_curr == curr_side_other->fdata[0].f || v_dst == curr_side_other->fdata[0].v_dst) {
2436 return 0;
2437 }
2438
2439 if (f_curr == curr_side_other->fdata[1].f || v_dst == curr_side_other->fdata[1].v_dst) {
2440 return 1;
2441 }
2442
2443 if (curr_side_other->fdata[0].f || curr_side_other->fdata[1].f) {
2444 /* Find the best direction checking the edges that share faces between them. */
2445 int best_dir = -1;
2446 const BMLoop *l_edge = l_src->next->v == v_dst ? l_src : l_src->prev;
2447 const BMLoop *l_other = l_edge->radial_next;
2448 while (l_other->f != l_edge->f) {
2449 if (l_other->f == curr_side_other->fdata[0].f) {
2450 best_dir = 0;
2451 break;
2452 }
2453 if (l_other->f == curr_side_other->fdata[1].f) {
2454 best_dir = 1;
2455 break;
2456 }
2457 l_other = (l_other->v == this->v ? l_other->prev : l_other->next)->radial_next;
2458 }
2459
2460 if (best_dir != -1) {
2461 *r_do_isect_curr_dirs = true;
2462 return best_dir;
2463 }
2464 }
2465
2466 if (ELEM(nullptr, this->fdata[0].f, this->fdata[1].f)) {
2467 return int(this->fdata[0].f != nullptr);
2468 }
2469
2470 /* Find the best direction among those already computed.
2471 * Prioritizing in order:
2472 * - Boundary edge that points to the closest direction.
2473 * - Any edge that points to the closest direction. */
2474
2475 *r_do_isect_curr_dirs = true;
2476 BMEdge *e0 = this->fdata[0].v_dst ? BM_edge_exists(this->v, this->fdata[0].v_dst) :
2477 nullptr;
2478 BMEdge *e1 = this->fdata[1].v_dst ? BM_edge_exists(this->v, this->fdata[1].v_dst) :
2479 nullptr;
2480 const bool is_boundary_0 = e0 && BM_edge_is_boundary(e0);
2481 const bool is_boundary_1 = e1 && BM_edge_is_boundary(e1);
2482 if (is_boundary_0 && !is_boundary_1) {
2483 return 0;
2484 }
2485
2486 if (is_boundary_1 && !is_boundary_0) {
2487 return 1;
2488 }
2489
2490 /* Find the closest direction. */
2491 float3 src = this->v->co;
2492 float3 dst = v_dst->co;
2493 float3 dir_curr = dst - src;
2494 float3 dir0 = math::normalize(this->fdata[0].dst - src);
2495 float3 dir1 = math::normalize(this->fdata[1].dst - src);
2496 float dot0 = math::dot(dir_curr, dir0);
2497 float dot1 = math::dot(dir_curr, dir1);
2498 return int(dot0 < dot1);
2499 }
2500 } prev = {}, curr = {}, next = {}, next_next = {}, tmp = {};
2501
2502 next.i = td_connected[i_curr][0] != i_prev ? td_connected[i_curr][0] : td_connected[i_curr][1];
2503 if (next.i != -1) {
2504 next.sv = &sv_array[next.i];
2505 next.v = static_cast<BMVert *>(next.sv->td->extra);
2506 next.vert_is_edge_pair = mesh_vert_is_inner(next.v);
2507 }
2508
2509 curr.i = i_curr;
2510 if (curr.i != -1) {
2511 curr.sv = &sv_array[curr.i];
2512 curr.v = static_cast<BMVert *>(curr.sv->td->extra);
2513 curr.vert_is_edge_pair = mesh_vert_is_inner(curr.v);
2514 if (next.i != -1) {
2515 curr.e = BM_edge_exists(curr.v, next.v);
2516 }
2517 }
2518
2519 /* Do not compute `prev` for now. Let the loop calculate `curr` twice. */
2520 prev.i = -1;
2521
2522 while (curr.i != -1) {
2523 if (next.i != -1) {
2524 next_next.i = td_connected[next.i][0] != curr.i ? td_connected[next.i][0] :
2525 td_connected[next.i][1];
2526 if (next_next.i != -1) {
2527 next_next.sv = &sv_array[next_next.i];
2528 next_next.v = static_cast<BMVert *>(next_next.sv->td->extra);
2529 next_next.vert_is_edge_pair = mesh_vert_is_inner(next_next.v);
2530 next.e = BM_edge_exists(next.v, next_next.v);
2531 }
2532
2533 tmp = curr;
2534
2535 BMLoop *l;
2536 BM_ITER_ELEM (l, &iter, curr.e, BM_LOOPS_OF_EDGE) {
2537 BMFace *f_curr = l->f;
2538
2539 BMVert *v1_dst, *v2_dst;
2540 BMEdge *l_edge_next;
2541 BMLoop *l1, *l2;
2542 if (l->v == curr.v) {
2543 l1 = l;
2544 l2 = l->next;
2545 l_edge_next = l2->e;
2546 v1_dst = l1->prev->v;
2547 v2_dst = l2->next->v;
2548 }
2549 else {
2550 l1 = l->next;
2551 l2 = l;
2552 l_edge_next = l2->prev->e;
2553 v1_dst = l1->next->v;
2554 v2_dst = l2->prev->v;
2555 }
2556
2557 float3 dst = v1_dst->co;
2558
2559 /* Sometimes the sliding direction may fork (`isect_curr_dirs` is `true`).
2560 * In this case, the resulting direction is the intersection of the destinations. */
2561 bool isect_curr_dirs = false;
2562
2563 /* Identify the slot to slide according to the directions already computed in `curr`. */
2564 int best_dir = curr.find_best_dir(&tmp, f_curr, l1, v1_dst, &isect_curr_dirs);
2565
2566 if (curr.fdata[best_dir].f == nullptr) {
2567 curr.fdata[best_dir].f = f_curr;
2568 if (curr.vert_is_edge_pair) {
2569 curr.fdata[best_dir].dst = isect_face_dst(l1);
2570 }
2571 else {
2572 curr.fdata[best_dir].v_dst = v1_dst;
2573 curr.fdata[best_dir].dst = v1_dst->co;
2574 }
2575 }
2576
2577 /* Compute `next`. */
2578 next.fdata[best_dir].f = f_curr;
2579 if (l_edge_next == next.e || next.vert_is_edge_pair) {
2580 /* Case where the vertex slides over the face. */
2581 next.fdata[best_dir].v_dst = nullptr;
2582 next.fdata[best_dir].dst = isect_face_dst(l2);
2583 }
2584 else {
2585 /* Case where the vertex slides over an edge. */
2586 next.fdata[best_dir].v_dst = v2_dst;
2587 next.fdata[best_dir].dst = v2_dst->co;
2588 }
2589
2590 if (isect_curr_dirs) {
2591 /* The `best_dir` can only have one direction. */
2592 const float *curr_orig = curr.sv->v_co_orig();
2593 const float3 &dst0 = prev.fdata[best_dir].dst;
2594 const float3 &dst1 = curr.fdata[best_dir].dst;
2595 const float3 &dst2 = dst;
2596 const float3 &dst3 = next.fdata[best_dir].dst;
2597 float3 isect_pair[2];
2598
2664
2665 const float isect_eps = FLT_EPSILON;
2666 int isect_line_line = isect_line_line_epsilon_v3(
2667 dst0, dst1, dst2, dst3, isect_pair[0], isect_pair[1], isect_eps);
2668
2669 if (isect_line_line != 0) {
2670 /* Check if the intersections are outside the "valid conical region". */
2671 BLI_assert(isect_line_line <= 2);
2672 const float3 dir1 = math::normalize(dst1 - float3(curr_orig));
2673 const float3 dir2 = math::normalize(dst2 - float3(curr_orig));
2674 float len_n;
2675 const float3 n = math::normalize_and_get_length(math::cross(dir1, dir2), len_n);
2676 if (UNLIKELY(len_n < isect_eps)) {
2677 isect_line_line = 0;
2678 }
2679 else {
2680 float len1, len2;
2681 const float3 plane_no_1 = math::normalize_and_get_length(math::cross(n, dir1),
2682 len1);
2683 const float3 plane_no_2 = math::normalize_and_get_length(math::cross(dir2, n),
2684 len2);
2685
2686 if (UNLIKELY((len1 < isect_eps) || (len2 < isect_eps))) {
2687 isect_line_line = 0;
2688 }
2689 else {
2690 for (int isect_pass = 0; isect_pass < isect_line_line; isect_pass++) {
2691 const float3 isect_co = isect_pair[isect_pass] - float3(curr_orig);
2692 if ((math::dot(isect_co, plane_no_1) <= 0.0f) ||
2693 (math::dot(isect_co, plane_no_2) <= 0.0f))
2694 {
2695 /* Outside the plane, ignore. */
2696 isect_line_line = 0;
2697 break;
2698 }
2699 }
2700 }
2701 }
2702 }
2703
2704 if (isect_line_line != 0) {
2705 curr.fdata[best_dir].dst = math::midpoint(isect_pair[0], isect_pair[1]);
2706 }
2707 else {
2708 curr.fdata[best_dir].dst = math::midpoint(dst1, dst2);
2709 }
2710 }
2711 }
2712 }
2713
2714 /* The data in `curr` is computed. Use to compute the #TransDataEdgeSlideVert. */
2715 float3 iloc = curr.sv->td->iloc;
2716 if (curr.fdata[0].f) {
2717 curr.sv->dir_side[0] = curr.fdata[0].dst - iloc;
2718 }
2719 if (curr.fdata[1].f) {
2720 curr.sv->dir_side[1] = curr.fdata[1].dst - iloc;
2721 }
2722 curr.sv->edge_len = math::distance(curr.sv->dir_side[0], curr.sv->dir_side[1]);
2723 curr.sv->loop_nr = loop_nr;
2724
2725 if (i_prev != -1 && prev.i == i_prev) {
2726 /* Cycle returned to the beginning.
2727 * The data with index `i_curr` was computed twice to make sure the directions are correct
2728 * the second time. */
2729 break;
2730 }
2731
2732 /* Move forward. */
2733 prev = curr;
2734 curr = next;
2735 next = next_next;
2736 }
2737 loop_nr++;
2738 }
2739 *r_group_len = loop_nr;
2740 return sv_array;
2741}
2742
2744
2746 /*flags*/ (T_EDIT | T_POINTS),
2747 /*create_trans_data*/ createTransEditVerts,
2748 /*recalc_data*/ recalcData_mesh,
2749 /*special_aftertrans_update*/ special_aftertrans_update__mesh,
2750};
2751
2752} // namespace blender::ed::transform
Main * CTX_data_main(const bContext *C)
blender::Array< blender::float3 > BKE_crazyspace_get_mapped_editverts(Depsgraph *depsgraph, Object *obedit)
Definition crazyspace.cc:91
int BKE_crazyspace_get_first_deform_matrices_editbmesh(Depsgraph *depsgraph, Scene *, Object *, BMEditMesh *em, blender::Array< blender::float3x3, 0 > &deformmats, blender::Array< blender::float3, 0 > &deformcos)
void BKE_crazyspace_set_quats_editmesh(BMEditMesh *em, blender::Span< blender::float3 > origcos, blender::Span< blender::float3 > mappedcos, float(*quats)[4], bool use_select)
int CustomData_get_offset(const CustomData *data, eCustomDataType type)
BMCustomDataCopyMap CustomData_bmesh_copy_map_calc(const CustomData &src, const CustomData &dst, eCustomDataMask mask_exclude=0)
bool CustomData_layer_has_math(const CustomData *data, int layer_n)
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
bool CustomData_has_math(const CustomData *data)
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:61
void BKE_editmesh_looptris_calc_with_partial_ex(BMEditMesh *em, BMPartialUpdate *bmpinfo, const BMeshCalcTessellation_Params *params)
Definition editmesh.cc:105
void BKE_editmesh_looptris_calc(BMEditMesh *em)
Definition editmesh.cc:88
void BKE_editmesh_looptris_and_normals_calc(BMEditMesh *em)
Definition editmesh.cc:95
int BKE_modifiers_get_cage_index(const Scene *scene, Object *ob, int *r_lastPossibleCageIndex, bool is_virtual)
bool BKE_modifiers_is_correctable_deformed(const Scene *scene, Object *ob)
void BKE_scene_graph_evaluated_ensure(Depsgraph *depsgraph, Main *bmain)
Definition scene.cc:2626
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:18
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
BLI_INLINE void * BLI_ghashIterator_getKey(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.h:295
BLI_INLINE void * BLI_ghashIterator_getValue(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.h:299
#define GHASH_ITER(gh_iter_, ghash_)
Definition BLI_ghash.h:318
GHash * BLI_ghash_ptr_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
GHash * BLI_ghash_ptr_new_ex(const char *info, unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:731
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition BLI_ghash.cc:707
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.cc:860
bool BLI_ghash_ensure_p(GHash *gh, void *key, void ***r_val) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:752
float geodesic_distance_propagate_across_triangle(const float v0[3], const float v1[3], const float v2[3], float dist1, float dist2)
int isect_line_line_epsilon_v3(const float v1[3], const float v2[3], const float v3[3], const float v4[3], float r_i1[3], float r_i2[3], float epsilon)
void axis_dominant_v3_to_m3(float r_mat[3][3], const float normal[3])
Normal to x,y matrix.
MINLINE int poly_to_tri_count(int poly_count, int corner_count)
float line_point_factor_v3(const float p[3], const float l1[3], const float l2[3])
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
float dist_signed_squared_to_corner_v3v3v3(const float p[3], const float v1[3], const float v2[3], const float v3[3], const float axis_ref[3])
Definition math_geom.cc:546
void mul_m3_v3(const float M[3][3], float r[3])
void pseudoinverse_m3_m3(float inverse[3][3], const float mat[3][3], float epsilon)
void copy_m3_m3(float m1[3][3], const float m2[3][3])
void unit_m3(float m[3][3])
void copy_m3_m4(float m1[3][3], const float m2[4][4])
#define PSEUDOINVERSE_EPSILON
bool invert_m3_m3(float inverse[3][3], const float mat[3][3])
void mul_v3_m3v3(float r[3], const float M[3][3], const float a[3])
#define mul_m3_series(...)
void mul_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3])
bool invert_m3(float mat[3][3])
void quat_to_mat3(float m[3][3], const float q[4])
MINLINE bool equals_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
void project_plane_normalized_v3_v3v3(float out[3], const float p[3], const float v_plane[3])
void copy_vn_i(int *array_tar, int size, int val)
MINLINE bool is_zero_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void zero_v3(float r[3])
float angle_v3v3v3(const float a[3], const float b[3], const float c[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
#define BLI_MEMARENA_STD_BUFSIZE
MemArena * BLI_memarena_new(size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC
void BLI_memarena_free(MemArena *ma) ATTR_NONNULL(1)
void * BLI_memarena_alloc(MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2)
unsigned int uint
#define ARRAY_SIZE(arr)
#define UNPACK3(a)
#define UNLIKELY(x)
#define ELEM(...)
#define POINTER_OFFSET(v, ofs)
void DEG_id_tag_update(ID *id, unsigned int flags)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ ME_EDIT_MIRROR_TOPO
@ UVCALC_TRANSFORM_CORRECT_SLIDE
@ UVCALC_TRANSFORM_CORRECT
@ UVCALC_TRANSFORM_CORRECT_KEEP_CONNECTED
@ SCE_SNAP_ROTATE
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
@ V3D_AROUND_LOCAL_ORIGINS
bool EDBM_automerge_and_split(Object *obedit, bool split_edges, bool split_faces, bool update, char hflag, float dist)
bool EDBM_automerge(Object *obedit, bool update, char hflag, float dist)
void EDBM_selectmode_flush_ex(BMEditMesh *em, short selectmode)
void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em, int axis, bool use_self, bool use_select, bool respecthide, bool use_topology, float maxdist, int *r_index)
void ED_mesh_mirror_spatial_table_end(Object *ob)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define MEM_reallocN(vmemh, len)
#define BM_FACE_FIRST_LOOP(p)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SELECT
@ BM_ELEM_TAG
@ BM_ELEM_TAG_ALT
@ BM_LOOP
void BM_elem_attrs_copy(BMesh *bm, const BMCustomDataCopyMap &map, const BMVert *src, BMVert *dst)
void BM_mesh_copy_init_customdata_all_layers(BMesh *bm_dst, BMesh *bm_src, const char htype, const BMAllocTemplate *allocsize)
BMFace * BM_face_copy(BMesh *bm_dst, const BMCustomDataCopyMap &cd_face_map, const BMCustomDataCopyMap &cd_loop_map, BMFace *f, const bool copy_verts, const bool copy_edges)
#define BM_elem_index_get(ele)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_flag_set(ele, hflag, val)
#define BM_elem_index_set(ele, index)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
void BM_face_interp_multires_ex(BMesh *bm, BMFace *f_dst, const BMFace *f_src, const float f_dst_center[3], const float f_src_center[3], const int cd_loop_mdisp_offset)
void BM_vert_loop_groups_data_layer_merge(BMesh *bm, LinkNode *groups, const int layer_n)
void BM_vert_loop_groups_data_layer_merge_weights(BMesh *bm, LinkNode *groups, const int layer_n, const float *loop_weights)
void BM_loop_interp_from_face(BMesh *bm, BMLoop *l_dst, const BMFace *f_src, const bool do_vertex, const bool do_multires)
LinkNode * BM_vert_loop_groups_data_layer_create(BMesh *bm, BMVert *v, const int layer_n, const float *loop_weights, MemArena *arena)
int BM_iter_mesh_count_flag(const char itype, BMesh *bm, const char hflag, const bool value)
Mesh Iter Flag Count.
int BM_iter_elem_count_flag(const char itype, void *data, const char hflag, const bool value)
Elem Iter Flag Count.
#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)
#define BM_iter_init(iter, bm, itype, data)
@ BM_FACES_OF_EDGE
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_VERTS_OF_EDGE
@ BM_VERTS_OF_FACE
@ BM_LOOPS_OF_VERT
@ BM_LOOPS_OF_EDGE
@ BM_EDGES_OF_VERT
@ BM_LOOPS_OF_FACE
#define BM_ITER_ELEM_INDEX(ele, iter, data, itype, indexvar)
BMesh const char void * data
BMesh * bm
BMesh const char itype
void BM_editselection_center(BMEditSelection *ese, float r_center[3])
void BM_editselection_plane(BMEditSelection *ese, float r_plane[3])
void BM_editselection_normal(BMEditSelection *ese, float r_normal[3])
void BM_mesh_elem_hflag_disable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
const BMAllocTemplate bm_mesh_allocsize_default
Definition bmesh_mesh.cc:30
void bmesh_edit_begin(BMesh *, BMOpTypeFlag)
BMesh Begin Edit.
void BM_mesh_free(BMesh *bm)
BMesh Free Mesh.
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
void bmesh_edit_end(BMesh *bm, BMOpTypeFlag type_flag)
BMesh End Edit.
BMesh * BM_mesh_create(const BMAllocTemplate *allocsize, const BMeshCreateParams *params)
BMesh Make Mesh.
BLI_INLINE BMVert * BM_vert_at_index(BMesh *bm, const int index)
void BM_mesh_normals_update_with_partial_ex(BMesh *, const BMPartialUpdate *bmpinfo, const BMeshNormalsUpdate_Params *params)
BMPartialUpdate * BM_mesh_partial_create_from_verts_group_single(BMesh &bm, const BMPartialUpdate_Params &params, const BitSpan verts_mask, const int verts_mask_count)
BMPartialUpdate * BM_mesh_partial_create_from_verts_group_multi(BMesh &bm, const BMPartialUpdate_Params &params, const Span< int > verts_group, const int verts_group_count)
void BM_mesh_partial_destroy(BMPartialUpdate *bmpinfo)
BMPartialUpdate * BM_mesh_partial_create_from_verts(BMesh &bm, const BMPartialUpdate_Params &params, const BitSpan verts_mask, const int verts_mask_count)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
@ BMO_OPTYPE_FLAG_UNTAN_MULTIRES
bool BM_vert_calc_normal_ex(const BMVert *v, const char hflag, float r_no[3])
void BM_face_calc_center_median(const BMFace *f, float r_cent[3])
int BM_mesh_calc_face_groups(BMesh *bm, int *r_groups_array, int(**r_group_index)[2], BMLoopFilterFunc filter_fn, BMLoopPairFilterFunc filter_pair_fn, void *user_data, const char hflag_test, const char htype_step)
float BM_vert_calc_shell_factor_ex(const BMVert *v, const float no[3], const char hflag)
BMLoop * BM_loop_find_next_nodouble(BMLoop *l, BMLoop *l_stop, const float eps_sq)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
void BM_loop_calc_face_direction(const BMLoop *l, float r_dir[3])
BM_loop_calc_face_direction.
int BM_mesh_calc_edge_groups(BMesh *bm, int *r_groups_array, int(**r_group_index)[2], BMVertFilterFunc filter_fn, void *user_data, const char hflag_test)
bool BM_vert_is_edge_pair(const BMVert *v)
bool BM_vert_is_boundary(const BMVert *v)
BMLoop * BM_loop_find_prev_nodouble(BMLoop *l, BMLoop *l_stop, const float eps_sq)
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()
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 BMVert * v
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
int64_t size() const
Definition BLI_array.hh:256
int64_t size() const
Definition BLI_array.hh:256
const T * data() const
Definition BLI_array.hh:312
IndexRange index_range() const
Definition BLI_array.hh:360
bool is_empty() const
Definition BLI_array.hh:264
int64_t size() const
void append(const T &value)
void reserve(const int64_t min_capacity)
Span< T > as_span() const
void resize(const int64_t new_size_in_bits, const bool value=false)
nullptr float
#define offsetof(t, d)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static ulong * next
bool shape_key_report_if_locked(const Object *obedit, ReportList *reports)
Array< TransDataVertSlideVert > transform_mesh_vert_slide_data_create(const TransDataContainer *tc, Vector< float3 > &r_loc_dst_buffer)
void createSpaceNormalTangent_or_fallback(float mat[3][3], const float normal[3], const float tangent[3])
static void mesh_customdatacorrect_init_container_merge_group(TransDataContainer *tc, TransCustomDataLayer *tcld)
static void mesh_customdata_free_fn(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data)
TransConvertTypeInfo TransConvertType_Mesh
static void mesh_customdatacorrect_face_substitute_set(TransCustomDataLayer *tcld, BMFace *f, BMFace *f_copy)
static void mesh_customdatacorrect_free(TransCustomDataLayer *tcld)
static TransCustomDataLayer * mesh_customdatacorrect_create_impl(TransDataContainer *tc, const bool use_merge_group)
static void mesh_customdatacorrect_init_container_generic(TransDataContainer *, TransCustomDataLayer *tcld)
void transform_convert_mesh_mirrordata_free(TransMirrorData *mirror_data)
void transform_convert_mesh_mirrordata_calc(BMEditMesh *em, bool use_select, bool use_topology, const bool mirror_axis[3], TransMirrorData *r_mirror_data)
Array< TransDataEdgeSlideVert > transform_mesh_edge_slide_data_create(const TransDataContainer *tc, int *r_group_len)
void transform_convert_mesh_islanddata_free(TransIslandData *island_data)
static void mesh_transdata_center_copy(const TransIslandData *island_data, const int island_index, const float iloc[3], float r_center[3])
bool validSnappingNormal(const TransInfo *t)
static void mesh_customdatacorrect_create(TransDataContainer *tc, const bool use_merge_group)
void transform_convert_mesh_crazyspace_free(TransMeshDataCrazySpace *r_crazyspace_data)
bool usingSnappingNormal(const TransInfo *t)
static const float * mesh_vert_orig_co_get(TransCustomDataLayer *tcld, BMVert *v)
static bool bm_loop_calc_opposite_co(const BMLoop *l_tmp, const float plane_no[3], float r_co[3])
void transform_snap_project_individual_apply(TransInfo *t)
void transform_convert_mesh_customdatacorrect_init(TransInfo *t)
static void recalcData_mesh(TransInfo *t)
void transform_convert_mesh_crazyspace_detect(TransInfo *t, TransDataContainer *tc, BMEditMesh *em, TransMeshDataCrazySpace *r_crazyspace_data)
static bool bmesh_test_loose_edge(BMEdge *edge)
static bool bmesh_test_dist_add(BMVert *v0, BMVert *v1, BMVert *v2, float *dists, int *index, const float mtx[3][3])
void transform_convert_mesh_crazyspace_transdata_set(const float mtx[3][3], const float smtx[3][3], const float defmat[3][3], const float quat[4], TransData *r_td)
bool transform_snap_project_individual_is_active(const TransInfo *t)
bool createSpaceNormal(float mat[3][3], const float normal[3])
void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc)
static TransCustomDataMesh * mesh_customdata_ensure(TransDataContainer *tc)
static BMPartialUpdate * mesh_partial_ensure(TransInfo *t, TransDataContainer *tc, enum ePartialType partial_type)
static void createTransEditVerts(bContext *, TransInfo *t)
static void mesh_customdatacorrect_apply_vert(TransCustomDataLayer *tcld, TransDataBasic *td, TransCustomDataMergeGroup *merge_data, bool do_loop_mdisps)
static void special_aftertrans_update__mesh(bContext *, TransInfo *t)
static void mesh_customdata_free(TransCustomDataMesh *tcmd)
static void mesh_partial_types_calc(TransInfo *t, PartialTypeState *r_partial_state)
static void mesh_partial_update(TransInfo *t, TransDataContainer *tc, const PartialTypeState *partial_state)
static void mesh_customdatacorrect_apply(TransDataContainer *tc, bool is_final)
bool transform_snap_is_active(const TransInfo *t)
static bool is_in_quadrant_v3(const float co[3], const int quadrant[3], const float epsilon)
static void mesh_customdatacorrect_restore(TransInfo *t)
void transform_convert_mesh_connectivity_distance(BMesh *bm, const float mtx[3][3], float *dists, int *index)
static bool mesh_vert_is_inner(BMVert *v)
static BMFace * mesh_customdatacorrect_face_substitute_get(BMFace *f_copy)
static float3 isect_face_dst(const BMLoop *l)
static BMFace * mesh_customdatacorrect_find_best_face_substitute(BMFace *f)
static void mesh_customdatacorrect_init_vert(TransCustomDataLayer *tcld, TransDataBasic *td, const int index)
static void mesh_transdata_mirror_apply(TransDataContainer *tc)
void transform_convert_mesh_islands_calc(BMEditMesh *em, bool calc_single_islands, bool calc_island_center, bool calc_island_axismtx, TransIslandData *r_island_data)
static void VertsToTransData(TransInfo *t, TransData *td, TransDataExtension *tx, BMEditMesh *em, BMVert *eve, const TransIslandData *island_data, const int island_index)
T distance(const T &a, const T &b)
QuaternionBase< T > normalize_and_get_length(const QuaternionBase< T > &q, T &out_length)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
T midpoint(const T &a, const T &b)
AxisSigned cross(const AxisSigned a, const AxisSigned b)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
VecBase< int32_t, 2 > int2
VecBase< float, 3 > float3
const btScalar eps
Definition poly34.cpp:11
#define fabsf
#define FLT_MAX
Definition stdcycles.h:14
struct BMLoop * l
short selectmode
blender::Array< std::array< BMLoop *, 3 > > looptris
float no[3]
struct BMVert * v
struct BMEdge * e
struct BMLoop * radial_next
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
float co[3]
float no[3]
int totvert
int totvertsel
CustomData pdata
CustomData ldata
Definition DNA_ID.h:414
struct LinkNode * next
struct ToolSettings * toolsettings
struct blender::ed::transform::TransCustomDataLayer::@157245340312017314101014255061344106116265121232 merge_group
TransCustomData_PartialUpdate partial_update[PARTIAL_TYPE_MAX]
void(* free_cb)(TransInfo *, TransDataContainer *tc, TransCustomData *custom_data)
Definition transform.hh:633
void foreach_index_selected(FunctionRef< void(int)> fn) const
Definition transform.hh:785
i
Definition text_draw.cc:230
#define TD_MIRROR_EDGE_AXIS_SHIFT
Definition transform.hh:376
#define T_PROP_EDIT_ALL
Definition transform.hh:28
#define FOREACH_TRANS_DATA_CONTAINER(t, th)
Definition transform.hh:42
conversion and adaptation of different datablocks to a common struct.
#define FACE_SUBSTITUTE_INDEX
#define TRANSFORM_MAXDIST_MIRROR
#define PARTIAL_TYPE_MAX
uint8_t flag
Definition wm_window.cc:145