Blender V4.5
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 -
580 * walk around the to the next.
581 *
582 * Since we only need to check if the vertex is in this corner,
583 * its not important _which_ loop - as long as its not overlapping
584 * 'sv->co_orig_3d', see: #45096. */
585 project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis);
586 while (UNLIKELY(((co_prev_ok = (len_squared_v3v3(v_proj[1], v_proj[0]) > eps)) == false) &&
587 ((l_prev = l_prev->prev) != l->next)))
588 {
589 co_prev = mesh_vert_orig_co_get(tcld, l_prev->v);
590 project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis);
591 }
592 project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis);
593 while (UNLIKELY(((co_next_ok = (len_squared_v3v3(v_proj[1], v_proj[2]) > eps)) == false) &&
594 ((l_next = l_next->next) != l->prev)))
595 {
596 co_next = mesh_vert_orig_co_get(tcld, l_next->v);
597 project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis);
598 }
599
600 if (co_prev_ok && co_next_ok) {
601 const float dist = dist_signed_squared_to_corner_v3v3v3(
602 v->co, UNPACK3(v_proj), v_proj_axis);
603
604 loop_weights[j] = (dist >= 0.0f) ? 1.0f : ((dist <= -eps) ? 0.0f : (1.0f + (dist / eps)));
605 if (UNLIKELY(!isfinite(loop_weights[j]))) {
606 loop_weights[j] = 0.0f;
607 }
608 }
609 else {
610 loop_weights[j] = 0.0f;
611 }
612 }
613 }
614
615 if (tcld->use_merge_group) {
616 LinkNode **cd_loop_groups = merge_data->cd_loop_groups;
617 if (tcld->merge_group.customdatalayer_map_len && cd_loop_groups) {
618 if (do_loop_weight) {
619 for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
621 bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j], loop_weights);
622 }
623 }
624 else {
625 for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) {
627 bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j]);
628 }
629 }
630 }
631 }
632
633 /* Special handling for multires
634 *
635 * Interpolate from every other loop (not ideal)
636 * However values will only be taken from loops which overlap other mdisps.
637 */
638 const bool update_loop_mdisps = is_moved && do_loop_mdisps && (tcld->cd_loop_mdisp_offset != -1);
639 if (update_loop_mdisps) {
640 float(*faces_center)[3] = static_cast<float(*)[3]>(BLI_array_alloca(faces_center, l_num));
641 BMLoop *l;
642
643 BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) {
644 BM_face_calc_center_median(l->f, faces_center[j]);
645 }
646
647 BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) {
648 BMFace *f_copy = static_cast<BMFace *>(BLI_ghash_lookup(tcld->origfaces, l->f));
649 float f_copy_center[3];
650 BMIter liter_other;
651 BMLoop *l_other;
652 int j_other;
653
654 BM_face_calc_center_median(f_copy, f_copy_center);
655
656 BM_ITER_ELEM_INDEX (l_other, &liter_other, v, BM_LOOPS_OF_VERT, j_other) {
658 l_other->f,
659 f_copy,
660 faces_center[j_other],
661 f_copy_center,
663 }
664 }
665 }
666}
667
669{
670 TransCustomDataMesh *tcmd = static_cast<TransCustomDataMesh *>(tc->custom.type.data);
671 TransCustomDataLayer *tcld = tcmd ? tcmd->cd_layer_correct : nullptr;
672 if (tcld == nullptr) {
673 return;
674 }
675 const bool use_merge_group = tcld->use_merge_group;
676
677 TransCustomDataMergeGroup *merge_data = tcld->merge_group.data;
678 TransData *tob = tc->data;
679 for (int i = tc->data_len; i--; tob++) {
680 mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)tob, merge_data, is_final);
681
682 if (use_merge_group) {
683 merge_data++;
684 }
685 }
686
687 TransDataMirror *td_mirror = tc->data_mirror;
688 for (int i = tc->data_mirror_len; i--; td_mirror++) {
689 mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)td_mirror, merge_data, is_final);
690
691 if (use_merge_group) {
692 merge_data++;
693 }
694 }
695}
696
698
699/* -------------------------------------------------------------------- */
702
704{
706 TransCustomDataMesh *tcmd = static_cast<TransCustomDataMesh *>(tc->custom.type.data);
707 TransCustomDataLayer *tcld = tcmd ? tcmd->cd_layer_correct : nullptr;
708 if (!tcld) {
709 continue;
710 }
711
712 BMesh *bm = tcld->bm;
713 BMesh *bm_copy = tcld->bm_origfaces;
714 const BMCustomDataCopyMap cd_loop_map = CustomData_bmesh_copy_map_calc(bm_copy->ldata,
715 bm->ldata);
716
717 GHashIterator gh_iter;
718 GHASH_ITER (gh_iter, tcld->origfaces) {
719 BMFace *f = static_cast<BMFace *>(BLI_ghashIterator_getKey(&gh_iter));
720 BMFace *f_copy = static_cast<BMFace *>(BLI_ghashIterator_getValue(&gh_iter));
721 BLI_assert(f->len == f_copy->len);
722
723 BMLoop *l_iter, *l_first, *l_copy;
724 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
725 l_copy = BM_FACE_FIRST_LOOP(f_copy);
726 do {
727 /* TODO: Restore only the elements that transform. */
728 BM_elem_attrs_copy(bm, cd_loop_map, l_copy, l_iter);
729 l_copy = l_copy->next;
730 } while ((l_iter = l_iter->next) != l_first);
731 }
732 }
733}
734
736
737/* -------------------------------------------------------------------- */
740
742 const bool calc_single_islands,
743 const bool calc_island_center,
744 const bool calc_island_axismtx,
745 TransIslandData *r_island_data)
746{
747 TransIslandData data = {nullptr};
748
749 BMesh *bm = em->bm;
750 char htype;
751 char itype;
752 int i;
753
754 /* Group variables. */
755 int *groups_array = nullptr;
756 int(*group_index)[2] = nullptr;
757
758 bool has_only_single_islands = bm->totedgesel == 0 && bm->totfacesel == 0;
759 if (has_only_single_islands && !calc_single_islands) {
760 return;
761 }
762
763 data.island_vert_map = static_cast<int *>(
764 MEM_mallocN(sizeof(*data.island_vert_map) * bm->totvert, __func__));
765 /* We shouldn't need this, but with incorrect selection flushing
766 * its possible we have a selected vertex that's not in a face,
767 * for now best not crash in that case. */
768 copy_vn_i(data.island_vert_map, bm->totvert, -1);
769
770 if (!has_only_single_islands) {
772 groups_array = MEM_malloc_arrayN<int>(bm->totedgesel, __func__);
773 data.island_tot = BM_mesh_calc_edge_groups(
774 bm, groups_array, &group_index, nullptr, nullptr, BM_ELEM_SELECT);
775
776 htype = BM_EDGE;
778 }
779 else { /* `bm->selectmode & SCE_SELECT_FACE`. */
780 groups_array = MEM_malloc_arrayN<int>(bm->totfacesel, __func__);
781 data.island_tot = BM_mesh_calc_face_groups(
782 bm, groups_array, &group_index, nullptr, nullptr, nullptr, BM_ELEM_SELECT, BM_VERT);
783
784 htype = BM_FACE;
786 }
787
788 BLI_assert(data.island_tot);
789 if (calc_island_center) {
790 data.center = static_cast<float(*)[3]>(
791 MEM_mallocN(sizeof(*data.center) * data.island_tot, __func__));
792 }
793
794 if (calc_island_axismtx) {
795 data.axismtx = static_cast<float(*)[3][3]>(
796 MEM_mallocN(sizeof(*data.axismtx) * data.island_tot, __func__));
797 }
798
800
801 void **ele_array;
802 ele_array = (htype == BM_FACE) ? (void **)bm->ftable : (void **)bm->etable;
803
805
806 /* May be an edge OR a face array. */
807 for (i = 0; i < data.island_tot; i++) {
808 BMEditSelection ese = {nullptr};
809
810 const int fg_sta = group_index[i][0];
811 const int fg_len = group_index[i][1];
812 float co[3], no[3], tangent[3];
813 int j;
814
815 zero_v3(co);
816 zero_v3(no);
817 zero_v3(tangent);
818
819 ese.htype = htype;
820
821 /* Loop on each face or edge in this group:
822 * - Assign `r_vert_map`.
823 * - Calculate (`co`, `no`).
824 */
825 for (j = 0; j < fg_len; j++) {
826 ese.ele = static_cast<BMElem *>(ele_array[groups_array[fg_sta + j]]);
827
828 if (data.center) {
829 float tmp_co[3];
830 BM_editselection_center(&ese, tmp_co);
831 add_v3_v3(co, tmp_co);
832 }
833
834 if (data.axismtx) {
835 float tmp_no[3], tmp_tangent[3];
836 BM_editselection_normal(&ese, tmp_no);
837 BM_editselection_plane(&ese, tmp_tangent);
838 add_v3_v3(no, tmp_no);
839 add_v3_v3(tangent, tmp_tangent);
840 }
841
842 {
843 /* Setup vertex map. */
844 BMIter iter;
845 BMVert *v;
846
847 /* Connected edge-verts. */
848 BM_ITER_ELEM (v, &iter, ese.ele, itype) {
849 data.island_vert_map[BM_elem_index_get(v)] = i;
850 }
851 }
852 }
853
854 if (data.center) {
855 mul_v3_v3fl(data.center[i], co, 1.0f / float(fg_len));
856 }
857
858 if (data.axismtx) {
859 normalize_v3(no);
860 normalize_v3(tangent);
861
862 createSpaceNormalTangent_or_fallback(data.axismtx[i], no, tangent);
863 }
864 }
865
866 MEM_freeN(groups_array);
867 MEM_freeN(group_index);
868 }
869
870 /* For proportional editing we need islands of 1 so connected vertices can use it with
871 * #V3D_AROUND_LOCAL_ORIGINS. */
872 if (calc_single_islands) {
873 BMIter viter;
874 BMVert *v;
875 int group_tot_single = 0;
876
878 if (BM_elem_flag_test(v, BM_ELEM_SELECT) && (data.island_vert_map[i] == -1)) {
879 group_tot_single += 1;
880 }
881 }
882
883 if (group_tot_single != 0) {
884 if (calc_island_center) {
885 data.center = static_cast<float(*)[3]>(MEM_reallocN(
886 data.center, sizeof(*data.center) * (data.island_tot + group_tot_single)));
887 }
888 if (calc_island_axismtx) {
889 data.axismtx = static_cast<float(*)[3][3]>(MEM_reallocN(
890 data.axismtx, sizeof(*data.axismtx) * (data.island_tot + group_tot_single)));
891 }
892
894 if (BM_elem_flag_test(v, BM_ELEM_SELECT) && (data.island_vert_map[i] == -1)) {
895 data.island_vert_map[i] = data.island_tot;
896 if (data.center) {
897 copy_v3_v3(data.center[data.island_tot], v->co);
898 }
899 if (data.axismtx) {
900 if (is_zero_v3(v->no) == false) {
901 axis_dominant_v3_to_m3(data.axismtx[data.island_tot], v->no);
902 invert_m3(data.axismtx[data.island_tot]);
903 }
904 else {
905 unit_m3(data.axismtx[data.island_tot]);
906 }
907 }
908
909 data.island_tot += 1;
910 }
911 }
912 }
913 }
914
915 *r_island_data = data;
916}
917
919{
920 if (island_data->center) {
921 MEM_freeN(island_data->center);
922 }
923 if (island_data->axismtx) {
924 MEM_freeN(island_data->axismtx);
925 }
926 if (island_data->island_vert_map) {
927 MEM_freeN(island_data->island_vert_map);
928 }
929}
930
932
933/* -------------------------------------------------------------------- */
936
937/* Propagate distance from v1 and v2 to v0. */
939 BMVert *v1,
940 BMVert *v2,
941 float *dists, /* Optionally track original index. */
942 int *index,
943 const float mtx[3][3])
944{
946 {
947 const int i0 = BM_elem_index_get(v0);
948 const int i1 = BM_elem_index_get(v1);
949
950 BLI_assert(dists[i1] != FLT_MAX);
951 if (dists[i0] <= dists[i1]) {
952 return false;
953 }
954
955 float dist0;
956
957 if (v2) {
958 /* Distance across triangle. */
959 const int i2 = BM_elem_index_get(v2);
960 BLI_assert(dists[i2] != FLT_MAX);
961 if (dists[i0] <= dists[i2]) {
962 return false;
963 }
964
965 float vm0[3], vm1[3], vm2[3];
966 mul_v3_m3v3(vm0, mtx, v0->co);
967 mul_v3_m3v3(vm1, mtx, v1->co);
968 mul_v3_m3v3(vm2, mtx, v2->co);
969
970 dist0 = geodesic_distance_propagate_across_triangle(vm0, vm1, vm2, dists[i1], dists[i2]);
971 }
972 else {
973 /* Distance along edge. */
974 float vec[3];
975 sub_v3_v3v3(vec, v1->co, v0->co);
976 mul_m3_v3(mtx, vec);
977
978 dist0 = dists[i1] + len_v3(vec);
979 }
980
981 if (dist0 < dists[i0]) {
982 dists[i0] = dist0;
983 if (index != nullptr) {
984 index[i0] = index[i1];
985 }
986 return true;
987 }
988 }
989
990 return false;
991}
992
994{
995 /* Actual loose edge. */
996 if (edge->l == nullptr) {
997 return true;
998 }
999
1000 /* Loose edge due to hidden adjacent faces. */
1001 BMIter iter;
1002 BMFace *face;
1003 BM_ITER_ELEM (face, &iter, edge, BM_FACES_OF_EDGE) {
1004 if (BM_elem_flag_test(face, BM_ELEM_HIDDEN) == 0) {
1005 return false;
1006 }
1007 }
1008 return true;
1009}
1010
1012 const float mtx[3][3],
1013 float *dists,
1014 int *index)
1015{
1016 BLI_LINKSTACK_DECLARE(queue, BMEdge *);
1017
1018 /* Any BM_ELEM_TAG'd edge is in 'queue_next', so we don't add in twice. */
1019 const int tag_queued = BM_ELEM_TAG;
1020 const int tag_loose = BM_ELEM_TAG_ALT;
1021
1022 BLI_LINKSTACK_DECLARE(queue_next, BMEdge *);
1023
1024 BLI_LINKSTACK_INIT(queue);
1025 BLI_LINKSTACK_INIT(queue_next);
1026
1027 {
1028 /* Set indexes and initial distances for selected vertices. */
1029 BMIter viter;
1030 BMVert *v;
1031 int i;
1032
1034 float dist;
1035 BM_elem_index_set(v, i); /* set_inline */
1036
1038 dist = FLT_MAX;
1039 if (index != nullptr) {
1040 index[i] = i;
1041 }
1042 }
1043 else {
1044 dist = 0.0f;
1045 if (index != nullptr) {
1046 index[i] = i;
1047 }
1048 }
1049
1050 dists[i] = dist;
1051 }
1052 bm->elem_index_dirty &= ~BM_VERT;
1053 }
1054
1055 {
1056 /* Add edges with at least one selected vertex to the queue. */
1057 BMIter eiter;
1058 BMEdge *e;
1059
1060 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
1061
1062 /* Always clear to satisfy the assert, also predictable to leave in cleared state. */
1063 BM_elem_flag_disable(e, tag_queued);
1064
1066 continue;
1067 }
1068
1069 BMVert *v1 = e->v1;
1070 BMVert *v2 = e->v2;
1071 int i1 = BM_elem_index_get(v1);
1072 int i2 = BM_elem_index_get(v2);
1073
1074 if (dists[i1] != FLT_MAX || dists[i2] != FLT_MAX) {
1075 BLI_LINKSTACK_PUSH(queue, e);
1076 }
1078 }
1079 }
1080
1081 do {
1082 BMEdge *e;
1083
1084 while ((e = BLI_LINKSTACK_POP(queue))) {
1085 BMVert *v1 = e->v1;
1086 BMVert *v2 = e->v2;
1087 int i1 = BM_elem_index_get(v1);
1088 int i2 = BM_elem_index_get(v2);
1089
1090 if (BM_elem_flag_test(e, tag_loose) || (dists[i1] == FLT_MAX || dists[i2] == FLT_MAX)) {
1091 /* Propagate along edge from vertex with smallest to largest distance. */
1092 if (dists[i1] > dists[i2]) {
1093 std::swap(i1, i2);
1094 std::swap(v1, v2);
1095 }
1096
1097 if (bmesh_test_dist_add(v2, v1, nullptr, dists, index, mtx)) {
1098 /* Add adjacent edges to the queue if:
1099 * - Adjacent edge is loose
1100 * - Edge itself is loose
1101 * - Edge has vertex that was originally selected
1102 * In all these cases a direct distance along the edge is accurate and
1103 * required to make sure we visit all edges. Other edges are handled by
1104 * propagation across edges below. */
1105 const bool need_direct_distance = BM_elem_flag_test(e, tag_loose) ||
1108 BMEdge *e_other;
1109 BMIter eiter;
1110 BM_ITER_ELEM (e_other, &eiter, v2, BM_EDGES_OF_VERT) {
1111 if (e_other != e && BM_elem_flag_test(e_other, tag_queued) == 0 &&
1112 !BM_elem_flag_test(e_other, BM_ELEM_HIDDEN) &&
1113 (need_direct_distance || BM_elem_flag_test(e_other, tag_loose)))
1114 {
1115 BM_elem_flag_enable(e_other, tag_queued);
1116 BLI_LINKSTACK_PUSH(queue_next, e_other);
1117 }
1118 }
1119 }
1120 }
1121
1122 if (!BM_elem_flag_test(e, tag_loose)) {
1123 /* Propagate across edge to vertices in adjacent faces. */
1124 BMLoop *l;
1125 BMIter liter;
1126 BM_ITER_ELEM (l, &liter, e, BM_LOOPS_OF_EDGE) {
1128 continue;
1129 }
1130 /* Don't check hidden edges or vertices in this loop
1131 * since any hidden edge causes the face to be hidden too. */
1132 for (BMLoop *l_other = l->next->next; l_other != l; l_other = l_other->next) {
1133 BMVert *v_other = l_other->v;
1134 BLI_assert(!ELEM(v_other, v1, v2));
1135
1136 if (bmesh_test_dist_add(v_other, v1, v2, dists, index, mtx)) {
1137 /* Add adjacent edges to the queue, if they are ready to propagate across/along.
1138 * Always propagate along loose edges, and for other edges only propagate across
1139 * if both vertices have a known distances. */
1140 BMEdge *e_other;
1141 BMIter eiter;
1142 BM_ITER_ELEM (e_other, &eiter, v_other, BM_EDGES_OF_VERT) {
1143 if (e_other != e && BM_elem_flag_test(e_other, tag_queued) == 0 &&
1144 !BM_elem_flag_test(e_other, BM_ELEM_HIDDEN) &&
1145 (BM_elem_flag_test(e_other, tag_loose) ||
1146 dists[BM_elem_index_get(BM_edge_other_vert(e_other, v_other))] != FLT_MAX))
1147 {
1148 BM_elem_flag_enable(e_other, tag_queued);
1149 BLI_LINKSTACK_PUSH(queue_next, e_other);
1150 }
1151 }
1152 }
1153 }
1154 }
1155 }
1156 }
1157
1158 /* Clear for the next loop. */
1159 for (LinkNode *lnk = queue_next; lnk; lnk = lnk->next) {
1160 BMEdge *e_link = static_cast<BMEdge *>(lnk->link);
1161
1162 BM_elem_flag_disable(e_link, tag_queued);
1163 }
1164
1165 BLI_LINKSTACK_SWAP(queue, queue_next);
1166
1167 /* None should be tagged now since 'queue_next' is empty. */
1168 BLI_assert(BM_iter_mesh_count_flag(BM_EDGES_OF_MESH, bm, tag_queued, true) == 0);
1169 } while (BLI_LINKSTACK_SIZE(queue));
1170
1171 BLI_LINKSTACK_FREE(queue);
1172 BLI_LINKSTACK_FREE(queue_next);
1173}
1174
1176
1177/* -------------------------------------------------------------------- */
1180
1181/* Used for both mirror epsilon and TD_MIRROR_EDGE_ */
1182#define TRANSFORM_MAXDIST_MIRROR 0.00002f
1183
1184static bool is_in_quadrant_v3(const float co[3], const int quadrant[3], const float epsilon)
1185{
1186 if (quadrant[0] && ((co[0] * quadrant[0]) < -epsilon)) {
1187 return false;
1188 }
1189 if (quadrant[1] && ((co[1] * quadrant[1]) < -epsilon)) {
1190 return false;
1191 }
1192 if (quadrant[2] && ((co[2] * quadrant[2]) < -epsilon)) {
1193 return false;
1194 }
1195 return true;
1196}
1197
1199 const bool use_select,
1200 const bool use_topology,
1201 const bool mirror_axis[3],
1202 TransMirrorData *r_mirror_data)
1203{
1204 MirrorDataVert *vert_map;
1205
1206 BMesh *bm = em->bm;
1207 BMVert *eve;
1208 BMIter iter;
1209 int i, flag, totvert = bm->totvert;
1210
1211 vert_map = MEM_calloc_arrayN<MirrorDataVert>(totvert, __func__);
1212
1213 float select_sum[3] = {0};
1214 BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
1215 vert_map[i] = MirrorDataVert{-1, 0};
1217 continue;
1218 }
1220 add_v3_v3(select_sum, eve->co);
1221 }
1222 }
1223
1224 /* Tag only elements that will be transformed within the quadrant. */
1225 int quadrant[3];
1226 for (int a = 0; a < 3; a++) {
1227 if (mirror_axis[a]) {
1228 quadrant[a] = select_sum[a] >= 0.0f ? 1 : -1;
1229 }
1230 else {
1231 quadrant[a] = 0;
1232 }
1233 }
1234
1235 uint mirror_elem_len = 0;
1236 int *index[3] = {nullptr, nullptr, nullptr};
1237 bool is_single_mirror_axis = (mirror_axis[0] + mirror_axis[1] + mirror_axis[2]) == 1;
1238 bool test_selected_only = use_select && is_single_mirror_axis;
1239 for (int a = 0; a < 3; a++) {
1240 if (!mirror_axis[a]) {
1241 continue;
1242 }
1243
1244 index[a] = static_cast<int *>(MEM_mallocN(totvert * sizeof(*index[a]), __func__));
1246 em, a, false, test_selected_only, true, use_topology, TRANSFORM_MAXDIST_MIRROR, index[a]);
1247
1248 flag = TD_MIRROR_X << a;
1249 BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
1250 int i_mirr = index[a][i];
1251 if (i_mirr < 0) {
1252 continue;
1253 }
1255 continue;
1256 }
1257 if (use_select && !BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
1258 continue;
1259 }
1260 if (!is_in_quadrant_v3(eve->co, quadrant, TRANSFORM_MAXDIST_MIRROR)) {
1261 continue;
1262 }
1263 if (vert_map[i_mirr].flag != 0) {
1264 /* One mirror per element.
1265 * It can happen when vertices occupy the same position. */
1266 continue;
1267 }
1268 if (vert_map[i].flag & flag) {
1269 /* It's already a mirror.
1270 * Avoid a mirror vertex dependency cycle.
1271 * This can happen when the vertices are within the mirror threshold. */
1272 continue;
1273 }
1274
1275 vert_map[i_mirr] = MirrorDataVert{i, flag};
1276 mirror_elem_len++;
1277 }
1278 }
1279
1280 if (!mirror_elem_len) {
1281 MEM_freeN(vert_map);
1282 vert_map = nullptr;
1283 }
1284 else if (!is_single_mirror_axis) {
1285 /* Adjustment for elements that are mirrors of mirrored elements. */
1286 for (int a = 0; a < 3; a++) {
1287 if (!mirror_axis[a]) {
1288 continue;
1289 }
1290
1291 flag = TD_MIRROR_X << a;
1292 for (i = 0; i < totvert; i++) {
1293 int i_mirr = index[a][i];
1294 if (i_mirr < 0) {
1295 continue;
1296 }
1297 if (vert_map[i].index != -1 && !(vert_map[i].flag & flag)) {
1298 if (vert_map[i_mirr].index == -1) {
1299 mirror_elem_len++;
1300 }
1301 vert_map[i_mirr].index = vert_map[i].index;
1302 vert_map[i_mirr].flag |= vert_map[i].flag | flag;
1303 }
1304 }
1305 }
1306 }
1307
1308 MEM_SAFE_FREE(index[0]);
1309 MEM_SAFE_FREE(index[1]);
1310 MEM_SAFE_FREE(index[2]);
1311
1312 r_mirror_data->vert_map = vert_map;
1313 r_mirror_data->mirror_elem_len = mirror_elem_len;
1314}
1315
1317{
1318 if (mirror_data->vert_map) {
1319 MEM_freeN(mirror_data->vert_map);
1320 }
1321}
1322
1324
1325/* -------------------------------------------------------------------- */
1328
1331 BMEditMesh *em,
1332 TransMeshDataCrazySpace *r_crazyspace_data)
1333{
1334 float(*quats)[4] = nullptr;
1335 const int prop_mode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0;
1336 if (BKE_modifiers_get_cage_index(t->scene, tc->obedit, nullptr, true) != -1) {
1337 Array<float3, 0> defcos;
1338 int totleft = -1;
1341
1342 /* Use evaluated state because we need b-bone cache. */
1343 Scene *scene_eval = DEG_get_evaluated(t->depsgraph, t->scene);
1344 Object *obedit_eval = DEG_get_evaluated(t->depsgraph, tc->obedit);
1345 BMEditMesh *em_eval = BKE_editmesh_from_object(obedit_eval);
1346 /* Check if we can use deform matrices for modifier from the
1347 * start up to stack, they are more accurate than quats. */
1349 t->depsgraph, scene_eval, obedit_eval, em_eval, r_crazyspace_data->defmats, defcos);
1350 }
1351
1352 /* If we still have more modifiers, also do crazy-space
1353 * correction with \a quats, relative to the coordinates after
1354 * the modifiers that support deform matrices \a defcos. */
1355
1356#if 0 /* TODO(@ideasman42): fix crazy-space & extrude so it can be enabled for general use. */
1357 if ((totleft > 0) || (totleft == -1))
1358#else
1359 if (totleft > 0)
1360#endif
1361 {
1363 tc->obedit);
1364 quats = static_cast<float(*)[4]>(
1365 MEM_mallocN(em->bm->totvert * sizeof(*quats), "crazy quats"));
1366 BKE_crazyspace_set_quats_editmesh(em, defcos, mappedcos, quats, !prop_mode);
1367 }
1368 }
1369 r_crazyspace_data->quats = quats;
1370}
1371
1373 const float smtx[3][3],
1374 const float defmat[3][3],
1375 const float quat[4],
1376 TransData *r_td)
1377{
1378 /* CrazySpace. */
1379 if (quat || defmat) {
1380 float mat[3][3], qmat[3][3], imat[3][3];
1381
1382 /* Use both or either quat and defmat correction. */
1383 if (quat) {
1384 quat_to_mat3(qmat, quat);
1385
1386 if (defmat) {
1387 mul_m3_series(mat, defmat, qmat, mtx);
1388 }
1389 else {
1390 mul_m3_m3m3(mat, mtx, qmat);
1391 }
1392 }
1393 else {
1394 mul_m3_m3m3(mat, mtx, defmat);
1395 }
1396
1397 invert_m3_m3(imat, mat);
1398
1399 copy_m3_m3(r_td->smtx, imat);
1400 copy_m3_m3(r_td->mtx, mat);
1401 }
1402 else {
1403 copy_m3_m3(r_td->smtx, smtx);
1404 copy_m3_m3(r_td->mtx, mtx);
1405 }
1406}
1407
1409{
1410 if (r_crazyspace_data->quats) {
1411 MEM_freeN(r_crazyspace_data->quats);
1412 }
1413}
1414
1416
1417/* -------------------------------------------------------------------- */
1420
1421static void mesh_transdata_center_copy(const TransIslandData *island_data,
1422 const int island_index,
1423 const float iloc[3],
1424 float r_center[3])
1425{
1426 if (island_data->center && island_index != -1) {
1427 copy_v3_v3(r_center, island_data->center[island_index]);
1428 }
1429 else {
1430 copy_v3_v3(r_center, iloc);
1431 }
1432}
1433
1434/* Way to overwrite what data is edited with transform. */
1436 TransData *td,
1438 BMEditMesh *em,
1439 BMVert *eve,
1440 const TransIslandData *island_data,
1441 const int island_index)
1442{
1443 float *no, _no[3];
1445
1446 td->flag = 0;
1447 // if (key)
1448 // td->loc = key->co;
1449 // else
1450 td->loc = eve->co;
1451 copy_v3_v3(td->iloc, td->loc);
1452
1453 if ((t->mode == TFM_SHRINKFATTEN) && (em->selectmode & SCE_SELECT_FACE) &&
1455 {
1456 no = _no;
1457 }
1458 else {
1459 no = eve->no;
1460 }
1461
1462 mesh_transdata_center_copy(island_data, island_index, td->iloc, td->center);
1463
1464 if ((island_index != -1) && island_data->axismtx) {
1465 copy_m3_m3(td->axismtx, island_data->axismtx[island_index]);
1466 }
1467 else if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
1468 createSpaceNormal(td->axismtx, no);
1469 }
1470 else {
1471 /* Setting normals. */
1472 copy_v3_v3(td->axismtx[2], no);
1473 td->axismtx[0][0] = td->axismtx[0][1] = td->axismtx[0][2] = td->axismtx[1][0] =
1474 td->axismtx[1][1] = td->axismtx[1][2] = 0.0f;
1475 }
1476
1477 td->ext = nullptr;
1478 td->val = nullptr;
1479 td->extra = eve;
1480 if (t->mode == TFM_SHRINKFATTEN) {
1481 td->ext = tx;
1483 }
1484}
1485
1487{
1489 TransDataExtension *tx = nullptr;
1490 BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
1491 Mesh *mesh = static_cast<Mesh *>(tc->obedit->data);
1492 BMesh *bm = em->bm;
1493 BMVert *eve;
1494 BMIter iter;
1495 float mtx[3][3], smtx[3][3];
1496 int a;
1497 const int prop_mode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0;
1498
1499 TransIslandData island_data = {nullptr};
1500 TransMirrorData mirror_data = {nullptr};
1501 TransMeshDataCrazySpace crazyspace_data = {};
1502
1503 /* Avoid editing locked shapes. */
1504 if (t->mode != TFM_DUMMY && object::shape_key_report_if_locked(tc->obedit, t->reports)) {
1505 continue;
1506 }
1507
1514
1515 /* Support other objects using proportional editing to adjust these, unless connected is
1516 * enabled. */
1517 if ((!prop_mode || (prop_mode & T_PROP_CONNECTED)) && (bm->totvertsel == 0)) {
1518 continue;
1519 }
1520
1521 int data_len = 0;
1522 if (prop_mode) {
1523 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
1524 if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
1525 data_len++;
1526 }
1527 }
1528 }
1529 else {
1530 data_len = bm->totvertsel;
1531 }
1532
1533 if (data_len == 0) {
1534 continue;
1535 }
1536
1537 /* Snap rotation along normal needs a common axis for whole islands,
1538 * otherwise one get random crazy results, see #59104.
1539 * However, we do not want to use the island center for the pivot/translation reference. */
1540 const bool is_snap_rotate = ((t->mode == TFM_TRANSLATION) &&
1541 /* There is not guarantee that snapping
1542 * is initialized yet at this point... */
1543 (usingSnappingNormal(t) ||
1544 (t->settings->snap_flag & SCE_SNAP_ROTATE) != 0) &&
1546
1547 /* Even for translation this is needed because of island-orientation, see: #51651. */
1548 const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS) || is_snap_rotate;
1549 if (is_island_center) {
1550 /* In this specific case, near-by vertices will need to know
1551 * the island of the nearest connected vertex. */
1552 const bool calc_single_islands = ((prop_mode & T_PROP_CONNECTED) &&
1555
1556 const bool calc_island_center = !is_snap_rotate;
1557 /* The island axismtx is only necessary in some modes.
1558 * TODO(Germano): Extend the list to exclude other modes. */
1559 const bool calc_island_axismtx = !ELEM(t->mode, TFM_SHRINKFATTEN);
1560
1562 em, calc_single_islands, calc_island_center, calc_island_axismtx, &island_data);
1563 }
1564
1565 copy_m3_m4(mtx, tc->obedit->object_to_world().ptr());
1566 /* We use a pseudo-inverse so that when one of the axes is scaled to 0,
1567 * matrix inversion still works and we can still moving along the other. */
1569
1570 /* Original index of our connected vertex when connected distances are calculated.
1571 * Optional, allocate if needed. */
1572 int *dists_index = nullptr;
1573 float *dists = nullptr;
1574 if (prop_mode & T_PROP_CONNECTED) {
1575 dists = MEM_malloc_arrayN<float>(bm->totvert, __func__);
1576 if (is_island_center) {
1577 dists_index = MEM_malloc_arrayN<int>(bm->totvert, __func__);
1578 }
1579 transform_convert_mesh_connectivity_distance(em->bm, mtx, dists, dists_index);
1580 }
1581
1582 /* Create TransDataMirror. */
1583 if (tc->use_mirror_axis_any) {
1584 bool use_topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0;
1585 bool use_select = (t->flag & T_PROP_EDIT) == 0;
1586 const bool mirror_axis[3] = {
1587 bool(tc->use_mirror_axis_x), bool(tc->use_mirror_axis_y), bool(tc->use_mirror_axis_z)};
1589 em, use_select, use_topology, mirror_axis, &mirror_data);
1590
1591 if (mirror_data.vert_map) {
1592 tc->data_mirror_len = mirror_data.mirror_elem_len;
1593 tc->data_mirror = static_cast<TransDataMirror *>(
1594 MEM_callocN(mirror_data.mirror_elem_len * sizeof(*tc->data_mirror), __func__));
1595
1596 BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) {
1597 if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
1598 if (mirror_data.vert_map[a].index != -1) {
1599 data_len--;
1600 }
1601 }
1602 }
1603 }
1604 }
1605
1606 /* Detect CrazySpace [tm]. */
1607 transform_convert_mesh_crazyspace_detect(t, tc, em, &crazyspace_data);
1608
1609 /* Create TransData. */
1610 BLI_assert(data_len >= 1);
1611 tc->data_len = data_len;
1612 tc->data = MEM_calloc_arrayN<TransData>(data_len, "TransObData(Mesh EditMode)");
1613 if (t->mode == TFM_SHRINKFATTEN) {
1614 /* Warning: this is overkill, we only need 2 extra floats,
1615 * but this stores loads of extra stuff, for TFM_SHRINKFATTEN its even more overkill
1616 * since we may not use the 'alt' transform mode to maintain shell thickness,
1617 * but with generic transform code its hard to lazy init variables. */
1618 tx = tc->data_ext = MEM_calloc_arrayN<TransDataExtension>(tc->data_len, "TransObData ext");
1619 }
1620
1621 TransData *tob = tc->data;
1622 TransDataMirror *td_mirror = tc->data_mirror;
1623 BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) {
1625 continue;
1626 }
1627
1628 int island_index = -1;
1629 if (island_data.island_vert_map) {
1630 const int connected_index = (dists_index && dists_index[a] != -1) ? dists_index[a] : a;
1631 island_index = island_data.island_vert_map[connected_index];
1632 }
1633
1634 if (mirror_data.vert_map && mirror_data.vert_map[a].index != -1) {
1635 int elem_index = mirror_data.vert_map[a].index;
1636 BMVert *v_src = BM_vert_at_index(bm, elem_index);
1637
1639 mirror_data.vert_map[a].flag |= TD_SELECTED;
1640 }
1641
1642 td_mirror->extra = eve;
1643 td_mirror->loc = eve->co;
1644 copy_v3_v3(td_mirror->iloc, eve->co);
1645 td_mirror->flag = mirror_data.vert_map[a].flag;
1646 td_mirror->loc_src = v_src->co;
1647 mesh_transdata_center_copy(&island_data, island_index, td_mirror->iloc, td_mirror->center);
1648
1649 td_mirror++;
1650 }
1651 else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
1652 /* Do not use the island center in case we are using islands
1653 * only to get axis for snap/rotate to normal... */
1654 VertsToTransData(t, tob, tx, em, eve, &island_data, island_index);
1655 if (tx) {
1656 tx++;
1657 }
1658
1659 /* Selected. */
1661 tob->flag |= TD_SELECTED;
1662 }
1663
1664 if (prop_mode) {
1665 if (prop_mode & T_PROP_CONNECTED) {
1666 tob->dist = dists[a];
1667 }
1668 else {
1669 tob->dist = FLT_MAX;
1670 }
1671 }
1672
1673 /* CrazySpace. */
1675 mtx,
1676 smtx,
1677 !crazyspace_data.defmats.is_empty() ? crazyspace_data.defmats[a].ptr() : nullptr,
1678 crazyspace_data.quats && BM_elem_flag_test(eve, BM_ELEM_TAG) ?
1679 crazyspace_data.quats[a] :
1680 nullptr,
1681 tob);
1682
1683 if (tc->use_mirror_axis_any) {
1684 if (tc->use_mirror_axis_x && fabsf(tob->loc[0]) < TRANSFORM_MAXDIST_MIRROR) {
1685 tob->flag |= TD_MIRROR_EDGE_X;
1686 }
1687 if (tc->use_mirror_axis_y && fabsf(tob->loc[1]) < TRANSFORM_MAXDIST_MIRROR) {
1688 tob->flag |= TD_MIRROR_EDGE_Y;
1689 }
1690 if (tc->use_mirror_axis_z && fabsf(tob->loc[2]) < TRANSFORM_MAXDIST_MIRROR) {
1691 tob->flag |= TD_MIRROR_EDGE_Z;
1692 }
1693 }
1694
1695 tob++;
1696 }
1697 }
1698
1702 if (dists) {
1703 MEM_freeN(dists);
1704 }
1705 if (dists_index) {
1706 MEM_freeN(dists_index);
1707 }
1708
1709 /* WORKAROUND: The transform operators rely on looptris being up-to-date.
1710 * However, this is not always the case, especially when called from scripts.
1711 * If this happens, to prevent update issues, make sure the size of #BMEditMesh::looptris
1712 * arrays aligns with the number looptris to update. */
1713 const bool looptri_is_dirty = em->looptris.size() !=
1714 poly_to_tri_count(bm->totface, bm->totloop);
1715 if (looptri_is_dirty) {
1717 }
1718 }
1719}
1720
1722
1723/* -------------------------------------------------------------------- */
1726
1729 enum ePartialType partial_type)
1730{
1732
1733 TransCustomData_PartialUpdate *pupdate = &tcmd->partial_update[partial_type];
1734
1735 if (pupdate->cache) {
1736
1737 /* Recalculate partial update data when the proportional editing size changes.
1738 *
1739 * Note that decreasing the proportional editing size requires the existing
1740 * partial data is used before recreating this partial data at the smaller size.
1741 * Since excluding geometry from being transformed requires an update.
1742 *
1743 * Extra logic is needed to account for this situation. */
1744
1745 bool recalc;
1746 if (pupdate->prop_size_prev < t->prop_size) {
1747 /* Size increase, simply recalculate. */
1748 recalc = true;
1749 }
1750 else if (pupdate->prop_size_prev > t->prop_size) {
1751 /* Size decreased, first use this partial data since reducing the size will transform
1752 * geometry which needs recalculating. */
1753 pupdate->prop_size_prev = t->prop_size;
1754 recalc = false;
1755 }
1756 else if (pupdate->prop_size != t->prop_size) {
1757 BLI_assert(pupdate->prop_size > pupdate->prop_size_prev);
1758 recalc = true;
1759 }
1760 else {
1761 BLI_assert(t->prop_size == pupdate->prop_size_prev);
1762 recalc = false;
1763 }
1764
1765 if (!recalc) {
1766 return pupdate->cache;
1767 }
1768
1770 pupdate->cache = nullptr;
1771 }
1772
1774
1776
1777 /* Only use `verts_group` or `verts_mask`. */
1778 Array<int> verts_group;
1779 int verts_group_count = 0; /* Number of non-zero elements in `verts_group`. */
1780
1781 blender::BitVector<> verts_mask;
1782 int verts_mask_count = 0; /* Number of elements enabled in `verts_mask`. */
1783
1784 if ((partial_type == PARTIAL_TYPE_GROUP) && ((t->flag & T_PROP_EDIT) || tc->use_mirror_axis_any))
1785 {
1786 verts_group = Array<int>(em->bm->totvert, 0);
1787 int i;
1788 TransData *td;
1789 for (i = 0, td = tc->data; i < tc->data_len; i++, td++) {
1790 if (td->factor == 0.0f) {
1791 continue;
1792 }
1793 const BMVert *v = (BMVert *)td->extra;
1794 const int v_index = BM_elem_index_get(v);
1795 BLI_assert(verts_group[v_index] == 0);
1796 if (td->factor < 1.0f) {
1797 /* Don't use grouping logic with the factor is under 1.0. */
1798 verts_group[v_index] = -1;
1799 }
1800 else {
1801 BLI_assert(td->factor == 1.0f);
1802 verts_group[v_index] = 1;
1803 if (tc->use_mirror_axis_any) {
1804 /* Use bits 2-4 for central alignment (don't overlap the first bit). */
1806 verts_group[v_index] |= (flag >> TD_MIRROR_EDGE_AXIS_SHIFT) << 1;
1807 }
1808 }
1809 verts_mask_count += 1;
1810 }
1811
1812 TransDataMirror *td_mirror = tc->data_mirror;
1813 for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) {
1814 BMVert *v_mirr = (BMVert *)POINTER_OFFSET(td_mirror->loc_src, -offsetof(BMVert, co));
1815 /* The equality check is to account for the case when topology mirror moves
1816 * the vertex from it's original location to match it's symmetrical position,
1817 * with proportional editing enabled. */
1818 const int v_mirr_index = BM_elem_index_get(v_mirr);
1819 if (verts_group[v_mirr_index] == 0 && equals_v3v3(td_mirror->loc, td_mirror->iloc)) {
1820 continue;
1821 }
1822
1823 BMVert *v_mirr_other = (BMVert *)td_mirror->extra;
1824 /* This assert should never fail since there is no overlap
1825 * between mirrored vertices and non-mirrored. */
1826 BLI_assert(verts_group[BM_elem_index_get(v_mirr_other)] == 0);
1827 const int v_mirr_other_index = BM_elem_index_get(v_mirr_other);
1828
1829 if (verts_group[v_mirr_index] == -1) {
1830 verts_group[v_mirr_other_index] = -1;
1831 }
1832 else {
1833 /* Use bits 5-8 for mirror (don't overlap previous bits). */
1834 const int flag = td_mirror->flag & (TD_MIRROR_X | TD_MIRROR_Y | TD_MIRROR_Z);
1835 verts_group[v_mirr_other_index] |= (flag >> TD_MIRROR_EDGE_AXIS_SHIFT) << 4;
1836 }
1837 verts_mask_count += 1;
1838 }
1839 }
1840 else {
1841 /* See the body of the comments in the previous block for details. */
1842 verts_mask.resize(em->bm->totvert);
1843 int i;
1844 TransData *td;
1845 for (i = 0, td = tc->data; i < tc->data_len; i++, td++) {
1846 if (td->factor == 0.0f) {
1847 continue;
1848 }
1849 const BMVert *v = (BMVert *)td->extra;
1850 const int v_index = BM_elem_index_get(v);
1851 BLI_assert(!verts_mask[v_index]);
1852 verts_mask[v_index].set();
1853 verts_mask_count += 1;
1854 }
1855
1856 TransDataMirror *td_mirror = tc->data_mirror;
1857 for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) {
1858 BMVert *v_mirr = (BMVert *)POINTER_OFFSET(td_mirror->loc_src, -offsetof(BMVert, co));
1859 if (!verts_mask[BM_elem_index_get(v_mirr)] && equals_v3v3(td_mirror->loc, td_mirror->iloc)) {
1860 continue;
1861 }
1862
1863 BMVert *v_mirr_other = (BMVert *)td_mirror->extra;
1864 BLI_assert(!verts_mask[BM_elem_index_get(v_mirr_other)]);
1865 const int v_mirr_other_index = BM_elem_index_get(v_mirr_other);
1866 verts_mask[v_mirr_other_index].set();
1867 verts_mask_count += 1;
1868 }
1869 }
1870
1871 switch (partial_type) {
1872 case PARTIAL_TYPE_ALL: {
1874 params.do_tessellate = true;
1875 params.do_normals = true;
1877 *em->bm, params, verts_mask, verts_mask_count);
1878 break;
1879 }
1880 case PARTIAL_TYPE_GROUP: {
1882 params.do_tessellate = true;
1883 params.do_normals = true;
1884 pupdate->cache = (!verts_group.is_empty() ?
1886 *em->bm, params, verts_group, verts_group_count) :
1888 *em->bm, params, verts_mask, verts_mask_count));
1889 break;
1890 }
1891 case PARTIAL_NONE: {
1893 }
1894 }
1895
1896 pupdate->prop_size_prev = t->prop_size;
1897 pupdate->prop_size = t->prop_size;
1898
1899 return pupdate->cache;
1900}
1901
1902static void mesh_partial_types_calc(TransInfo *t, PartialTypeState *r_partial_state)
1903{
1904 /* Calculate the kind of partial updates which can be performed. */
1905 enum ePartialType partial_for_normals = PARTIAL_NONE;
1906 enum ePartialType partial_for_looptris = PARTIAL_NONE;
1907
1908 /* Note that operations such as #TFM_CREASE are not handled here
1909 * (if they were, leaving as #PARTIAL_NONE would be appropriate). */
1910 switch (t->mode) {
1911 case TFM_TRANSLATION: {
1912 partial_for_looptris = PARTIAL_TYPE_GROUP;
1913 partial_for_normals = PARTIAL_TYPE_GROUP;
1914 /* Translation can rotate when snapping to normal. */
1916 partial_for_normals = PARTIAL_TYPE_ALL;
1917 }
1918 break;
1919 }
1920 case TFM_ROTATION: {
1921 partial_for_looptris = PARTIAL_TYPE_GROUP;
1922 partial_for_normals = PARTIAL_TYPE_ALL;
1923 break;
1924 }
1925 case TFM_RESIZE: {
1926 partial_for_looptris = PARTIAL_TYPE_GROUP;
1927 partial_for_normals = PARTIAL_TYPE_GROUP;
1928 /* Non-uniform scale needs to recalculate all normals
1929 * since their relative locations change.
1930 * Uniform negative scale can keep normals as-is since the faces are flipped,
1931 * normals remain unchanged. */
1932 if ((t->con.mode & CON_APPLY) ||
1933 (t->values_final[0] != t->values_final[1] || t->values_final[0] != t->values_final[2]))
1934 {
1935 partial_for_normals = PARTIAL_TYPE_ALL;
1936 }
1937 break;
1938 }
1939 default: {
1940 partial_for_looptris = PARTIAL_TYPE_ALL;
1941 partial_for_normals = PARTIAL_TYPE_ALL;
1942 break;
1943 }
1944 }
1945
1946 /* With projection, transform isn't affine. */
1948 if (partial_for_looptris == PARTIAL_TYPE_GROUP) {
1949 partial_for_looptris = PARTIAL_TYPE_ALL;
1950 }
1951 if (partial_for_normals == PARTIAL_TYPE_GROUP) {
1952 partial_for_normals = PARTIAL_TYPE_ALL;
1953 }
1954 }
1955
1956 r_partial_state->for_looptris = partial_for_looptris;
1957 r_partial_state->for_normals = partial_for_normals;
1958}
1959
1962 const PartialTypeState *partial_state)
1963{
1965
1967
1968 const PartialTypeState *partial_state_prev = &tcmd->partial_update_state_prev;
1969
1970 /* Promote the partial update types based on the previous state
1971 * so the values that no longer modified are reset before being left as-is.
1972 * Needed for translation which can toggle snap-to-normal during transform. */
1973 const enum ePartialType partial_for_looptris = std::max(partial_state->for_looptris,
1974 partial_state_prev->for_looptris);
1975 const enum ePartialType partial_for_normals = std::max(partial_state->for_normals,
1976 partial_state_prev->for_normals);
1977
1978 if ((partial_for_looptris == PARTIAL_TYPE_ALL) && (partial_for_normals == PARTIAL_TYPE_ALL) &&
1979 (em->bm->totvert == em->bm->totvertsel))
1980 {
1981 /* The additional cost of generating the partial connectivity data isn't justified
1982 * when all data needs to be updated.
1983 *
1984 * While proportional editing can cause all geometry to need updating with a partial
1985 * selection. It's impractical to calculate this ahead of time. Further, the down side of
1986 * using partial updates when their not needed is negligible. */
1988 }
1989 else {
1990 if (partial_for_looptris != PARTIAL_NONE) {
1991 BMPartialUpdate *bmpinfo = mesh_partial_ensure(t, tc, partial_for_looptris);
1993 params.face_normals = true;
1995 }
1996
1997 if (partial_for_normals != PARTIAL_NONE) {
1998 BMPartialUpdate *bmpinfo = mesh_partial_ensure(t, tc, partial_for_normals);
1999 /* While not a large difference, take advantage of existing normals where possible. */
2000 const bool face_normals = !((partial_for_looptris == PARTIAL_TYPE_ALL) ||
2001 ((partial_for_looptris == PARTIAL_TYPE_GROUP) &&
2002 (partial_for_normals == PARTIAL_TYPE_GROUP)));
2004 params.face_normals = face_normals;
2006 }
2007 }
2008
2009 /* Store the previous requested (not the previous used),
2010 * since the values used may have been promoted based on the previous types. */
2011 tcmd->partial_update_state_prev = *partial_state;
2012}
2013
2015
2016/* -------------------------------------------------------------------- */
2019
2021{
2022 if (tc->use_mirror_axis_any) {
2023 int i;
2024 TransData *td;
2025 for (i = 0, td = tc->data; i < tc->data_len; i++, td++) {
2027 if (td->flag & TD_MIRROR_EDGE_X) {
2028 td->loc[0] = 0.0f;
2029 }
2030 if (td->flag & TD_MIRROR_EDGE_Y) {
2031 td->loc[1] = 0.0f;
2032 }
2033 if (td->flag & TD_MIRROR_EDGE_Z) {
2034 td->loc[2] = 0.0f;
2035 }
2036 }
2037 }
2038
2039 TransDataMirror *td_mirror = tc->data_mirror;
2040 for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) {
2041 copy_v3_v3(td_mirror->loc, td_mirror->loc_src);
2042 if (td_mirror->flag & TD_MIRROR_X) {
2043 td_mirror->loc[0] *= -1;
2044 }
2045 if (td_mirror->flag & TD_MIRROR_Y) {
2046 td_mirror->loc[1] *= -1;
2047 }
2048 if (td_mirror->flag & TD_MIRROR_Z) {
2049 td_mirror->loc[2] *= -1;
2050 }
2051 }
2052 }
2053}
2054
2056{
2057 bool is_canceling = t->state == TRANS_CANCEL;
2058 /* Apply corrections. */
2059 if (!is_canceling) {
2061
2062 bool do_mirror = !(t->flag & T_NO_MIRROR);
2064 /* Apply clipping after so we never project past the clip plane #25423. */
2066
2067 if (do_mirror) {
2069 }
2070
2072 }
2073 }
2074 else {
2076 }
2077
2078 PartialTypeState partial_state;
2079 mesh_partial_types_calc(t, &partial_state);
2080
2082 DEG_id_tag_update(static_cast<ID *>(tc->obedit->data), ID_RECALC_GEOMETRY);
2083
2084 mesh_partial_update(t, tc, &partial_state);
2085 }
2086}
2087
2089
2090/* -------------------------------------------------------------------- */
2093
2095{
2096 const bool is_canceling = (t->state == TRANS_CANCEL);
2097 const bool use_automerge = !is_canceling && (t->flag & (T_AUTOMERGE | T_AUTOSPLIT)) != 0;
2098
2099 if (!is_canceling && ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
2100 /* NOTE(joeedh): Handle multi-res re-projection,
2101 * done on transform completion since it's really slow. */
2104 }
2105 }
2106
2107 if (use_automerge) {
2109 BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
2110 BMesh *bm = em->bm;
2111 char hflag;
2112 bool has_face_sel = (bm->totfacesel != 0);
2113
2114 if (tc->use_mirror_axis_any) {
2115 /* Rather than adjusting the selection (which the user would notice)
2116 * tag all mirrored verts, then auto-merge those. */
2118
2119 TransDataMirror *td_mirror = tc->data_mirror;
2120 for (int i = tc->data_mirror_len; i--; td_mirror++) {
2122 }
2123
2124 hflag = BM_ELEM_SELECT | BM_ELEM_TAG;
2125 }
2126 else {
2127 hflag = BM_ELEM_SELECT;
2128 }
2129
2130 if (t->flag & T_AUTOSPLIT) {
2132 tc->obedit, true, true, true, hflag, t->scene->toolsettings->doublimit);
2133 }
2134 else {
2135 EDBM_automerge(tc->obedit, true, hflag, t->scene->toolsettings->doublimit);
2136 }
2137
2138 /* Special case, this is needed or faces won't re-select.
2139 * Flush selected edges to faces. */
2140 if (has_face_sel && (em->selectmode == SCE_SELECT_FACE)) {
2142 }
2143 }
2144 }
2145
2147 /* Table needs to be created for each edit command, since vertices can move etc. */
2149 /* TODO(@ideasman42): xform: We need support for many mirror objects at once! */
2150 break;
2151 }
2152}
2153
2155
2156/* -------------------------------------------------------------------- */
2159
2161 const TransDataContainer *tc, Vector<float3> &r_loc_dst_buffer)
2162{
2163 int td_selected_len = 0;
2164 tc->foreach_index_selected([&](const int /*i*/) { td_selected_len++; });
2165
2166 Array<TransDataVertSlideVert> r_sv(td_selected_len);
2167
2168 r_loc_dst_buffer.reserve(r_sv.size() * 4);
2169 int r_sv_index = 0;
2170 tc->foreach_index_selected([&](const int i) {
2171 TransData *td = &tc->data[i];
2172 const int size_prev = r_loc_dst_buffer.size();
2173
2174 BMVert *v = static_cast<BMVert *>(td->extra);
2175 if (!v->e) {
2176 r_loc_dst_buffer.append(td->iloc);
2177 }
2178 else {
2179 BMIter eiter;
2180 BMEdge *e;
2181 BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
2183 continue;
2184 }
2185 BMVert *v_other = BM_edge_other_vert(e, v);
2186 r_loc_dst_buffer.append(v_other->co);
2187 }
2188 }
2189
2190 TransDataVertSlideVert &sv = r_sv[r_sv_index];
2191 sv.td = &tc->data[i];
2192 /* The buffer address may change as the vector is resized. Avoid setting #Span. */
2193 // sv.targets = r_loc_dst_buffer.as_span().drop_front(size_prev);
2194
2195 /* Store the buffer size temporarily in `target_curr`. */
2196 sv.co_link_curr = r_loc_dst_buffer.size() - size_prev;
2197
2198 r_sv_index++;
2199 });
2200
2201 int start = 0;
2202 for (TransDataVertSlideVert &sv : r_sv) {
2203 int size = sv.co_link_curr;
2204 sv.co_link_orig_3d = r_loc_dst_buffer.as_span().slice(start, size);
2205 sv.co_link_curr = 0;
2206 start += size;
2207 }
2208
2209 return r_sv;
2210}
2211
2213
2214/* -------------------------------------------------------------------- */
2217
2219{
2221}
2222
2227static bool bm_loop_calc_opposite_co(const BMLoop *l_tmp, const float plane_no[3], float r_co[3])
2228{
2229 /* Skip adjacent edges. */
2230 BMLoop *l_first = l_tmp->next;
2231 BMLoop *l_last = l_tmp->prev;
2232 BMLoop *l_iter;
2233 float dist_sq_best = FLT_MAX;
2234 bool found = false;
2235
2236 l_iter = l_first;
2237 do {
2238 float tvec[3];
2239 if (isect_line_plane_v3(tvec, l_iter->v->co, l_iter->next->v->co, l_tmp->v->co, plane_no)) {
2240 const float fac = line_point_factor_v3(tvec, l_iter->v->co, l_iter->next->v->co);
2241 /* Allow some overlap to avoid missing the intersection because of float precision. */
2242 if ((fac > -FLT_EPSILON) && (fac < 1.0f + FLT_EPSILON)) {
2243 /* Likelihood of multiple intersections per ngon is quite low,
2244 * it would have to loop back on itself, but better support it
2245 * so check for the closest opposite edge. */
2246 const float dist_sq_test = len_squared_v3v3(l_tmp->v->co, tvec);
2247 if (dist_sq_test < dist_sq_best) {
2248 copy_v3_v3(r_co, tvec);
2249 dist_sq_best = dist_sq_test;
2250 found = true;
2251 }
2252 }
2253 }
2254 } while ((l_iter = l_iter->next) != l_last);
2255
2256 return found;
2257}
2258
2260{
2261 BMFace *f = l->f;
2262 BMLoop *l_next = l->next;
2263 if (f->len == 4) {
2264 /* We could use code below, but in this case
2265 * sliding diagonally across the quad works well. */
2266 return l_next->next->v->co;
2267 }
2268
2269 float3 plane_no;
2270 BM_loop_calc_face_direction(l, plane_no);
2271
2272 float3 isect_co;
2273 if (!bm_loop_calc_opposite_co(l, plane_no, isect_co)) {
2274 /* Rare case. */
2275 mid_v3_v3v3(isect_co, l->prev->v->co, l_next->v->co);
2276 }
2277 return isect_co;
2278}
2279
2281 int *r_group_len)
2282{
2284 BMesh *bm = em->bm;
2285
2286 int td_selected_len = 0;
2287
2288 /* Ensure valid selection. */
2289 BMIter iter;
2290 BMVert *v;
2291 bool found_invalid_edge_selection = false;
2292 tc->foreach_index_selected([&](const int i) {
2293 TransData *td = &tc->data[i];
2294 v = static_cast<BMVert *>(td->extra);
2296 if (numsel == 0 || numsel > 2) {
2297 /* Invalid edge selection. */
2298 found_invalid_edge_selection = true;
2299 return;
2300 }
2301 td_selected_len++;
2302 });
2303
2304 if (found_invalid_edge_selection) {
2305 return {};
2306 }
2307
2308 BMEdge *e;
2309 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
2311 continue;
2312 }
2314 /* Can edges with at least once face user. */
2315 return {};
2316 }
2317 }
2318
2319 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
2320 BM_elem_index_set(v, -1);
2321 }
2322 bm->elem_index_dirty |= BM_VERT;
2323
2324 /* Alloc and initialize the #TransDataEdgeSlideVert. */
2325
2326 Array<TransDataEdgeSlideVert> r_sv(td_selected_len);
2327 TransDataEdgeSlideVert *sv = r_sv.data();
2328 int sv_index = 0;
2329 tc->foreach_index_selected([&](const int i) {
2330 TransData *td = &tc->data[i];
2331
2332 sv->td = td;
2333 sv->loop_nr = -1;
2334 sv->dir_side[0] = float3(0);
2335 sv->dir_side[1] = float3(0);
2336
2337 /* Identify the #TransDataEdgeSlideVert by the vertex index. */
2338 v = static_cast<BMVert *>(td->extra);
2339 BM_elem_index_set(v, sv_index);
2340 sv_index++;
2341 sv++;
2342 });
2343
2344 /* Map indicating the indexes of #TransData connected by edge. */
2345 Array<int2> td_connected(tc->data_len, int2(-1, -1));
2346 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
2348 continue;
2349 }
2350 int td_index_1 = BM_elem_index_get(e->v1);
2351 int td_index_2 = BM_elem_index_get(e->v2);
2352
2353 /* This can occur when the mesh has symmetry enabled but is not symmetrical. See #120811. */
2354 if (ELEM(-1, td_index_1, td_index_2)) {
2355 continue;
2356 }
2357
2358 int slot_1 = int(td_connected[td_index_1][0] != -1);
2359 int slot_2 = int(td_connected[td_index_2][0] != -1);
2360
2361 td_connected[td_index_1][slot_1] = td_index_2;
2362 td_connected[td_index_2][slot_2] = td_index_1;
2363 }
2364
2365 /* Compute the sliding groups. */
2366 int loop_nr = 0;
2367 for (int i : r_sv.index_range()) {
2368 TransDataEdgeSlideVert *sv = &r_sv[i];
2369 if (sv->loop_nr != -1) {
2370 /* This vertex has already been computed. */
2371 continue;
2372 }
2373
2374 /* Start from a vertex connected to just a single edge or any if it doesn't exist. */
2375 int i_curr = i;
2376 int i_prev = td_connected[i][1];
2377 while (!ELEM(i_prev, -1, i)) {
2378 int tmp = td_connected[i_prev][0] != i_curr ? td_connected[i_prev][0] :
2379 td_connected[i_prev][1];
2380 i_curr = i_prev;
2381 i_prev = tmp;
2382 }
2383
2393 struct SlideTempDataMesh {
2394 int i; /* The #TransDataEdgeSlideVert index. */
2396 BMVert *v;
2397 BMEdge *e;
2398 struct {
2399 BMFace *f;
2400 BMVert *v_dst;
2401 float3 dst;
2402 } fdata[2];
2403 bool vert_is_edge_pair;
2412 int find_best_dir(const SlideTempDataMesh *curr_side_other,
2413 const BMFace *f_curr,
2414 const BMLoop *l_src,
2415 const BMVert *v_dst,
2416 bool *r_do_isect_curr_dirs) const
2417 {
2418 *r_do_isect_curr_dirs = false;
2419
2420 if (f_curr == curr_side_other->fdata[0].f || v_dst == curr_side_other->fdata[0].v_dst) {
2421 return 0;
2422 }
2423
2424 if (f_curr == curr_side_other->fdata[1].f || v_dst == curr_side_other->fdata[1].v_dst) {
2425 return 1;
2426 }
2427
2428 if (curr_side_other->fdata[0].f || curr_side_other->fdata[1].f) {
2429 /* Find the best direction checking the edges that share faces between them. */
2430 int best_dir = -1;
2431 const BMLoop *l_edge = l_src->next->v == v_dst ? l_src : l_src->prev;
2432 const BMLoop *l_other = l_edge->radial_next;
2433 while (l_other->f != l_edge->f) {
2434 if (l_other->f == curr_side_other->fdata[0].f) {
2435 best_dir = 0;
2436 break;
2437 }
2438 if (l_other->f == curr_side_other->fdata[1].f) {
2439 best_dir = 1;
2440 break;
2441 }
2442 l_other = (l_other->v == this->v ? l_other->prev : l_other->next)->radial_next;
2443 }
2444
2445 if (best_dir != -1) {
2446 *r_do_isect_curr_dirs = true;
2447 return best_dir;
2448 }
2449 }
2450
2451 if (ELEM(nullptr, this->fdata[0].f, this->fdata[1].f)) {
2452 return int(this->fdata[0].f != nullptr);
2453 }
2454
2455 /* Find the best direction among those already computed.
2456 * Prioritizing in order:
2457 * - Boundary edge that points to the closest direction.
2458 * - Any edge that points to the closest direction. */
2459
2460 *r_do_isect_curr_dirs = true;
2461 BMEdge *e0 = this->fdata[0].v_dst ? BM_edge_exists(this->v, this->fdata[0].v_dst) :
2462 nullptr;
2463 BMEdge *e1 = this->fdata[1].v_dst ? BM_edge_exists(this->v, this->fdata[1].v_dst) :
2464 nullptr;
2465 const bool is_boundary_0 = e0 && BM_edge_is_boundary(e0);
2466 const bool is_boundary_1 = e1 && BM_edge_is_boundary(e1);
2467 if (is_boundary_0 && !is_boundary_1) {
2468 return 0;
2469 }
2470
2471 if (is_boundary_1 && !is_boundary_0) {
2472 return 1;
2473 }
2474
2475 /* Find the closest direction. */
2476 float3 src = this->v->co;
2477 float3 dst = v_dst->co;
2478 float3 dir_curr = dst - src;
2479 float3 dir0 = math::normalize(this->fdata[0].dst - src);
2480 float3 dir1 = math::normalize(this->fdata[1].dst - src);
2481 float dot0 = math::dot(dir_curr, dir0);
2482 float dot1 = math::dot(dir_curr, dir1);
2483 return int(dot0 < dot1);
2484 }
2485 } prev = {}, curr = {}, next = {}, next_next = {}, tmp = {};
2486
2487 next.i = td_connected[i_curr][0] != i_prev ? td_connected[i_curr][0] : td_connected[i_curr][1];
2488 if (next.i != -1) {
2489 next.sv = &r_sv[next.i];
2490 next.v = static_cast<BMVert *>(next.sv->td->extra);
2491 next.vert_is_edge_pair = mesh_vert_is_inner(next.v);
2492 }
2493
2494 curr.i = i_curr;
2495 if (curr.i != -1) {
2496 curr.sv = &r_sv[curr.i];
2497 curr.v = static_cast<BMVert *>(curr.sv->td->extra);
2498 curr.vert_is_edge_pair = mesh_vert_is_inner(curr.v);
2499 if (next.i != -1) {
2500 curr.e = BM_edge_exists(curr.v, next.v);
2501 }
2502 }
2503
2504 /* Do not compute `prev` for now. Let the loop calculate `curr` twice. */
2505 prev.i = -1;
2506
2507 while (curr.i != -1) {
2508 if (next.i != -1) {
2509 next_next.i = td_connected[next.i][0] != curr.i ? td_connected[next.i][0] :
2510 td_connected[next.i][1];
2511 if (next_next.i != -1) {
2512 next_next.sv = &r_sv[next_next.i];
2513 next_next.v = static_cast<BMVert *>(next_next.sv->td->extra);
2514 next_next.vert_is_edge_pair = mesh_vert_is_inner(next_next.v);
2515 next.e = BM_edge_exists(next.v, next_next.v);
2516 }
2517
2518 tmp = curr;
2519
2520 BMLoop *l;
2521 BM_ITER_ELEM (l, &iter, curr.e, BM_LOOPS_OF_EDGE) {
2522 BMFace *f_curr = l->f;
2523
2524 BMVert *v1_dst, *v2_dst;
2525 BMEdge *l_edge_next;
2526 BMLoop *l1, *l2;
2527 if (l->v == curr.v) {
2528 l1 = l;
2529 l2 = l->next;
2530 l_edge_next = l2->e;
2531 v1_dst = l1->prev->v;
2532 v2_dst = l2->next->v;
2533 }
2534 else {
2535 l1 = l->next;
2536 l2 = l;
2537 l_edge_next = l2->prev->e;
2538 v1_dst = l1->next->v;
2539 v2_dst = l2->prev->v;
2540 }
2541
2542 float3 dst = v1_dst->co;
2543
2544 /* Sometimes the sliding direction may fork (`isect_curr_dirs` is `true`).
2545 * In this case, the resulting direction is the intersection of the destinations. */
2546 bool isect_curr_dirs = false;
2547
2548 /* Identify the slot to slide according to the directions already computed in `curr`. */
2549 int best_dir = curr.find_best_dir(&tmp, f_curr, l1, v1_dst, &isect_curr_dirs);
2550
2551 if (curr.fdata[best_dir].f == nullptr) {
2552 curr.fdata[best_dir].f = f_curr;
2553 if (curr.vert_is_edge_pair) {
2554 curr.fdata[best_dir].dst = isect_face_dst(l1);
2555 }
2556 else {
2557 curr.fdata[best_dir].v_dst = v1_dst;
2558 curr.fdata[best_dir].dst = v1_dst->co;
2559 }
2560 }
2561
2562 /* Compute `next`. */
2563 next.fdata[best_dir].f = f_curr;
2564 if (l_edge_next == next.e || next.vert_is_edge_pair) {
2565 /* Case where the vertex slides over the face. */
2566 next.fdata[best_dir].v_dst = nullptr;
2567 next.fdata[best_dir].dst = isect_face_dst(l2);
2568 }
2569 else {
2570 /* Case where the vertex slides over an edge. */
2571 next.fdata[best_dir].v_dst = v2_dst;
2572 next.fdata[best_dir].dst = v2_dst->co;
2573 }
2574
2575 if (isect_curr_dirs) {
2576 /* The `best_dir` can only have one direction. */
2577 const float *curr_orig = curr.sv->v_co_orig();
2578 const float3 &dst0 = prev.fdata[best_dir].dst;
2579 const float3 &dst1 = curr.fdata[best_dir].dst;
2580 const float3 &dst2 = dst;
2581 const float3 &dst3 = next.fdata[best_dir].dst;
2582 float3 isect_pair[2];
2583
2649
2650 const float isect_eps = FLT_EPSILON;
2651 int isect_line_line = isect_line_line_epsilon_v3(
2652 dst0, dst1, dst2, dst3, isect_pair[0], isect_pair[1], isect_eps);
2653
2654 if (isect_line_line != 0) {
2655 /* Check if the intersections are outside the "valid conical region". */
2656 BLI_assert(isect_line_line <= 2);
2657 const float3 dir1 = math::normalize(dst1 - float3(curr_orig));
2658 const float3 dir2 = math::normalize(dst2 - float3(curr_orig));
2659 float len_n;
2660 const float3 n = math::normalize_and_get_length(math::cross(dir1, dir2), len_n);
2661 if (UNLIKELY(len_n < isect_eps)) {
2662 isect_line_line = 0;
2663 }
2664 else {
2665 float len1, len2;
2666 const float3 plane_no_1 = math::normalize_and_get_length(math::cross(n, dir1),
2667 len1);
2668 const float3 plane_no_2 = math::normalize_and_get_length(math::cross(dir2, n),
2669 len2);
2670
2671 if (UNLIKELY((len1 < isect_eps) || (len2 < isect_eps))) {
2672 isect_line_line = 0;
2673 }
2674 else {
2675 for (int isect_pass = 0; isect_pass < isect_line_line; isect_pass++) {
2676 const float3 isect_co = isect_pair[isect_pass] - float3(curr_orig);
2677 if ((math::dot(isect_co, plane_no_1) <= 0.0f) ||
2678 (math::dot(isect_co, plane_no_2) <= 0.0f))
2679 {
2680 /* Outside the plane, ignore. */
2681 isect_line_line = 0;
2682 break;
2683 }
2684 }
2685 }
2686 }
2687 }
2688
2689 if (isect_line_line != 0) {
2690 curr.fdata[best_dir].dst = math::midpoint(isect_pair[0], isect_pair[1]);
2691 }
2692 else {
2693 curr.fdata[best_dir].dst = math::midpoint(dst1, dst2);
2694 }
2695 }
2696 }
2697 }
2698
2699 /* The data in `curr` is computed. Use to compute the #TransDataEdgeSlideVert. */
2700 float3 iloc = curr.sv->td->iloc;
2701 if (curr.fdata[0].f) {
2702 curr.sv->dir_side[0] = curr.fdata[0].dst - iloc;
2703 }
2704 if (curr.fdata[1].f) {
2705 curr.sv->dir_side[1] = curr.fdata[1].dst - iloc;
2706 }
2707 curr.sv->edge_len = math::distance(curr.sv->dir_side[0], curr.sv->dir_side[1]);
2708 curr.sv->loop_nr = loop_nr;
2709
2710 if (i_prev != -1 && prev.i == i_prev) {
2711 /* Cycle returned to the beginning.
2712 * The data with index `i_curr` was computed twice to make sure the directions are correct
2713 * the second time. */
2714 break;
2715 }
2716
2717 /* Move forward. */
2718 prev = curr;
2719 curr = next;
2720 next = next_next;
2721 }
2722 loop_nr++;
2723 }
2724 *r_group_len = loop_nr;
2725 return r_sv;
2726}
2727
2729
2731 /*flags*/ (T_EDIT | T_POINTS),
2732 /*create_trans_data*/ createTransEditVerts,
2733 /*recalc_data*/ recalcData_mesh,
2734 /*special_aftertrans_update*/ special_aftertrans_update__mesh,
2735};
2736
2737} // 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:2623
#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:982
@ ME_EDIT_MIRROR_TOPO
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
@ SCE_SNAP_ROTATE
@ UVCALC_TRANSFORM_CORRECT_SLIDE
@ UVCALC_TRANSFORM_CORRECT
@ UVCALC_TRANSFORM_CORRECT_KEEP_CONNECTED
@ V3D_AROUND_LOCAL_ORIGINS
void EDBM_automerge_and_split(Object *obedit, bool split_edges, bool split_faces, bool update, char hflag, float dist)
void 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 BM_FACE_FIRST_LOOP(p)
@ BM_LOOP
@ BM_ELEM_HIDDEN
@ BM_ELEM_SELECT
@ BM_ELEM_TAG
@ BM_ELEM_TAG_ALT
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:245
int64_t size() const
Definition BLI_array.hh:245
const T * data() const
Definition BLI_array.hh:301
IndexRange index_range() const
Definition BLI_array.hh:349
bool is_empty() const
Definition BLI_array.hh:253
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)
#define offsetof(t, d)
#define fabsf(x)
#define MEM_SAFE_FREE(v)
#define MEM_reallocN(vmemh, len)
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 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:404
struct LinkNode * next
struct ToolSettings * toolsettings
struct blender::ed::transform::TransCustomDataLayer::@227111341216221011006223277275277165133033277207 merge_group
TransCustomData_PartialUpdate partial_update[PARTIAL_TYPE_MAX]
void(* free_cb)(TransInfo *, TransDataContainer *tc, TransCustomData *custom_data)
Definition transform.hh:628
void foreach_index_selected(FunctionRef< void(int)> fn) const
Definition transform.hh:780
i
Definition text_draw.cc:230
#define TD_MIRROR_EDGE_AXIS_SHIFT
Definition transform.hh:375
#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:139