Welcome to mirror list, hosted at ThFree Co, Russian Federation.

bmo_connect_concave.c « operators « bmesh « blender « source - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: bc1111676a39430bf790daac7ce2c2147240c565 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

/** \file
 * \ingroup bmesh
 *
 * Connect vertices so all resulting faces are convex.
 *
 * Implementation:
 *
 * - triangulate all concave face (tagging convex verts),
 * - rotate edges (beautify) so edges will connect nearby verts.
 * - sort long edges (longest first),
 *   put any edges between 2 convex verts last since they often split convex regions.
 * - merge the sorted edges as long as they don't create convex ngons.
 */

#include "MEM_guardedalloc.h"

#include "BLI_alloca.h"
#include "BLI_heap.h"
#include "BLI_linklist.h"
#include "BLI_math.h"
#include "BLI_memarena.h"
#include "BLI_polyfill_2d.h"
#include "BLI_polyfill_2d_beautify.h"
#include "BLI_utildefines.h"

#include "bmesh.h"

#include "intern/bmesh_operators_private.h" /* own include */

#define EDGE_OUT (1 << 0)
#define FACE_OUT (1 << 1)

static int bm_edge_length_cmp(const void *a_, const void *b_)
{
  const BMEdge *e_a = *(const void **)a_;
  const BMEdge *e_b = *(const void **)b_;

  int e_a_concave = ((BM_elem_flag_test(e_a->v1, BM_ELEM_TAG)) &&
                     (BM_elem_flag_test(e_a->v2, BM_ELEM_TAG)));
  int e_b_concave = ((BM_elem_flag_test(e_b->v1, BM_ELEM_TAG)) &&
                     (BM_elem_flag_test(e_b->v2, BM_ELEM_TAG)));

  /* merge edges between concave edges last since these
   * are most likely to remain and be the main dividers */
  if (e_a_concave < e_b_concave) {
    return -1;
  }
  if (e_a_concave > e_b_concave) {
    return 1;
  }

  /* otherwise shortest edges last */
  const float e_a_len = BM_edge_calc_length_squared(e_a);
  const float e_b_len = BM_edge_calc_length_squared(e_b);
  if (e_a_len < e_b_len) {
    return 1;
  }
  if (e_a_len > e_b_len) {
    return -1;
  }
  return 0;
}

static bool bm_face_split_by_concave(BMesh *bm,
                                     BMFace *f_base,
                                     const float eps,

                                     MemArena *pf_arena,
                                     struct Heap *pf_heap)
{
  const int f_base_len = f_base->len;
  int faces_array_tot = f_base_len - 3;
  int edges_array_tot = f_base_len - 3;
  BMFace **faces_array = BLI_array_alloca(faces_array, faces_array_tot);
  BMEdge **edges_array = BLI_array_alloca(edges_array, edges_array_tot);
  const int quad_method = 0, ngon_method = 0; /* beauty */
  LinkNode *faces_double = NULL;

  float normal[3];
  BLI_assert(f_base->len > 3);

  copy_v3_v3(normal, f_base->no);

  BM_face_triangulate(bm,
                      f_base,
                      faces_array,
                      &faces_array_tot,
                      edges_array,
                      &edges_array_tot,
                      &faces_double,
                      quad_method,
                      ngon_method,
                      false,
                      pf_arena,
                      pf_heap);

  BLI_assert(edges_array_tot <= f_base_len - 3);

  if (faces_array_tot) {
    int i;
    for (i = 0; i < faces_array_tot; i++) {
      BMFace *f = faces_array[i];
      BMO_face_flag_enable(bm, f, FACE_OUT);
    }
  }
  BMO_face_flag_enable(bm, f_base, FACE_OUT);

  if (edges_array_tot) {
    int i;

    qsort(edges_array, edges_array_tot, sizeof(*edges_array), bm_edge_length_cmp);

    for (i = 0; i < edges_array_tot; i++) {
      BMLoop *l_pair[2];
      BMEdge *e = edges_array[i];
      BMO_edge_flag_enable(bm, e, EDGE_OUT);

      if (BM_edge_is_contiguous(e) && BM_edge_loop_pair(e, &l_pair[0], &l_pair[1])) {
        bool ok = true;
        int j;
        for (j = 0; j < 2; j++) {
          BMLoop *l = l_pair[j];

          /* check that merging the edge (on this side)
           * wouldn't result in a convex face-loop.
           *
           * This is the (l->next, l->prev) we would have once joined.
           */
          float cross[3];
          cross_tri_v3(cross, l->v->co, l->radial_next->next->next->v->co, l->prev->v->co);

          if (dot_v3v3(cross, normal) <= eps) {
            ok = false;
            break;
          }
        }

        if (ok) {
          BMFace *f_new, *f_pair[2] = {l_pair[0]->f, l_pair[1]->f};
          f_new = BM_faces_join(bm, f_pair, 2, true);
          if (f_new) {
            BMO_face_flag_enable(bm, f_new, FACE_OUT);
          }
        }
      }
    }
  }

  BLI_heap_clear(pf_heap, NULL);

  while (faces_double) {
    LinkNode *next = faces_double->next;
    BM_face_kill(bm, faces_double->link);
    MEM_freeN(faces_double);
    faces_double = next;
  }

  return true;
}

static bool bm_face_convex_tag_verts(BMFace *f)
{
  bool is_concave = false;
  if (f->len > 3) {
    const BMLoop *l_iter, *l_first;

    l_iter = l_first = BM_FACE_FIRST_LOOP(f);
    do {
      if (BM_loop_is_convex(l_iter) == false) {
        is_concave = true;
        BM_elem_flag_enable(l_iter->v, BM_ELEM_TAG);
      }
      else {
        BM_elem_flag_disable(l_iter->v, BM_ELEM_TAG);
      }
    } while ((l_iter = l_iter->next) != l_first);
  }
  return is_concave;
}

void bmo_connect_verts_concave_exec(BMesh *bm, BMOperator *op)
{
  BMOIter siter;
  BMFace *f;
  bool changed = false;

  MemArena *pf_arena;
  Heap *pf_heap;

  pf_arena = BLI_memarena_new(BLI_POLYFILL_ARENA_SIZE, __func__);
  pf_heap = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE);

  BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) {
    if (f->len > 3 && bm_face_convex_tag_verts(f)) {
      if (bm_face_split_by_concave(bm, f, FLT_EPSILON, pf_arena, pf_heap)) {
        changed = true;
      }
    }
  }

  if (changed) {
    BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "edges.out", BM_EDGE, EDGE_OUT);
    BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "faces.out", BM_FACE, FACE_OUT);
  }

  BLI_memarena_free(pf_arena);
  BLI_heap_free(pf_heap, NULL);
}