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