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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2012-02-19 22:31:04 +0400
committerCampbell Barton <ideasman42@gmail.com>2012-02-19 22:31:04 +0400
commitafc56a0b10b52d2c8bdfd44257624d776db72f79 (patch)
tree25d194e29b2708ac1143b3dde6dfbeec7ef1d876 /source/blender/bmesh/operators
parentd6deca4e9d6bc7faff98644286571c7934324a9d (diff)
copying bmesh dir on its own from bmesh branch
Diffstat (limited to 'source/blender/bmesh/operators')
-rw-r--r--source/blender/bmesh/operators/bmo_bevel.c881
-rw-r--r--source/blender/bmesh/operators/bmo_connect.c414
-rw-r--r--source/blender/bmesh/operators/bmo_create.c1412
-rw-r--r--source/blender/bmesh/operators/bmo_dissolve.c559
-rw-r--r--source/blender/bmesh/operators/bmo_dupe.c512
-rw-r--r--source/blender/bmesh/operators/bmo_edgesplit.c426
-rw-r--r--source/blender/bmesh/operators/bmo_extrude.c591
-rw-r--r--source/blender/bmesh/operators/bmo_join_triangles.c373
-rw-r--r--source/blender/bmesh/operators/bmo_mesh_conv.c906
-rw-r--r--source/blender/bmesh/operators/bmo_mirror.c126
-rw-r--r--source/blender/bmesh/operators/bmo_primitive.c734
-rw-r--r--source/blender/bmesh/operators/bmo_removedoubles.c590
-rw-r--r--source/blender/bmesh/operators/bmo_subdivide.c1104
-rw-r--r--source/blender/bmesh/operators/bmo_subdivide.h66
-rw-r--r--source/blender/bmesh/operators/bmo_triangulate.c219
-rw-r--r--source/blender/bmesh/operators/bmo_utils.c1297
16 files changed, 10210 insertions, 0 deletions
diff --git a/source/blender/bmesh/operators/bmo_bevel.c b/source/blender/bmesh/operators/bmo_bevel.c
new file mode 100644
index 00000000000..98e9a510126
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_bevel.c
@@ -0,0 +1,881 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Joseph Eagar.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_array.h"
+#include "BLI_math.h"
+#include "BLI_smallhash.h"
+
+#include "BKE_customdata.h"
+
+#include "bmesh.h"
+
+#define BEVEL_FLAG 1
+#define BEVEL_DEL 2
+#define FACE_NEW 4
+#define EDGE_OLD 8
+#define FACE_OLD 16
+#define VERT_OLD 32
+#define FACE_SPAN 64
+#define FACE_HOLE 128
+
+typedef struct LoopTag {
+ BMVert *newv;
+} LoopTag;
+
+typedef struct EdgeTag {
+ BMVert *newv1, *newv2;
+} EdgeTag;
+
+static void calc_corner_co(BMesh *bm, BMLoop *l, const float fac, float r_co[3],
+ const short do_dist, const short do_even)
+{
+ float no[3], l_vec_prev[3], l_vec_next[3], l_co_prev[3], l_co[3], l_co_next[3], co_ofs[3];
+ int is_concave;
+
+ /* first get the prev/next verts */
+ if (l->f->len > 2) {
+ copy_v3_v3(l_co_prev, l->prev->v->co);
+ copy_v3_v3(l_co, l->v->co);
+ copy_v3_v3(l_co_next, l->next->v->co);
+
+ /* calculate norma */
+ sub_v3_v3v3(l_vec_prev, l_co_prev, l_co);
+ sub_v3_v3v3(l_vec_next, l_co_next, l_co);
+
+ cross_v3_v3v3(no, l_vec_prev, l_vec_next);
+ is_concave = dot_v3v3(no, l->f->no) > 0.0f;
+ }
+ else {
+ BMIter iter;
+ BMLoop *l2;
+ float up[3] = {0.0f, 0.0f, 1.0f};
+
+ copy_v3_v3(l_co_prev, l->prev->v->co);
+ copy_v3_v3(l_co, l->v->co);
+
+ BM_ITER(l2, &iter, bm, BM_LOOPS_OF_VERT, l->v) {
+ if (l2->f != l->f) {
+ copy_v3_v3(l_co_next, BM_edge_other_vert(l2->e, l2->next->v)->co);
+ break;
+ }
+ }
+
+ sub_v3_v3v3(l_vec_prev, l_co_prev, l_co);
+ sub_v3_v3v3(l_vec_next, l_co_next, l_co);
+
+ cross_v3_v3v3(no, l_vec_prev, l_vec_next);
+ if (dot_v3v3(no, no) == 0.0f) {
+ no[0] = no[1] = 0.0f; no[2] = -1.0f;
+ }
+
+ is_concave = dot_v3v3(no, up) < 0.0f;
+ }
+
+
+ /* now calculate the new location */
+ if (do_dist) { /* treat 'fac' as distance */
+
+ normalize_v3(l_vec_prev);
+ normalize_v3(l_vec_next);
+
+ add_v3_v3v3(co_ofs, l_vec_prev, l_vec_next);
+ if (UNLIKELY(normalize_v3(co_ofs) == 0.0f)) { /* edges form a straignt line */
+ cross_v3_v3v3(co_ofs, l_vec_prev, l->f->no);
+ }
+
+ if (do_even) {
+ negate_v3(l_vec_next);
+ mul_v3_fl(co_ofs, fac * shell_angle_to_dist(0.5f * angle_normalized_v3v3(l_vec_prev, l_vec_next)));
+ /* negate_v3(l_vec_next); */ /* no need unless we use again */
+ }
+ else {
+ mul_v3_fl(co_ofs, fac);
+ }
+ }
+ else { /* treat as 'fac' as a factor (0 - 1) */
+
+ /* not strictly necessary, balance vectors
+ * so the longer edge doesn't skew the result,
+ * gives nicer, move even output.
+ *
+ * Use the minimum rather then the middle value so skinny faces don't flip along the short axis */
+ float min_fac = minf(normalize_v3(l_vec_prev), normalize_v3(l_vec_next));
+ float angle;
+
+ if (do_even) {
+ negate_v3(l_vec_next);
+ angle = angle_normalized_v3v3(l_vec_prev, l_vec_next);
+ negate_v3(l_vec_next); /* no need unless we use again */
+ }
+ else {
+ angle = 0.0f;
+ }
+
+ mul_v3_fl(l_vec_prev, min_fac);
+ mul_v3_fl(l_vec_next, min_fac);
+
+ add_v3_v3v3(co_ofs, l_vec_prev, l_vec_next);
+
+ if (UNLIKELY(is_zero_v3(co_ofs))) {
+ cross_v3_v3v3(co_ofs, l_vec_prev, l->f->no);
+ normalize_v3(co_ofs);
+ mul_v3_fl(co_ofs, min_fac);
+ }
+
+ /* done */
+ if (do_even) {
+ mul_v3_fl(co_ofs, (fac * 0.5) * shell_angle_to_dist(0.5f * angle));
+ }
+ else {
+ mul_v3_fl(co_ofs, fac * 0.5);
+ }
+ }
+
+ /* apply delta vec */
+ if (is_concave)
+ negate_v3(co_ofs);
+
+ add_v3_v3v3(r_co, co_ofs, l->v->co);
+}
+
+
+#define ETAG_SET(e, v, nv) ( \
+ (v) == (e)->v1 ? \
+ (etags[BM_elem_index_get((e))].newv1 = (nv)) : \
+ (etags[BM_elem_index_get((e))].newv2 = (nv)) \
+ )
+
+#define ETAG_GET(e, v) ( \
+ (v) == (e)->v1 ? \
+ (etags[BM_elem_index_get((e))].newv1) : \
+ (etags[BM_elem_index_get((e))].newv2) \
+ )
+
+void bmesh_bevel_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMIter iter;
+ BMEdge *e;
+ BMVert *v;
+ BMFace **faces = NULL, *f;
+ LoopTag *tags = NULL, *tag;
+ EdgeTag *etags = NULL;
+ BMVert **verts = NULL;
+ BMEdge **edges = NULL;
+ BLI_array_declare(faces);
+ BLI_array_declare(tags);
+ BLI_array_declare(etags);
+ BLI_array_declare(verts);
+ BLI_array_declare(edges);
+ SmallHash hash;
+ float fac = BMO_slot_float_get(op, "percent");
+ const short do_even = BMO_slot_int_get(op, "use_even");
+ const short do_dist = BMO_slot_int_get(op, "use_dist");
+ int i, li, has_elens, HasMDisps = CustomData_has_layer(&bm->ldata, CD_MDISPS);
+
+ has_elens = CustomData_has_layer(&bm->edata, CD_PROP_FLT) && BMO_slot_int_get(op, "use_lengths");
+ if (has_elens) {
+ li = BMO_slot_int_get(op, "lengthlayer");
+ }
+
+ BLI_smallhash_init(&hash);
+
+ BMO_ITER(e, &siter, bm, op, "geom", BM_EDGE) {
+ BMO_elem_flag_enable(bm, e, BEVEL_FLAG|BEVEL_DEL);
+ BMO_elem_flag_enable(bm, e->v1, BEVEL_FLAG|BEVEL_DEL);
+ BMO_elem_flag_enable(bm, e->v2, BEVEL_FLAG|BEVEL_DEL);
+
+ if (BM_edge_face_count(e) < 2) {
+ BMO_elem_flag_disable(bm, e, BEVEL_DEL);
+ BMO_elem_flag_disable(bm, e->v1, BEVEL_DEL);
+ BMO_elem_flag_disable(bm, e->v2, BEVEL_DEL);
+ }
+#if 0
+ if (BM_edge_face_count(e) == 0) {
+ BMVert *verts[2] = {e->v1, e->v2};
+ BMEdge *edges[2] = {e, BM_edge_create(bm, e->v1, e->v2, e, 0)};
+
+ BMO_elem_flag_enable(bm, edges[1], BEVEL_FLAG);
+ BM_face_create(bm, verts, edges, 2, FALSE);
+ }
+#endif
+ }
+
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ BMO_elem_flag_enable(bm, v, VERT_OLD);
+ }
+
+#if 0
+ //a bit of cleaner code that, alas, doens't work.
+ /* build edge tag */
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, e->v1, BEVEL_FLAG) || BMO_elem_flag_test(bm, e->v2, BEVEL_FLAG)) {
+ BMIter liter;
+ BMLoop *l;
+
+ if (!BMO_elem_flag_test(bm, e, EDGE_OLD)) {
+ BM_elem_index_set(e, BLI_array_count(etags)); /* set_dirty! */
+ BLI_array_growone(etags);
+
+ BMO_elem_flag_enable(bm, e, EDGE_OLD);
+ }
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_EDGE, e) {
+ BMLoop *l2;
+ BMIter liter2;
+
+ if (BMO_elem_flag_test(bm, l->f, BEVEL_FLAG))
+ continue;
+
+ BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_FACE, l->f) {
+ BM_elem_index_set(l2, BLI_array_count(tags)); /* set_loop */
+ BLI_array_growone(tags);
+
+ if (!BMO_elem_flag_test(bm, l2->e, EDGE_OLD)) {
+ BM_elem_index_set(l2->e, BLI_array_count(etags)); /* set_dirty! */
+ BLI_array_growone(etags);
+
+ BMO_elem_flag_enable(bm, l2->e, EDGE_OLD);
+ }
+ }
+
+ BMO_elem_flag_enable(bm, l->f, BEVEL_FLAG);
+ BLI_array_append(faces, l->f);
+ }
+ }
+ else {
+ BM_elem_index_set(e, -1); /* set_dirty! */
+ }
+ }
+#endif
+
+ /* create and assign looptag structure */
+ BMO_ITER(e, &siter, bm, op, "geom", BM_EDGE) {
+ BMLoop *l;
+ BMIter liter;
+
+ BMO_elem_flag_enable(bm, e->v1, BEVEL_FLAG|BEVEL_DEL);
+ BMO_elem_flag_enable(bm, e->v2, BEVEL_FLAG|BEVEL_DEL);
+
+ if (BM_edge_face_count(e) < 2) {
+ BMO_elem_flag_disable(bm, e, BEVEL_DEL);
+ BMO_elem_flag_disable(bm, e->v1, BEVEL_DEL);
+ BMO_elem_flag_disable(bm, e->v2, BEVEL_DEL);
+ }
+
+ if (!BLI_smallhash_haskey(&hash, (intptr_t)e)) {
+ BLI_array_growone(etags);
+ BM_elem_index_set(e, BLI_array_count(etags) - 1); /* set_dirty! */
+ BLI_smallhash_insert(&hash, (intptr_t)e, NULL);
+ BMO_elem_flag_enable(bm, e, EDGE_OLD);
+ }
+
+ /* find all faces surrounding e->v1 and, e->v2 */
+ for (i = 0; i < 2; i++) {
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, i ? e->v2:e->v1) {
+ BMLoop *l2;
+ BMIter liter2;
+
+ /* see if we've already processed this loop's fac */
+ if (BLI_smallhash_haskey(&hash, (intptr_t)l->f))
+ continue;
+
+ /* create tags for all loops in l-> */
+ BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_FACE, l->f) {
+ BLI_array_growone(tags);
+ BM_elem_index_set(l2, BLI_array_count(tags) - 1); /* set_loop */
+
+ if (!BLI_smallhash_haskey(&hash, (intptr_t)l2->e)) {
+ BLI_array_growone(etags);
+ BM_elem_index_set(l2->e, BLI_array_count(etags) - 1); /* set_dirty! */
+ BLI_smallhash_insert(&hash, (intptr_t)l2->e, NULL);
+ BMO_elem_flag_enable(bm, l2->e, EDGE_OLD);
+ }
+ }
+
+ BLI_smallhash_insert(&hash, (intptr_t)l->f, NULL);
+ BMO_elem_flag_enable(bm, l->f, BEVEL_FLAG);
+ BLI_array_append(faces, l->f);
+ }
+ }
+ }
+
+ bm->elem_index_dirty |= BM_EDGE;
+
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ BMIter eiter;
+
+ if (!BMO_elem_flag_test(bm, v, BEVEL_FLAG))
+ continue;
+
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
+ if (!BMO_elem_flag_test(bm, e, BEVEL_FLAG) && !ETAG_GET(e, v)) {
+ BMVert *v2;
+ float co[3];
+
+ v2 = BM_edge_other_vert(e, v);
+ sub_v3_v3v3(co, v2->co, v->co);
+ if (has_elens) {
+ float elen = *(float *)CustomData_bmesh_get_n(&bm->edata, e->head.data, CD_PROP_FLT, li);
+
+ normalize_v3(co);
+ mul_v3_fl(co, elen);
+ }
+
+ mul_v3_fl(co, fac);
+ add_v3_v3(co, v->co);
+
+ v2 = BM_vert_create(bm, co, v);
+ ETAG_SET(e, v, v2);
+ }
+ }
+ }
+
+ for (i = 0; i < BLI_array_count(faces); i++) {
+ BMLoop *l;
+ BMIter liter;
+
+ BMO_elem_flag_enable(bm, faces[i], FACE_OLD);
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, faces[i]) {
+ float co[3];
+
+ if (BMO_elem_flag_test(bm, l->e, BEVEL_FLAG)) {
+ if (BMO_elem_flag_test(bm, l->prev->e, BEVEL_FLAG))
+ {
+ tag = tags + BM_elem_index_get(l);
+ calc_corner_co(bm, l, fac, co, do_dist, do_even);
+ tag->newv = BM_vert_create(bm, co, l->v);
+ }
+ else {
+ tag = tags + BM_elem_index_get(l);
+ tag->newv = ETAG_GET(l->prev->e, l->v);
+
+ if (!tag->newv) {
+ sub_v3_v3v3(co, l->prev->v->co, l->v->co);
+ if (has_elens) {
+ float elen = *(float *)CustomData_bmesh_get_n(&bm->edata, l->prev->e->head.data,
+ CD_PROP_FLT, li);
+
+ normalize_v3(co);
+ mul_v3_fl(co, elen);
+ }
+
+ mul_v3_fl(co, fac);
+ add_v3_v3(co, l->v->co);
+
+ tag->newv = BM_vert_create(bm, co, l->v);
+
+ ETAG_SET(l->prev->e, l->v, tag->newv);
+ }
+ }
+ }
+ else if (BMO_elem_flag_test(bm, l->v, BEVEL_FLAG)) {
+ tag = tags + BM_elem_index_get(l);
+ tag->newv = ETAG_GET(l->e, l->v);
+
+ if (!tag->newv) {
+ sub_v3_v3v3(co, l->next->v->co, l->v->co);
+ if (has_elens) {
+ float elen = *(float *)CustomData_bmesh_get_n(&bm->edata, l->e->head.data, CD_PROP_FLT, li);
+
+ normalize_v3(co);
+ mul_v3_fl(co, elen);
+ }
+
+ mul_v3_fl(co, fac);
+ add_v3_v3(co, l->v->co);
+
+ tag = tags + BM_elem_index_get(l);
+ tag->newv = BM_vert_create(bm, co, l->v);
+
+ ETAG_SET(l->e, l->v, tag->newv);
+ }
+ }
+ else {
+ tag = tags + BM_elem_index_get(l);
+ tag->newv = l->v;
+ BMO_elem_flag_disable(bm, l->v, BEVEL_DEL);
+ }
+ }
+ }
+
+ /* create new faces inset from original face */
+ for (i = 0; i < BLI_array_count(faces); i++) {
+ BMLoop *l;
+ BMIter liter;
+ BMFace *f;
+ BMVert *lastv = NULL, *firstv = NULL;
+
+ BMO_elem_flag_enable(bm, faces[i], BEVEL_DEL);
+
+ BLI_array_empty(verts);
+ BLI_array_empty(edges);
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, faces[i]) {
+ BMVert *v2;
+
+ tag = tags + BM_elem_index_get(l);
+ BLI_array_append(verts, tag->newv);
+
+ if (!firstv)
+ firstv = tag->newv;
+
+ if (lastv) {
+ e = BM_edge_create(bm, lastv, tag->newv, l->e, TRUE);
+ BM_elem_attrs_copy(bm, bm, l->prev->e, e);
+ BLI_array_append(edges, e);
+ }
+ lastv = tag->newv;
+
+ v2 = ETAG_GET(l->e, l->next->v);
+
+ tag = &tags[BM_elem_index_get(l->next)];
+ if (!BMO_elem_flag_test(bm, l->e, BEVEL_FLAG) && v2 && v2 != tag->newv) {
+ BLI_array_append(verts, v2);
+
+ e = BM_edge_create(bm, lastv, v2, l->e, TRUE);
+ BM_elem_attrs_copy(bm, bm, l->e, e);
+
+ BLI_array_append(edges, e);
+ lastv = v2;
+ }
+ }
+
+ e = BM_edge_create(bm, firstv, lastv, BM_FACE_FIRST_LOOP(faces[i])->e, TRUE);
+ if (BM_FACE_FIRST_LOOP(faces[i])->prev->e != e) {
+ BM_elem_attrs_copy(bm, bm, BM_FACE_FIRST_LOOP(faces[i])->prev->e, e);
+ }
+ BLI_array_append(edges, e);
+
+ f = BM_face_create_ngon(bm, verts[0], verts[1], edges, BLI_array_count(edges), FALSE);
+ if (!f) {
+ printf("%s: could not make face!\n", __func__);
+ continue;
+ }
+
+ BMO_elem_flag_enable(bm, f, FACE_NEW);
+ }
+
+ for (i = 0; i < BLI_array_count(faces); i++) {
+ BMLoop *l;
+ BMIter liter;
+ int j;
+
+ /* create quad spans between split edge */
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, faces[i]) {
+ BMVert *v1 = NULL, *v2 = NULL, *v3 = NULL, *v4 = NULL;
+
+ if (!BMO_elem_flag_test(bm, l->e, BEVEL_FLAG))
+ continue;
+
+ v1 = tags[BM_elem_index_get(l)].newv;
+ v2 = tags[BM_elem_index_get(l->next)].newv;
+ if (l->radial_next != l) {
+ v3 = tags[BM_elem_index_get(l->radial_next)].newv;
+ if (l->radial_next->next->v == l->next->v) {
+ v4 = v3;
+ v3 = tags[BM_elem_index_get(l->radial_next->next)].newv;
+ }
+ else {
+ v4 = tags[BM_elem_index_get(l->radial_next->next)].newv;
+ }
+ }
+ else {
+ /* the loop is on a boundar */
+ v3 = l->next->v;
+ v4 = l->v;
+
+ for (j = 0; j < 2; j++) {
+ BMIter eiter;
+ BMVert *v = j ? v4 : v3;
+
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
+ if (!BM_vert_in_edge(e, v3) || !BM_vert_in_edge(e, v4))
+ continue;
+
+ if (!BMO_elem_flag_test(bm, e, BEVEL_FLAG) && BMO_elem_flag_test(bm, e, EDGE_OLD)) {
+ BMVert *vv;
+
+ vv = ETAG_GET(e, v);
+ if (!vv || BMO_elem_flag_test(bm, vv, BEVEL_FLAG))
+ continue;
+
+ if (j)
+ v1 = vv;
+ else
+ v2 = vv;
+ break;
+ }
+ }
+ }
+
+ BMO_elem_flag_disable(bm, v3, BEVEL_DEL);
+ BMO_elem_flag_disable(bm, v4, BEVEL_DEL);
+ }
+
+ if (v1 != v2 && v2 != v3 && v3 != v4) {
+ BMIter liter2;
+ BMLoop *l2, *l3;
+ BMEdge *e1, *e2;
+ float d1, d2, *d3;
+
+ f = BM_face_create_quad_tri(bm, v4, v3, v2, v1, l->f, TRUE);
+
+ e1 = BM_edge_exists(v4, v3);
+ e2 = BM_edge_exists(v2, v1);
+ BM_elem_attrs_copy(bm, bm, l->e, e1);
+ BM_elem_attrs_copy(bm, bm, l->e, e2);
+
+ /* set edge lengths of cross edges as the average of the cross edges they're based o */
+ if (has_elens) {
+ /* angle happens not to be used. why? - not sure it just isnt - campbell.
+ * leave this in incase we need to use it later */
+#if 0
+ float ang;
+#endif
+ e1 = BM_edge_exists(v1, v4);
+ e2 = BM_edge_exists(v2, v3);
+
+ if (l->radial_next->v == l->v) {
+ l2 = l->radial_next->prev;
+ l3 = l->radial_next->next;
+ }
+ else {
+ l2 = l->radial_next->next;
+ l3 = l->radial_next->prev;
+ }
+
+ d3 = CustomData_bmesh_get_n(&bm->edata, e1->head.data, CD_PROP_FLT, li);
+ d1 = *(float *)CustomData_bmesh_get_n(&bm->edata, l->prev->e->head.data, CD_PROP_FLT, li);
+ d2 = *(float *)CustomData_bmesh_get_n(&bm->edata, l2->e->head.data, CD_PROP_FLT, li);
+#if 0
+ ang = angle_v3v3v3(l->prev->v->co, l->v->co, BM_edge_other_vert(l2->e, l->v)->co);
+#endif
+ *d3 = (d1 + d2) * 0.5f;
+
+ d3 = CustomData_bmesh_get_n(&bm->edata, e2->head.data, CD_PROP_FLT, li);
+ d1 = *(float *)CustomData_bmesh_get_n(&bm->edata, l->next->e->head.data, CD_PROP_FLT, li);
+ d2 = *(float *)CustomData_bmesh_get_n(&bm->edata, l3->e->head.data, CD_PROP_FLT, li);
+#if 0
+ ang = angle_v3v3v3(BM_edge_other_vert(l->next->e, l->next->v)->co, l->next->v->co,
+ BM_edge_other_vert(l3->e, l->next->v)->co);
+#endif
+ *d3 = (d1 + d2) * 0.5f;
+ }
+
+ if (!f) {
+ fprintf(stderr, "%s: face index out of range! (bmesh internal error)\n", __func__);
+ continue;
+ }
+
+ BMO_elem_flag_enable(bm, f, FACE_NEW|FACE_SPAN);
+
+ /* un-tag edges in f for deletio */
+ BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_FACE, f) {
+ BMO_elem_flag_disable(bm, l2->e, BEVEL_DEL);
+ }
+ }
+ else {
+ f = NULL;
+ }
+ }
+ }
+
+ /* fill in holes at vertices */
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ BMIter eiter;
+ BMVert *vv, *vstart = NULL, *lastv = NULL;
+ SmallHash tmphash;
+ int rad, insorig = 0, err = 0;
+
+ BLI_smallhash_init(&tmphash);
+
+ if (!BMO_elem_flag_test(bm, v, BEVEL_FLAG))
+ continue;
+
+ BLI_array_empty(verts);
+ BLI_array_empty(edges);
+
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
+ BMIter liter;
+ BMVert *v1 = NULL, *v2 = NULL;
+ BMLoop *l;
+
+ if (BM_edge_face_count(e) < 2)
+ insorig = 1;
+
+ if (BM_elem_index_get(e) == -1)
+ continue;
+
+ rad = 0;
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_EDGE, e) {
+ if (!BMO_elem_flag_test(bm, l->f, FACE_OLD))
+ continue;
+
+ rad++;
+
+ tag = tags + BM_elem_index_get((l->v == v) ? l : l->next);
+
+ if (!v1)
+ v1 = tag->newv;
+ else if (!v2)
+ v2 = tag->newv;
+ }
+
+ if (rad < 2)
+ insorig = 1;
+
+ if (!v1)
+ v1 = ETAG_GET(e, v);
+ if (!v2 || v1 == v2)
+ v2 = ETAG_GET(e, v);
+
+ if (v1) {
+ if (!BLI_smallhash_haskey(&tmphash, (intptr_t)v1)) {
+ BLI_array_append(verts, v1);
+ BLI_smallhash_insert(&tmphash, (intptr_t)v1, NULL);
+ }
+
+ if (v2 && v1 != v2 && !BLI_smallhash_haskey(&tmphash, (intptr_t)v2)) {
+ BLI_array_append(verts, v2);
+ BLI_smallhash_insert(&tmphash, (intptr_t)v2, NULL);
+ }
+ }
+ }
+
+ if (!BLI_array_count(verts))
+ continue;
+
+ if (insorig) {
+ BLI_array_append(verts, v);
+ BLI_smallhash_insert(&tmphash, (intptr_t)v, NULL);
+ }
+
+ /* find edges that exist between vertices in verts. this is basically
+ * a topological walk of the edges connecting them */
+ vstart = vstart ? vstart : verts[0];
+ vv = vstart;
+ do {
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, vv) {
+ BMVert *vv2 = BM_edge_other_vert(e, vv);
+
+ if (vv2 != lastv && BLI_smallhash_haskey(&tmphash, (intptr_t)vv2)) {
+ /* if we've go over the same vert twice, break out of outer loop */
+ if (BLI_smallhash_lookup(&tmphash, (intptr_t)vv2) != NULL) {
+ e = NULL;
+ err = 1;
+ break;
+ }
+
+ /* use self pointer as ta */
+ BLI_smallhash_remove(&tmphash, (intptr_t)vv2);
+ BLI_smallhash_insert(&tmphash, (intptr_t)vv2, vv2);
+
+ lastv = vv;
+ BLI_array_append(edges, e);
+ vv = vv2;
+ break;
+ }
+ }
+
+ if (e == NULL) {
+ break;
+ }
+ } while (vv != vstart);
+
+ if (err) {
+ continue;
+ }
+
+ /* there may not be a complete loop of edges, so start again and make
+ * final edge afterwards. in this case, the previous loop worked to
+ * find one of the two edges at the extremes. */
+ if (vv != vstart) {
+ /* undo previous taggin */
+ for (i = 0; i < BLI_array_count(verts); i++) {
+ BLI_smallhash_remove(&tmphash, (intptr_t)verts[i]);
+ BLI_smallhash_insert(&tmphash, (intptr_t)verts[i], NULL);
+ }
+
+ vstart = vv;
+ lastv = NULL;
+ BLI_array_empty(edges);
+ do {
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, vv) {
+ BMVert *vv2 = BM_edge_other_vert(e, vv);
+
+ if (vv2 != lastv && BLI_smallhash_haskey(&tmphash, (intptr_t)vv2)) {
+ /* if we've go over the same vert twice, break out of outer loo */
+ if (BLI_smallhash_lookup(&tmphash, (intptr_t)vv2) != NULL) {
+ e = NULL;
+ err = 1;
+ break;
+ }
+
+ /* use self pointer as ta */
+ BLI_smallhash_remove(&tmphash, (intptr_t)vv2);
+ BLI_smallhash_insert(&tmphash, (intptr_t)vv2, vv2);
+
+ lastv = vv;
+ BLI_array_append(edges, e);
+ vv = vv2;
+ break;
+ }
+ }
+ if (e == NULL)
+ break;
+ } while (vv != vstart);
+
+ if (!err) {
+ e = BM_edge_create(bm, vv, vstart, NULL, TRUE);
+ BLI_array_append(edges, e);
+ }
+ }
+
+ if (err)
+ continue;
+
+ if (BLI_array_count(edges) >= 3) {
+ BMFace *f;
+
+ if (BM_face_exists(bm, verts, BLI_array_count(verts), &f))
+ continue;
+
+ f = BM_face_create_ngon(bm, lastv, vstart, edges, BLI_array_count(edges), FALSE);
+ if (!f) {
+ fprintf(stderr, "%s: in bevel vert fill! (bmesh internal error)\n", __func__);
+ }
+ else {
+ BMO_elem_flag_enable(bm, f, FACE_NEW|FACE_HOLE);
+ }
+ }
+ BLI_smallhash_release(&tmphash);
+ }
+
+ /* copy over customdat */
+ for (i = 0; i < BLI_array_count(faces); i++) {
+ BMLoop *l;
+ BMIter liter;
+ BMFace *f = faces[i];
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ BMLoop *l2;
+ BMIter liter2;
+
+ tag = tags + BM_elem_index_get(l);
+ if (!tag->newv)
+ continue;
+
+ BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_VERT, tag->newv) {
+ if (!BMO_elem_flag_test(bm, l2->f, FACE_NEW) || (l2->v != tag->newv && l2->v != l->v))
+ continue;
+
+ if (tag->newv != l->v || HasMDisps) {
+ BM_elem_attrs_copy(bm, bm, l->f, l2->f);
+ BM_loop_interp_from_face(bm, l2, l->f, TRUE, TRUE);
+ }
+ else {
+ BM_elem_attrs_copy(bm, bm, l->f, l2->f);
+ BM_elem_attrs_copy(bm, bm, l, l2);
+ }
+
+ if (HasMDisps) {
+ BMLoop *l3;
+ BMIter liter3;
+
+ BM_ITER(l3, &liter3, bm, BM_LOOPS_OF_FACE, l2->f) {
+ BM_loop_interp_multires(bm, l3, l->f);
+ }
+ }
+ }
+ }
+ }
+
+ /* handle vertices along boundary edge */
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, v, VERT_OLD) &&
+ BMO_elem_flag_test(bm, v, BEVEL_FLAG) &&
+ !BMO_elem_flag_test(bm, v, BEVEL_DEL))
+ {
+ BMLoop *l;
+ BMLoop *lorig = NULL;
+ BMIter liter;
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, v) {
+ // BMIter liter2;
+ // BMLoop *l2 = l->v == v ? l : l->next, *l3;
+
+ if (BMO_elem_flag_test(bm, l->f, FACE_OLD)) {
+ lorig = l;
+ break;
+ }
+ }
+
+ if (!lorig)
+ continue;
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, v) {
+ BMLoop *l2 = l->v == v ? l : l->next;
+
+ BM_elem_attrs_copy(bm, bm, lorig->f, l2->f);
+ BM_elem_attrs_copy(bm, bm, lorig, l2);
+ }
+ }
+ }
+#if 0
+ /* clean up any remaining 2-edged face */
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ if (f->len == 2) {
+ BMFace *faces[2] = {f, BM_FACE_FIRST_LOOP(f)->radial_next->f};
+
+ if (faces[0] == faces[1])
+ BM_face_kill(bm, f);
+ else
+ BM_faces_join(bm, faces, 2);
+ }
+ }
+#endif
+
+ BMO_op_callf(bm, "del geom=%fv context=%i", BEVEL_DEL, DEL_VERTS);
+
+ /* clean up any edges that might not get properly delete */
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, e, EDGE_OLD) && !e->l)
+ BMO_elem_flag_enable(bm, e, BEVEL_DEL);
+ }
+
+ BMO_op_callf(bm, "del geom=%fe context=%i", BEVEL_DEL, DEL_EDGES);
+ BMO_op_callf(bm, "del geom=%ff context=%i", BEVEL_DEL, DEL_FACES);
+
+ BLI_smallhash_release(&hash);
+ BLI_array_free(tags);
+ BLI_array_free(etags);
+ BLI_array_free(verts);
+ BLI_array_free(edges);
+ BLI_array_free(faces);
+
+ BMO_slot_from_flag(bm, op, "face_spans", FACE_SPAN, BM_FACE);
+ BMO_slot_from_flag(bm, op, "face_holes", FACE_HOLE, BM_FACE);
+}
diff --git a/source/blender/bmesh/operators/bmo_connect.c b/source/blender/bmesh/operators/bmo_connect.c
new file mode 100644
index 00000000000..6ab6d28008b
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_connect.c
@@ -0,0 +1,414 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Joseph Eagar.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+
+#include "bmesh.h"
+
+#include "BLI_math.h"
+#include "BLI_array.h"
+
+#define VERT_INPUT 1
+#define EDGE_OUT 1
+#define FACE_NEW 2
+#define EDGE_MARK 4
+#define EDGE_DONE 8
+
+void connectverts_exec(BMesh *bm, BMOperator *op)
+{
+ BMIter iter, liter;
+ BMFace *f, *nf;
+ BMLoop **loops = NULL, *lastl = NULL;
+ BLI_array_declare(loops);
+ BMLoop *l, *nl;
+ BMVert **verts = NULL;
+ BLI_array_declare(verts);
+ int i;
+
+ BMO_slot_buffer_flag_enable(bm, op, "verts", VERT_INPUT, BM_VERT);
+
+ for (f = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); f; f = BM_iter_step(&iter)) {
+ BLI_array_empty(loops);
+ BLI_array_empty(verts);
+
+ if (BMO_elem_flag_test(bm, f, FACE_NEW)) continue;
+
+ l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f);
+ lastl = NULL;
+ for ( ; l; l = BM_iter_step(&liter)) {
+ if (BMO_elem_flag_test(bm, l->v, VERT_INPUT)) {
+ if (!lastl) {
+ lastl = l;
+ continue;
+ }
+
+ if (lastl != l->prev && lastl != l->next) {
+ BLI_array_growone(loops);
+ loops[BLI_array_count(loops) - 1] = lastl;
+
+ BLI_array_growone(loops);
+ loops[BLI_array_count(loops) - 1] = l;
+
+ }
+ lastl = l;
+ }
+ }
+
+ if (BLI_array_count(loops) == 0) continue;
+
+ if (BLI_array_count(loops) > 2) {
+ BLI_array_growone(loops);
+ loops[BLI_array_count(loops) - 1] = loops[BLI_array_count(loops) - 2];
+
+ BLI_array_growone(loops);
+ loops[BLI_array_count(loops) - 1] = loops[0];
+ }
+
+ BM_face_legal_splits(bm, f, (BMLoop *(*)[2])loops, BLI_array_count(loops) / 2);
+
+ for (i = 0; i < BLI_array_count(loops) / 2; i++) {
+ if (loops[i * 2] == NULL) continue;
+
+ BLI_array_growone(verts);
+ verts[BLI_array_count(verts) - 1] = loops[i * 2]->v;
+
+ BLI_array_growone(verts);
+ verts[BLI_array_count(verts) - 1] = loops[i * 2 + 1]->v;
+ }
+
+ for (i = 0; i < BLI_array_count(verts) / 2; i++) {
+ nf = BM_face_split(bm, f, verts[i * 2], verts[i * 2 + 1], &nl, NULL);
+ f = nf;
+
+ if (!nl || !nf) {
+ BMO_error_raise(bm, op, BMERR_CONNECTVERT_FAILED, NULL);
+ BLI_array_free(loops);
+ return;
+ }
+ BMO_elem_flag_enable(bm, nf, FACE_NEW);
+ BMO_elem_flag_enable(bm, nl->e, EDGE_OUT);
+ }
+ }
+
+ BMO_slot_from_flag(bm, op, "edgeout", EDGE_OUT, BM_EDGE);
+
+ BLI_array_free(loops);
+ BLI_array_free(verts);
+}
+
+static BMVert *get_outer_vert(BMesh *bm, BMEdge *e)
+{
+ BMIter iter;
+ BMEdge *e2;
+ int i;
+
+ i = 0;
+ BM_ITER(e2, &iter, bm, BM_EDGES_OF_VERT, e->v1) {
+ if (BMO_elem_flag_test(bm, e2, EDGE_MARK)) {
+ i++;
+ }
+ }
+
+ return (i == 2) ? e->v2 : e->v1;
+}
+
+/* Clamp x to the interval {0..len-1}, with wrap-around */
+static int clamp_index(const int x, const int len)
+{
+ return (x < 0) ? (len - (-x % len)) : (x % len);
+}
+
+/* There probably is a better way to swap BLI_arrays, or if there
+ * isn't there should be... */
+#define ARRAY_SWAP(elemtype, arr1, arr2) \
+ { \
+ int i; \
+ elemtype *arr_tmp = NULL; \
+ BLI_array_declare(arr_tmp); \
+ for (i = 0; i < BLI_array_count(arr1); i++) { \
+ BLI_array_append(arr_tmp, arr1[i]); \
+ } \
+ BLI_array_empty(arr1); \
+ for (i = 0; i < BLI_array_count(arr2); i++) { \
+ BLI_array_append(arr1, arr2[i]); \
+ } \
+ BLI_array_empty(arr2); \
+ for (i = 0; i < BLI_array_count(arr_tmp); i++) { \
+ BLI_array_append(arr2, arr_tmp[i]); \
+ } \
+ BLI_array_free(arr_tmp); \
+ }
+
+void bmesh_bridge_loops_exec(BMesh *bm, BMOperator *op)
+{
+ BMEdge **ee1 = NULL, **ee2 = NULL;
+ BMVert **vv1 = NULL, **vv2 = NULL;
+ BLI_array_declare(ee1);
+ BLI_array_declare(ee2);
+ BLI_array_declare(vv1);
+ BLI_array_declare(vv2);
+ BMOIter siter;
+ BMIter iter;
+ BMEdge *e, *nexte;
+ int c = 0, cl1 = 0, cl2 = 0;
+
+ BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE);
+
+ BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
+ if (!BMO_elem_flag_test(bm, e, EDGE_DONE)) {
+ BMVert *v, *ov;
+ /* BMEdge *e2, *e3, *oe = e; */ /* UNUSED */
+ BMEdge *e2, *e3;
+
+ if (c > 2) {
+ BMO_error_raise(bm, op, BMERR_INVALID_SELECTION, "Select only two edge loops");
+ goto cleanup;
+ }
+
+ e2 = e;
+ v = e->v1;
+ do {
+ v = BM_edge_other_vert(e2, v);
+ nexte = NULL;
+ BM_ITER(e3, &iter, bm, BM_EDGES_OF_VERT, v) {
+ if (e3 != e2 && BMO_elem_flag_test(bm, e3, EDGE_MARK)) {
+ if (nexte == NULL) {
+ nexte = e3;
+ }
+ else {
+ /* edges do not form a loop: there is a disk
+ * with more than two marked edges. */
+ BMO_error_raise(bm, op, BMERR_INVALID_SELECTION,
+ "Selection must only contain edges from two edge loops");
+ goto cleanup;
+ }
+ }
+ }
+
+ if (nexte)
+ e2 = nexte;
+ } while (nexte && e2 != e);
+
+ if (!e2)
+ e2 = e;
+
+ e = e2;
+ ov = v;
+ do {
+ if (c == 0) {
+ BLI_array_append(ee1, e2);
+ BLI_array_append(vv1, v);
+ }
+ else {
+ BLI_array_append(ee2, e2);
+ BLI_array_append(vv2, v);
+ }
+
+ BMO_elem_flag_enable(bm, e2, EDGE_DONE);
+
+ v = BM_edge_other_vert(e2, v);
+ BM_ITER(e3, &iter, bm, BM_EDGES_OF_VERT, v) {
+ if (e3 != e2 && BMO_elem_flag_test(bm, e3, EDGE_MARK) && !BMO_elem_flag_test(bm, e3, EDGE_DONE)) {
+ break;
+ }
+ }
+ if (e3)
+ e2 = e3;
+ } while (e3 && e2 != e);
+
+ if (v && !e3) {
+ if (c == 0) {
+ if (BLI_array_count(vv1) && v == vv1[BLI_array_count(vv1) - 1]) {
+ printf("%s: internal state waning *TODO DESCRIPTION!*\n", __func__);
+ }
+ BLI_array_append(vv1, v);
+ }
+ else {
+ BLI_array_append(vv2, v);
+ }
+ }
+
+ /* test for connected loops, and set cl1 or cl2 if so */
+ if (v == ov) {
+ if (c == 0) {
+ cl1 = 1;
+ }
+ else {
+ cl2 = 1;
+ }
+ }
+
+ c++;
+ }
+ }
+
+ if (ee1 && ee2) {
+ int i, j;
+ BMVert *v1, *v2, *v3, *v4;
+ int starti = 0, dir1 = 1, wdir = 0, lenv1, lenv2;
+
+ /* Simplify code below by avoiding the (!cl1 && cl2) case */
+ if (!cl1 && cl2) {
+ SWAP(int, cl1, cl2);
+ ARRAY_SWAP(BMVert *, vv1, vv2);
+ ARRAY_SWAP(BMEdge *, ee1, ee2);
+ }
+
+ lenv1 = lenv2 = BLI_array_count(vv1);
+
+ /* Below code assumes vv1/vv2 each have at least two verts. should always be
+ * a safe assumption, since ee1/ee2 are non-empty and an edge has two verts. */
+ BLI_assert((lenv1 > 1) && (lenv2 > 1));
+
+ /* BMESH_TODO: Would be nice to handle cases where the edge loops
+ * have different edge counts by generating triangles & quads for
+ * the bridge instead of quads only. */
+ if (BLI_array_count(ee1) != BLI_array_count(ee2)) {
+ BMO_error_raise(bm, op, BMERR_INVALID_SELECTION,
+ "Selected loops must have equal edge counts");
+ goto cleanup;
+ }
+
+ j = 0;
+ if (vv1[0] == vv1[lenv1 - 1]) {
+ lenv1--;
+ }
+ if (vv2[0] == vv2[lenv2 - 1]) {
+ lenv2--;
+ }
+
+ /* Find starting point and winding direction for two unclosed loops */
+ if (!cl1 && !cl2) {
+ /* First point of loop 1 */
+ v1 = get_outer_vert(bm, ee1[0]);
+ /* Last point of loop 1 */
+ v2 = get_outer_vert(bm, ee1[clamp_index(-1, BLI_array_count(ee1))]);
+ /* First point of loop 2 */
+ v3 = get_outer_vert(bm, ee2[0]);
+ /* Last point of loop 2 */
+ v4 = get_outer_vert(bm, ee2[clamp_index(-1, BLI_array_count(ee2))]);
+
+ /* If v1 is a better match for v4 than v3, AND v2 is a better match
+ * for v3 than v4, the loops are in opposite directions, so reverse
+ * the order of reads from vv1. We can avoid sqrt for comparison */
+ if (len_squared_v3v3(v1->co, v3->co) > len_squared_v3v3(v1->co, v4->co) &&
+ len_squared_v3v3(v2->co, v4->co) > len_squared_v3v3(v2->co, v3->co))
+ {
+ dir1 = -1;
+ starti = clamp_index(-1, lenv1);
+ }
+ }
+
+ /* Find the shortest distance from a vert in vv1 to vv2[0]. Use that
+ * vertex in vv1 as a starting point in the first loop, while starting
+ * from vv2[0] in the second loop. This is a simplistic attempt to get
+ * a better edge-to-edge match between the two loops. */
+ if (cl1) {
+ int previ, nexti;
+ float min = 1e32;
+
+ /* BMESH_TODO: Would be nice to do a more thorough analysis of all
+ * the vertices in both loops to find a more accurate match for the
+ * starting point and winding direction of the bridge generation. */
+
+ for (i = 0; i < BLI_array_count(vv1); i++) {
+ if (len_v3v3(vv1[i]->co, vv2[0]->co) < min) {
+ min = len_v3v3(vv1[i]->co, vv2[0]->co);
+ starti = i;
+ }
+ }
+
+ /* Reverse iteration order for the first loop if the distance of
+ * the (starti - 1) vert from vv1 is a better match for vv2[1] than
+ * the (starti + 1) vert.
+ *
+ * This is not always going to be right, but it will work better in
+ * the average case.
+ */
+ previ = clamp_index(starti - 1, lenv1);
+ nexti = clamp_index(starti + 1, lenv1);
+
+ /* avoid sqrt for comparison */
+ if (len_squared_v3v3(vv1[nexti]->co, vv2[1]->co) > len_squared_v3v3(vv1[previ]->co, vv2[1]->co)) {
+ /* reverse direction for reading vv1 (1 is forward, -1 is backward) */
+ dir1 = -1;
+ }
+ }
+
+ /* Vert rough attempt to determine proper winding for the bridge quads:
+ * just uses the first loop it finds for any of the edges of ee2 or ee1 */
+ if (wdir == 0) {
+ for (i = 0; i < BLI_array_count(ee2); i++) {
+ if (ee2[i]->l) {
+ wdir = (ee2[i]->l->v == vv2[i]) ? (-1) : (1);
+ break;
+ }
+ }
+ }
+ if (wdir == 0) {
+ for (i = 0; i < BLI_array_count(ee1); i++) {
+ j = clamp_index((i * dir1) + starti, BLI_array_count(ee1));
+ if (ee1[j]->l && ee2[j]->l) {
+ wdir = (ee2[j]->l->v == vv2[j]) ? (1) : (-1);
+ break;
+ }
+ }
+ }
+
+ /* Generate the bridge quads */
+ for (i = 0; i < BLI_array_count(ee1) && i < BLI_array_count(ee2); i++) {
+ BMFace *f;
+ int i1, i1next, i2, i2next;
+
+ i1 = clamp_index(i * dir1 + starti, lenv1);
+ i1next = clamp_index((i + 1) * dir1 + starti, lenv1);
+ i2 = i;
+ i2next = clamp_index(i + 1, lenv2);
+
+ if (vv1[i1] == vv1[i1next]) {
+ continue;
+ }
+
+ if (wdir < 0) {
+ SWAP(int, i1, i1next);
+ SWAP(int, i2, i2next);
+ }
+
+ f = BM_face_create_quad_tri(bm,
+ vv1[i1],
+ vv2[i2],
+ vv2[i2next],
+ vv1[i1next],
+ NULL, TRUE);
+ if (!f || f->len != 4) {
+ fprintf(stderr, "%s: in bridge! (bmesh internal error)\n", __func__);
+ }
+ }
+ }
+
+cleanup:
+ BLI_array_free(ee1);
+ BLI_array_free(ee2);
+ BLI_array_free(vv1);
+ BLI_array_free(vv2);
+}
diff --git a/source/blender/bmesh/operators/bmo_create.c b/source/blender/bmesh/operators/bmo_create.c
new file mode 100644
index 00000000000..d90e5c36c80
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_create.c
@@ -0,0 +1,1412 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Joseph Eagar.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_heap.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_array.h"
+#include "BLI_smallhash.h"
+#include "BLI_rand.h"
+
+#include "bmesh.h"
+
+
+#define EDGE_MARK 1
+#define EDGE_VIS 2
+
+#define FACE_NEW 1
+
+#define ELE_NEW 1
+#define ELE_OUT 2
+#define ELE_ORIG 4
+
+#define FACE_IGNORE 16
+
+typedef struct EPathNode {
+ struct EPathNode *next, *prev;
+ BMVert *v;
+ BMEdge *e;
+ BMEdge *cure;
+} EPathNode;
+
+typedef struct EPath {
+ ListBase nodes;
+ float weight;
+ int group;
+} EPath;
+
+typedef struct PathBase {
+ BLI_mempool *nodepool, *pathpool;
+} PathBase;
+
+typedef struct EdgeData {
+ int tag;
+ int ftag;
+ BMDiskLink v1_disk_link, v2_disk_link;
+} EdgeData;
+
+typedef struct VertData {
+ BMEdge *e;
+ float no[3], offco[3], sco[3]; /* offco is vertex coordinate slightly offset randoml */
+ int tag;
+} VertData;
+
+static int count_edge_faces(BMesh *bm, BMEdge *e);
+
+/**** rotation system code * */
+
+#define RS_GET_EDGE_LINK(e, v, e_data) ( \
+ (v) == ((BMEdge *)(e))->v1 ? \
+ &(((EdgeData *)(e_data))->v1_disk_link) : \
+ &(((EdgeData *)(e_data))->v2_disk_link) \
+ )
+
+
+static int rotsys_append_edge(struct BMEdge *e, struct BMVert *v,
+ EdgeData *edata, VertData *vdata)
+{
+ EdgeData *ed = &edata[BM_elem_index_get(e)];
+ VertData *vd = &vdata[BM_elem_index_get(v)];
+
+ if (!vd->e) {
+ Link *e1 = (Link *)RS_GET_EDGE_LINK(e, v, ed);
+
+ vd->e = e;
+ e1->next = e1->prev = (Link *)e;
+ }
+ else {
+ BMDiskLink *dl1, *dl2, *dl3;
+ EdgeData *ved = &edata[BM_elem_index_get(vd->e)];
+
+ dl1 = RS_GET_EDGE_LINK(e, v, ed);
+ dl2 = RS_GET_EDGE_LINK(vd->e, v, ved);
+ dl3 = dl2->prev ? RS_GET_EDGE_LINK(dl2->prev, v, &edata[BM_elem_index_get(dl2->prev)]) : NULL;
+
+ dl1->next = vd->e;
+ dl1->prev = dl2->prev;
+
+ dl2->prev = e;
+ if (dl3) {
+ dl3->next = e;
+ }
+ }
+
+ return TRUE;
+}
+
+static void UNUSED_FUNCTION(rotsys_remove_edge)(struct BMEdge *e, struct BMVert *v,
+ EdgeData *edata, VertData *vdata)
+{
+ EdgeData *ed = edata + BM_elem_index_get(e);
+ VertData *vd = vdata + BM_elem_index_get(v);
+ BMDiskLink *e1, *e2;
+
+ e1 = RS_GET_EDGE_LINK(e, v, ed);
+ if (e1->prev) {
+ e2 = RS_GET_EDGE_LINK(e1->prev, v, ed);
+ e2->next = e1->next;
+ }
+
+ if (e1->next) {
+ e2 = RS_GET_EDGE_LINK(e1->next, v, ed);
+ e2->prev = e1->prev;
+ }
+
+ if (vd->e == e)
+ vd->e = (e != (BMEdge *)e1->next) ? (BMEdge *)e1->next : NULL;
+
+ e1->next = e1->prev = NULL;
+}
+
+static struct BMEdge *rotsys_nextedge(struct BMEdge *e, struct BMVert *v,
+ EdgeData *edata, VertData *UNUSED(vdata))
+{
+ if (v == e->v1)
+ return edata[BM_elem_index_get(e)].v1_disk_link.next;
+ if (v == e->v2)
+ return edata[BM_elem_index_get(e)].v2_disk_link.next;
+ return NULL;
+}
+
+static BMEdge *rotsys_prevedge(BMEdge *e, BMVert *v,
+ EdgeData *edata, VertData *UNUSED(vdata))
+{
+ if (v == e->v1)
+ return edata[BM_elem_index_get(e)].v1_disk_link.prev;
+ if (v == e->v2)
+ return edata[BM_elem_index_get(e)].v2_disk_link.prev;
+ return NULL;
+}
+
+static void rotsys_reverse(struct BMEdge *UNUSED(e), struct BMVert *v, EdgeData *edata, VertData *vdata)
+{
+ BMEdge **edges = NULL;
+ BMEdge *e_first;
+ BMEdge *e;
+ BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE);
+ int i, totedge;
+
+ e = e_first = vdata[BM_elem_index_get(v)].e;
+ do {
+ BLI_array_append(edges, e);
+ e = rotsys_nextedge(e, v, edata, vdata);
+ } while (e != e_first);
+
+ totedge = BLI_array_count(edges);
+ for (i = 0; i < totedge / 2; i++) {
+ SWAP(BMEdge *, edges[i], edges[totedge - 1 - i]);
+ }
+
+ vdata[BM_elem_index_get(v)].e = NULL;
+ for (i = 0; i < totedge; i++) {
+ rotsys_append_edge(edges[i], v, edata, vdata);
+ }
+
+ BLI_array_free(edges);
+}
+
+static int UNUSED_FUNCTION(rotsys_count)(struct BMVert *v, EdgeData *edata, VertData *vdata)
+{
+ BMEdge *e = vdata[BM_elem_index_get(v)].e;
+ int i = 0;
+
+ if (!e)
+ return 0;
+
+ do {
+ if (!e)
+ return 0;
+ e = rotsys_nextedge(e, v, edata, vdata);
+
+ if (i >= (1 << 20)) {
+ printf("bmesh error: infinite loop in disk cycle!\n");
+ return 0;
+ }
+
+ i += 1;
+ } while (e != vdata[BM_elem_index_get(v)].e);
+
+ return i;
+}
+
+static int UNUSED_FUNCTION(rotsys_fill_faces)(BMesh *bm, EdgeData *edata, VertData *vdata)
+{
+ BMIter iter;
+ BMEdge *e, **edges = NULL;
+ BLI_array_declare(edges);
+ BMVert *v, **verts = NULL;
+ BMFace *f;
+ BLI_array_declare(verts);
+ SmallHash visithash, *hash = &visithash;
+ int i;
+
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ BMEdge *e2, *starte;
+ BMVert *startv;
+ int rad, ok;
+
+ rad = count_edge_faces(bm, e);
+
+ if (rad < 2)
+ starte = e;
+ else
+ continue;
+
+ /* do two passes, going forward then backwar */
+ for (i = 0; i < 2; i++) {
+ BLI_smallhash_init(hash);
+
+ BLI_array_empty(verts);
+ BLI_array_empty(edges);
+
+ startv = v = starte->v1;
+ e2 = starte;
+ ok = 1;
+ if (!v || !e2)
+ continue;
+
+ do {
+ if (BLI_smallhash_haskey(hash, (intptr_t)e2) ||
+ BLI_smallhash_haskey(hash, (intptr_t)v))
+ {
+ ok = 0;
+ break;
+ }
+
+ BLI_array_append(verts, v);
+ BLI_array_append(edges, e2);
+
+ BLI_smallhash_insert(hash, (intptr_t)e2, NULL);
+
+ v = BM_edge_other_vert(e2, v);
+ e2 = i ? rotsys_prevedge(e2, v, edata, vdata) : rotsys_nextedge(e2, v, edata, vdata);
+ } while (e2 != starte && v != startv);
+
+ BLI_smallhash_release(hash);
+
+ if (!ok || BLI_array_count(edges) < 3)
+ continue;
+
+ f = BM_face_create_ngon(bm, verts[0], verts[1], edges, BLI_array_count(edges), TRUE);
+ if (!f)
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+static void rotsys_make_consistent(BMesh *bm, EdgeData *edata, VertData *vdata)
+{
+ BMIter iter;
+ BMEdge *e;
+ BMVert *v, **stack = NULL;
+ BLI_array_declare(stack);
+ int i;
+
+ for (i = 0; i < bm->totvert; i++) {
+ vdata[i].tag = 0;
+ }
+
+ while (1) {
+ VertData *vd;
+ BMVert *startv = NULL;
+ float dis;
+
+ v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL);
+ for (i = 0; i < bm->totvert; i++, BM_iter_step(&iter)) {
+ vd = vdata + BM_elem_index_get(v);
+
+ if (vd->tag)
+ continue;
+
+ if (!startv || dot_v3v3(vd->offco, vd->offco) > dis) {
+ dis = dot_v3v3(vd->offco, vd->offco);
+ startv = v;
+ }
+ }
+
+ if (!startv)
+ break;
+
+ vd = vdata + BM_elem_index_get(startv);
+
+ BLI_array_empty(stack);
+ BLI_array_append(stack, startv);
+
+ vd->tag = 1;
+
+ while (BLI_array_count(stack)) {
+ v = BLI_array_pop(stack);
+ vd = vdata + BM_elem_index_get(v);
+
+ if (!vd->e)
+ continue;
+
+ e = vd->e;
+ do {
+ BMVert *v2 = BM_edge_other_vert(e, v);
+ VertData *vd2 = vdata + BM_elem_index_get(v2);
+
+ if (dot_v3v3(vd->no, vd2->no) < 0.0f + FLT_EPSILON * 2) {
+ rotsys_reverse(e, v2, edata, vdata);
+ mul_v3_fl(vd2->no, -1.0f);
+ }
+
+ if (!vd2->tag) {
+ BLI_array_append(stack, v2);
+ vd2->tag = 1;
+ }
+
+ e = rotsys_nextedge(e, v, edata, vdata);
+ } while (e != vd->e);
+ }
+ }
+
+ BLI_array_free(stack);
+}
+
+static void init_rotsys(BMesh *bm, EdgeData *edata, VertData *vdata)
+{
+ BMIter iter;
+ BMEdge *e;
+ BMEdge **edges = NULL;
+ BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE);
+ BMVert *v;
+ /* BMVert **verts = NULL; */
+ /* BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE); */ /* UNUSE */
+ int i;
+
+#define SIGN(n) ((n)<0.0f)
+
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ BMIter eiter;
+ float no[3], cent[3];
+ int j, k = 0, totedge = 0;
+
+ if (BM_elem_index_get(v) == -1)
+ continue;
+
+ BLI_array_empty(edges);
+
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
+ if (BMO_elem_flag_test(bm, e, EDGE_MARK)) {
+ BLI_array_append(edges, e);
+ totedge++;
+ }
+ }
+
+ copy_v3_v3(cent, v->co);
+
+ zero_v3(no);
+ for (i = 0; i < totedge; i++) {
+ BMEdge *e1, *e2;
+ float cno[3], vec1[3], vec2[3];
+
+ e1 = edges[i];
+ e2 = edges[(i + 1) % totedge];
+
+ sub_v3_v3v3(vec1, (BM_edge_other_vert(e1, v))->co, v->co);
+ sub_v3_v3v3(vec2, (BM_edge_other_vert(e2, v))->co, v->co);
+
+ cross_v3_v3v3(cno, vec1, vec2);
+ normalize_v3(cno);
+
+ if (i && dot_v3v3(cno, no) < 0.0f + FLT_EPSILON * 10)
+ mul_v3_fl(cno, -1.0f);
+
+ add_v3_v3(no, cno);
+ normalize_v3(no);
+ }
+
+ /* generate plane-flattened coordinate */
+ for (i = 0; i < totedge; i++) {
+ BMEdge *e1;
+ BMVert *v2;
+ float cvec[3], vec1[3];
+
+ e1 = edges[i];
+ v2 = BM_edge_other_vert(e1, v);
+
+ sub_v3_v3v3(vec1, v2->co, v->co);
+
+ cross_v3_v3v3(cvec, vec1, no);
+ cross_v3_v3v3(vec1, cvec, no);
+ normalize_v3(vec1);
+
+ mul_v3_fl(vec1, len_v3v3(v2->co, v->co));
+ add_v3_v3(vec1, v->co);
+
+ copy_v3_v3(vdata[BM_elem_index_get(v2)].sco, vec1);
+ }
+
+ BLI_srandom(0);
+
+ /* first, ensure no 0 or 180 angles between adjacent
+ * (and that adjacent's adjacent) edges */
+ for (i = 0, k = 0; i < totedge; i++) {
+ BMEdge *e1, *e2, *e3 = NULL;
+ BMVert *v1, *v2, *v3;
+ VertData *vd1, *vd2, *vd3;
+ float vec1[3], vec2[3], vec3[3], size;
+ int s1, s2, s3;
+
+ if (totedge < 3)
+ continue;
+
+ e1 = edges[(i + totedge - 1) % totedge];
+ e2 = edges[i];
+ e3 = edges[(i + 1) % totedge];
+
+ v1 = BM_edge_other_vert(e1, v);
+ v2 = BM_edge_other_vert(e2, v);
+ v3 = BM_edge_other_vert(e3, v);
+
+ vd1 = vdata + BM_elem_index_get(v1);
+ vd2 = vdata + BM_elem_index_get(v2);
+ vd3 = vdata + BM_elem_index_get(v3);
+
+ sub_v3_v3v3(vec1, vd1->sco, cent);
+ sub_v3_v3v3(vec2, vd2->sco, cent);
+ sub_v3_v3v3(vec3, vd3->sco, cent);
+
+ size = (len_v3(vec1) + len_v3(vec3)) * 0.01f;
+ normalize_v3(vec1); normalize_v3(vec2); normalize_v3(vec3);
+
+#ifdef STRAIGHT
+#undef STRAIGHT
+#endif
+#define STRAIGHT(vec11, vec22) (fabsf(dot_v3v3((vec11), (vec22))) > 1.0f - ((float)FLT_EPSILON * 1000.0f))
+
+ s1 = STRAIGHT(vec1, vec2); s2 = STRAIGHT(vec2, vec3); s3 = STRAIGHT(vec1, vec3);
+
+ if (s1 || s2 || s3) {
+ copy_v3_v3(cent, v->co);
+
+ for (j = 0; j < 3; j++) {
+ float fac = (BLI_frand() - 0.5f)*size;
+ cent[j] += fac;
+ }
+
+ if (k < 2000) {
+ i = 0;
+ k++;
+ continue;
+ }
+ else {
+ k++;
+ continue;
+ }
+
+ }
+ }
+
+ copy_v3_v3(vdata[BM_elem_index_get(v)].offco, cent);
+ //copy_v3_v3(v->co, cent);
+
+ /* now, sort edges so the triangle fan of all edges
+ * has a consistent normal. this is the same as
+ * sorting by polar coordinates along a group normal */
+ for (j = 0; j < totedge; j++) {
+ for (i = 0; i < totedge; i++) {
+ BMEdge *e1, *e2, *e3 = NULL;
+ BMVert *v1, *v2, *v3;
+ VertData *vd1, *vd2, *vd3;
+ float vec1[3], vec2[3], vec3[3], n1[3], n2[3], n3[3];
+ int s1, s2, s3;
+
+ e1 = edges[(i + totedge - 1) % totedge];
+ e2 = edges[i];
+ e3 = edges[(i + 1) % totedge];
+
+ v1 = BM_edge_other_vert(e1, v);
+ v2 = BM_edge_other_vert(e2, v);
+ v3 = BM_edge_other_vert(e3, v);
+
+ vd1 = vdata + BM_elem_index_get(v1);
+ vd2 = vdata + BM_elem_index_get(v2);
+ vd3 = vdata + BM_elem_index_get(v3);
+
+ sub_v3_v3v3(vec1, vd1->sco, cent);
+ sub_v3_v3v3(vec2, vd2->sco, cent);
+ sub_v3_v3v3(vec3, vd3->sco, cent);
+
+ cross_v3_v3v3(n1, vec1, vec2);
+ cross_v3_v3v3(n2, vec2, vec3);
+ cross_v3_v3v3(n3, vec1, vec3);
+
+ /* Other way to determine if two vectors approach are (nearly) parallel: the
+ * cross product of the two vectors will approach zero */
+ s1 = (dot_v3v3(n1, n1) < (0.0f + FLT_EPSILON * 10));
+ s2 = (dot_v3v3(n2, n2) < (0.0f + FLT_EPSILON * 10));
+ s3 = (totedge < 3) ? 0 : (dot_v3v3(n3, n3) < (0.0f + FLT_EPSILON * 10));
+
+ normalize_v3(n1); normalize_v3(n2); normalize_v3(n3);
+
+ if (s1 || s2 || s3) {
+ fprintf(stderr, "%s: s1: %d, s2: %d, s3: %dx (bmesh internal error)\n", __func__, s1, s2, s3);
+ }
+ if (dot_v3v3(n1, n2) < 0.0f) {
+ if (dot_v3v3(n1, n3) >= 0.0f + FLT_EPSILON * 10) {
+ SWAP(BMEdge *, edges[i], edges[(i + 1) % totedge]);
+ }
+ else {
+ SWAP(BMEdge *, edges[(i + totedge - 1) % totedge], edges[(i + 1) % totedge]);
+ SWAP(BMEdge *, edges[i], edges[(i + 1) % totedge]);
+ }
+ }
+ }
+ }
+
+#undef STRAIGHT
+
+ zero_v3(no);
+
+ /* yay, edges are sorted */
+ for (i = 0; i < totedge; i++) {
+ BMEdge *e1 = edges[i], *e2 = edges[(i + 1) % totedge];
+ float eno[3];
+
+ normal_tri_v3(eno, BM_edge_other_vert(e1, v)->co, v->co, BM_edge_other_vert(e2, v)->co);
+ add_v3_v3(no, eno);
+
+ rotsys_append_edge(edges[i], v, edata, vdata);
+ }
+
+ normalize_v3(no);
+ copy_v3_v3(vdata[BM_elem_index_get(v)].no, no);
+ }
+
+ /* now, make sure rotation system is topologically consistent
+ * (e.g. vert normals consistently point either inside or outside) */
+ rotsys_make_consistent(bm, edata, vdata);
+
+ //rotsys_fill_faces(bm, edata, vdata);
+
+#if 0
+ /* create visualizing geometr */
+ BMVert *lastv;
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ BMVert *v2;
+ BMFace *f;
+ int totedge = BM_vert_edge_count(v);
+
+ if (BM_elem_index_get(v) == -1)
+ continue;
+
+ //cv = BM_vert_create(bm, cent, v);
+ //BM_elem_index_set(cv, -1); /* set_dirty! */
+ i = 0;
+ e = vdata[BM_elem_index_get(v)].e;
+ lastv = NULL;
+ do {
+ BMEdge *e2;
+ BMVert *v2;
+ float f = ((float)i / (float)totedge) * 0.35 + 0.05;
+ float co[3];
+
+ if (!e)
+ break;
+
+ if (!BM_edge_other_vert(e, v))
+ continue;
+
+ sub_v3_v3v3(co, (BM_edge_other_vert(e, v))->co, vdata[BM_elem_index_get(v)].offco);
+ mul_v3_fl(co, f);
+ add_v3_v3(co, vdata[BM_elem_index_get(v)].offco);
+
+ v2 = BM_vert_create(bm, co, NULL);
+ BM_elem_index_set(v2, -1); /* set_dirty! */
+ //BM_edge_create(bm, cv, v2, NULL, FALSE);
+
+ BM_elem_select_set(bm, v2, TRUE);
+ if (lastv) {
+ e2 = BM_edge_create(bm, lastv, v2, NULL, FALSE);
+ BM_elem_select_set(bm, e2, TRUE);
+ }
+
+ lastv = v2;
+
+ e = rotsys_nextedge(e, v, edata, vdata);
+ i++;
+ } while (e != vdata[BM_elem_index_get(v)].e);
+ }
+#endif
+
+ BLI_array_free(edges);
+}
+
+static PathBase *edge_pathbase_new(void)
+{
+ PathBase *pb = MEM_callocN(sizeof(PathBase), "PathBase");
+
+ pb->nodepool = BLI_mempool_create(sizeof(EPathNode), 1, 512, TRUE, FALSE);
+ pb->pathpool = BLI_mempool_create(sizeof(EPath), 1, 512, TRUE, FALSE);
+
+ return pb;
+}
+
+static void edge_pathbase_free(PathBase *pathbase)
+{
+ BLI_mempool_destroy(pathbase->nodepool);
+ BLI_mempool_destroy(pathbase->pathpool);
+ MEM_freeN(pathbase);
+}
+
+static EPath *edge_copy_add_path(PathBase *pb, EPath *path, BMVert *appendv, BMEdge *e)
+{
+ EPath *path2;
+ EPathNode *node, *node2;
+
+ path2 = BLI_mempool_alloc(pb->pathpool);
+ path2->nodes.first = path2->nodes.last = NULL;
+ path2->weight = 0.0f;
+ path2->group = path->group;
+
+ for (node = path->nodes.first; node; node = node->next) {
+ node2 = BLI_mempool_alloc(pb->nodepool);
+ *node2 = *node;
+ BLI_addtail(&path2->nodes, node2);
+ }
+
+ node2 = BLI_mempool_alloc(pb->nodepool);
+ node2->v = appendv;
+ node2->e = e;
+ node2->cure = NULL;
+
+ BLI_addtail(&path2->nodes, node2);
+
+ return path2;
+}
+
+static EPath *edge_path_new(PathBase *pb, BMVert *start, BMEdge *starte)
+{
+ EPath *path;
+ EPathNode *node;
+
+ path = BLI_mempool_alloc(pb->pathpool);
+ node = BLI_mempool_alloc(pb->nodepool);
+
+ path->nodes.first = path->nodes.last = NULL;
+
+ node->v = start;
+ node->e = starte;
+ node->cure = NULL;
+
+ BLI_addtail(&path->nodes, node);
+ path->weight = 0.0f;
+
+ return path;
+}
+
+static float edge_weight_path(EPath *path, EdgeData *edata, VertData *UNUSED(vdata))
+{
+ EPathNode *node, *first = path->nodes.first;
+ float w = 0.0;
+
+ for (node = path->nodes.first; node; node = node->next) {
+ if (node->e && node != path->nodes.first) {
+ w += edata[BM_elem_index_get(node->e)].ftag;
+ if (node->prev) {
+ /* BMESH_TOD */
+ (void)first;
+ //w += len_v3v3(node->v->co, first->e->v1->co) * 0.0001f;
+ //w += len_v3v3(node->v->co, first->e->v2->co) * 0.0001f;
+ }
+ }
+
+ w += 1.0f;
+ }
+
+ return w;
+}
+
+
+static void edge_free_path(PathBase *pathbase, EPath *path)
+{
+ EPathNode *node, *next;
+
+ for (node = path->nodes.first; node; node = next) {
+ next = node->next;
+ BLI_mempool_free(pathbase->nodepool, node);
+ }
+
+ BLI_mempool_free(pathbase->pathpool, path);
+}
+
+static EPath *edge_find_shortest_path(BMesh *bm, BMOperator *op, BMEdge *edge, EdgeData *edata,
+ VertData *vdata, PathBase *pathbase, int group)
+{
+ BMEdge *e;
+ GHash *gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "createops find shortest path");
+ BMVert *v1, *v2;
+ BMVert **verts = NULL;
+ BLI_array_staticdeclare(verts, 1024);
+ Heap *heap = BLI_heap_new();
+ EPath *path = NULL, *path2;
+ BMVert *startv;
+ BMVert *endv;
+ EPathNode *node;
+ int i, use_restrict = BMO_slot_int_get(op, "use_restrict");
+
+ startv = edata[BM_elem_index_get(edge)].ftag ? edge->v2 : edge->v1;
+ endv = edata[BM_elem_index_get(edge)].ftag ? edge->v1 : edge->v2;
+
+ path = edge_path_new(pathbase, startv, edge);
+ BLI_ghash_insert(gh, startv, NULL);
+ BLI_heap_insert(heap, path->weight, path);
+ path->group = group;
+
+ while (BLI_heap_size(heap)) {
+ VertData *vd;
+ EPathNode *last;
+ BMFace *f = NULL;
+
+ path = BLI_heap_popmin(heap);
+ last = path->nodes.last;
+ v1 = last->v;
+
+ if (v1 == endv) {
+ /* make sure this path loop doesn't already exist */
+ i = 0;
+ BLI_array_empty(verts);
+ for (i = 0, node = path->nodes.first; node; node = node->next, i++) {
+ BLI_array_growone(verts);
+ verts[i] = node->v;
+ }
+
+ if (BM_face_exists(bm, verts, i, &f)) {
+ if (!BMO_elem_flag_test(bm, f, FACE_IGNORE)) {
+ BLI_ghash_remove(gh, endv, NULL, NULL);
+ continue;
+ }
+ }
+ break;
+ }
+
+ vd = vdata + BM_elem_index_get(v1);
+ if (!vd->e)
+ continue;
+
+ v2 = NULL;
+ while (1) {
+ if (!last->cure) {
+ last->cure = e = vdata[BM_elem_index_get(last->v)].e;
+ }
+ else {
+ last->cure = e = rotsys_nextedge(last->cure, last->v, edata, vdata);
+ if (last->cure == vdata[BM_elem_index_get(last->v)].e) {
+ v2 = NULL;
+ break;
+ }
+ }
+
+ if (e == edge || !BMO_elem_flag_test(bm, e, EDGE_MARK)) {
+ continue;
+ }
+
+ v2 = BM_edge_other_vert(e, last->v);
+
+ if (BLI_ghash_haskey(gh, v2)) {
+ v2 = NULL;
+ continue;
+ }
+
+ if (use_restrict && BMO_slot_map_contains(bm, op, "restrict", e)) {
+ int group = BMO_slot_map_int_get(bm, op, "restrict", e);
+
+ if (!(group & path->group)) {
+ v2 = NULL;
+ continue;
+ }
+ }
+
+ break;
+ }
+
+ if (!v2) {
+ if (path) {
+ edge_free_path(pathbase, path);
+ path = NULL;
+ }
+ continue;
+ }
+
+ /* add path back into hea */
+ BLI_heap_insert(heap, path->weight, path);
+
+ /* put v2 in gh ma */
+ BLI_ghash_insert(gh, v2, NULL);
+
+ path2 = edge_copy_add_path(pathbase, path, v2, e);
+ path2->weight = edge_weight_path(path2, edata, vdata);
+
+ BLI_heap_insert(heap, path2->weight, path2);
+ }
+
+ if (path && ((EPathNode *)path->nodes.last)->v != endv) {
+ edge_free_path(pathbase, path);
+ path = NULL;
+ }
+
+ BLI_array_free(verts);
+ BLI_heap_free(heap, NULL);
+ BLI_ghash_free(gh, NULL, NULL);
+
+ return path;
+}
+
+static int count_edge_faces(BMesh *bm, BMEdge *e)
+{
+ int i = 0;
+ BMLoop *l = e->l;
+
+ if (!l) {
+ return 0;
+ }
+
+ do {
+ if (!BMO_elem_flag_test(bm, l->f, FACE_IGNORE)) {
+ i++;
+ }
+
+ l = l->radial_next;
+ } while (l != e->l);
+
+ return i;
+}
+
+void bmesh_edgenet_fill_exec(BMesh *bm, BMOperator *op)
+{
+ BMIter iter;
+ BMOIter siter;
+ BMFace *f;
+ BMEdge *e, *edge;
+ BMVert **verts = NULL;
+ BLI_array_declare(verts);
+ EPath *path;
+ EPathNode *node;
+ EdgeData *edata;
+ VertData *vdata;
+ BMEdge **edges = NULL;
+ PathBase *pathbase = edge_pathbase_new();
+ BLI_array_declare(edges);
+ int use_restrict = BMO_slot_int_get(op, "use_restrict");
+ int i, j, group = 0;
+ unsigned int winding[2]; /* accumulte winding directions for each edge which has a face */
+
+ if (!bm->totvert || !bm->totedge)
+ return;
+
+ edata = MEM_callocN(sizeof(EdgeData)*bm->totedge, "EdgeData");
+ vdata = MEM_callocN(sizeof(VertData)*bm->totvert, "VertData");
+
+ BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE);
+ BMO_slot_buffer_flag_enable(bm, op, "excludefaces", FACE_IGNORE, BM_FACE);
+
+ BM_mesh_elem_index_ensure(bm, BM_VERT);
+
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ BMO_elem_flag_enable(bm, f, ELE_ORIG);
+ }
+
+ i = 0;
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ BM_elem_index_set(e, i); /* set_inline */
+
+ if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) {
+ edata[i].tag = 2;
+ }
+
+ i++;
+ }
+ bm->elem_index_dirty &= ~BM_EDGE;
+
+ init_rotsys(bm, edata, vdata);
+
+ while (1) {
+ edge = NULL;
+ group = 0;
+
+ BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
+ /* if restrict is on, only start on faces in the restrict map */
+ if (use_restrict && !BMO_slot_map_contains(bm, op, "restrict", e))
+ continue;
+
+ if (edata[BM_elem_index_get(e)].tag < 2) {
+ edge = e;
+
+ if (use_restrict) {
+ int i = 0, j = 0, gi = 0;
+
+ group = BMO_slot_map_int_get(bm, op, "restrict", e);
+
+ for (i = 0; i < 30; i++) {
+ if (group & (1 << i)) {
+ j++;
+ gi = i;
+
+ if (j - 1 == edata[BM_elem_index_get(e)].tag) {
+ break;
+ }
+ }
+ }
+
+ group = (1 << gi);
+ }
+
+ break;
+ }
+ }
+
+ if (!edge)
+ break;
+
+ edata[BM_elem_index_get(edge)].tag += 1;
+
+ path = edge_find_shortest_path(bm, op, edge, edata, vdata, pathbase, group);
+ if (!path)
+ continue;
+
+ winding[0] = winding[1] = 0;
+
+ BLI_array_empty(edges);
+ BLI_array_empty(verts);
+ i = 0;
+ for (node = path->nodes.first; node; node = node->next) {
+ if (!node->next)
+ continue;
+
+ e = BM_edge_exists(node->v, node->next->v);
+
+ /* this should never happe */
+ if (!e)
+ break;
+
+ /* check on the winding */
+ if (e->l) {
+ BMVert *test_v1, *test_v2;
+ /* we want to use the reverse winding to the existing order */
+ BM_edge_ordered_verts(edge, &test_v2, &test_v1);
+
+ /* edges vote on which winding wins out */
+ winding[(test_v1 == node->v)]++;
+ }
+
+ edata[BM_elem_index_get(e)].ftag++;
+ BLI_array_growone(edges);
+ edges[i++] = e;
+
+ BLI_array_append(verts, node->v);
+ }
+
+ BLI_array_growone(edges);
+ edges[i++] = edge;
+ edata[BM_elem_index_get(edge)].ftag++;
+
+ for (j = 0; j < i; j++) {
+ if (count_edge_faces(bm, edges[j]) >= 2) {
+ edge_free_path(pathbase, path);
+ break;
+ }
+ }
+
+ if (j != i) {
+ continue;
+ }
+
+ if (i) {
+ BMVert *v1, *v2;
+
+ /* to define the winding order must select first edge,
+ * otherwise we could leave this as-is */
+ edge = edges[0];
+
+ /* if these are even it doesnt really matter what to do,
+ * with consistent geometry one will be zero, the choice is clear */
+ if (winding[0] > winding[1]) {
+ v1 = verts[0];
+ v2 = verts[1];
+ }
+ else {
+ v1 = verts[1];
+ v2 = verts[0];
+ }
+
+ f = BM_face_create_ngon(bm, v1, v2, edges, i, TRUE);
+ if (f && !BMO_elem_flag_test(bm, f, ELE_ORIG)) {
+ BMO_elem_flag_enable(bm, f, FACE_NEW);
+ }
+
+ if (use_restrict)
+ BMO_slot_map_int_insert(bm, op, "faceout_groupmap", f, path->group);
+ }
+
+ edge_free_path(pathbase, path);
+ }
+
+ BMO_slot_from_flag(bm, op, "faceout", FACE_NEW, BM_FACE);
+
+ BLI_array_free(edges);
+ BLI_array_free(verts);
+ edge_pathbase_free(pathbase);
+ MEM_freeN(edata);
+ MEM_freeN(vdata);
+}
+
+static BMEdge *edge_next(BMesh *bm, BMEdge *e)
+{
+ BMIter iter;
+ BMEdge *e2;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ BM_ITER(e2, &iter, bm, BM_EDGES_OF_VERT, i ? e->v2 : e->v1) {
+ if ( (BMO_elem_flag_test(bm, e2, EDGE_MARK)) &&
+ (!BMO_elem_flag_test(bm, e2, EDGE_VIS)) &&
+ (e2 != e))
+ {
+ return e2;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+void bmesh_edgenet_prepare(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMEdge *e;
+ BMEdge **edges1 = NULL, **edges2 = NULL, **edges;
+ BLI_array_declare(edges1);
+ BLI_array_declare(edges2);
+ BLI_array_declare(edges);
+ int ok = 1;
+ int i, count;
+
+ BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE);
+
+ /* validate that each edge has at most one other tagged edge in the
+ * disk cycle around each of it's vertices */
+ BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
+ for (i = 0; i < 2; i++) {
+ count = BMO_vert_edge_flags_count(bm, i ? e->v2 : e->v1, EDGE_MARK);
+ if (count > 2) {
+ ok = 0;
+ break;
+ }
+ }
+
+ if (!ok) {
+ break;
+ }
+ }
+
+ /* we don't have valid edge layouts, retur */
+ if (!ok) {
+ return;
+ }
+
+ /* find connected loops within the input edge */
+ count = 0;
+ while (1) {
+ BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
+ if (!BMO_elem_flag_test(bm, e, EDGE_VIS)) {
+ if ( BMO_vert_edge_flags_count(bm, e->v1, EDGE_MARK) == 1 ||
+ BMO_vert_edge_flags_count(bm, e->v2, EDGE_MARK) == 1)
+ {
+ break;
+ }
+ }
+ }
+
+ if (!e) {
+ break;
+ }
+
+ if (!count) {
+ edges = edges1;
+ }
+ else if (count == 1) {
+ edges = edges2;
+ }
+ else {
+ break;
+ }
+
+ i = 0;
+ while (e) {
+ BMO_elem_flag_enable(bm, e, EDGE_VIS);
+ BLI_array_growone(edges);
+ edges[i] = e;
+
+ e = edge_next(bm, e);
+ i++;
+ }
+
+ if (!count) {
+ edges1 = edges;
+ BLI_array_set_length(edges1, BLI_array_count(edges));
+ }
+ else {
+ edges2 = edges;
+ BLI_array_set_length(edges2, BLI_array_count(edges));
+ }
+
+ BLI_array_empty(edges);
+ count++;
+ }
+
+ if (edges1 && BLI_array_count(edges1) > 2 &&
+ BM_edge_share_vert(edges1[0], edges1[BLI_array_count(edges1) - 1]))
+ {
+ if (edges2 && BLI_array_count(edges2) > 2 &&
+ BM_edge_share_vert(edges2[0], edges2[BLI_array_count(edges2) - 1]))
+ {
+ BLI_array_free(edges1);
+ BLI_array_free(edges2);
+ return;
+ }
+ else {
+ edges1 = edges2;
+ edges2 = NULL;
+ }
+ }
+
+ if (edges2 && BLI_array_count(edges2) > 2 &&
+ BM_edge_share_vert(edges2[0], edges2[BLI_array_count(edges2) - 1]))
+ {
+ edges2 = NULL;
+ }
+
+ /* two unconnected loops, connect the */
+ if (edges1 && edges2) {
+ BMVert *v1, *v2, *v3, *v4;
+
+ if (BLI_array_count(edges1) == 1) {
+ v1 = edges1[0]->v1;
+ v2 = edges1[0]->v2;
+ }
+ else {
+ if (BM_vert_in_edge(edges1[1], edges1[0]->v1))
+ v1 = edges1[0]->v2;
+ else v1 = edges1[0]->v1;
+
+ i = BLI_array_count(edges1) - 1;
+ if (BM_vert_in_edge(edges1[i - 1], edges1[i]->v1))
+ v2 = edges1[i]->v2;
+ else v2 = edges1[i]->v1;
+ }
+
+ if (BLI_array_count(edges2) == 1) {
+ v3 = edges2[0]->v1;
+ v4 = edges2[0]->v2;
+ }
+ else {
+ if (BM_vert_in_edge(edges2[1], edges2[0]->v1))
+ v3 = edges2[0]->v2;
+ else v3 = edges2[0]->v1;
+
+ i = BLI_array_count(edges2) - 1;
+ if (BM_vert_in_edge(edges2[i - 1], edges2[i]->v1))
+ v4 = edges2[i]->v2;
+ else v4 = edges2[i]->v1;
+ }
+
+ /* avoid sqrt for comparison */
+ if (len_squared_v3v3(v1->co, v3->co) + len_squared_v3v3(v2->co, v4->co) >
+ len_squared_v3v3(v1->co, v4->co) + len_squared_v3v3(v2->co, v3->co))
+ {
+ BMVert *v;
+ v = v3;
+ v3 = v4;
+ v4 = v;
+ }
+
+ e = BM_edge_create(bm, v1, v3, NULL, TRUE);
+ BMO_elem_flag_enable(bm, e, ELE_NEW);
+ e = BM_edge_create(bm, v2, v4, NULL, TRUE);
+ BMO_elem_flag_enable(bm, e, ELE_NEW);
+ }
+ else if (edges1) {
+ BMVert *v1, *v2;
+
+ if (BLI_array_count(edges1) > 1) {
+ if (BM_vert_in_edge(edges1[1], edges1[0]->v1))
+ v1 = edges1[0]->v2;
+ else v1 = edges1[0]->v1;
+
+ i = BLI_array_count(edges1) - 1;
+ if (BM_vert_in_edge(edges1[i - 1], edges1[i]->v1))
+ v2 = edges1[i]->v2;
+ else v2 = edges1[i]->v1;
+
+ e = BM_edge_create(bm, v1, v2, NULL, TRUE);
+ BMO_elem_flag_enable(bm, e, ELE_NEW);
+ }
+ }
+
+ BMO_slot_from_flag(bm, op, "edgeout", ELE_NEW, BM_EDGE);
+
+ BLI_array_free(edges1);
+ BLI_array_free(edges2);
+}
+
+/* this is essentially new fke */
+void bmesh_contextual_create_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator op2;
+ BMOIter oiter;
+ BMIter iter;
+ BMHeader *h;
+ BMVert *v, *verts[4];
+ BMEdge *e;
+ BMFace *f;
+ int totv = 0, tote = 0, totf = 0, amount;
+
+ /* count number of each element type we were passe */
+ BMO_ITER(h, &oiter, bm, op, "geom", BM_VERT|BM_EDGE|BM_FACE) {
+ switch (h->htype) {
+ case BM_VERT: totv++; break;
+ case BM_EDGE: tote++; break;
+ case BM_FACE: totf++; break;
+ }
+
+ BMO_elem_flag_enable(bm, (BMElemF *)h, ELE_NEW);
+ }
+
+ /* --- Support for Special Case ---
+ * where there is a contiguous edge ring with one isolated vertex.
+ *
+ * This example shows 2 edges created from 3 verts
+ * with 1 free standing vertex. Dotted lines denote the 2 edges that are created.
+ *
+ * note that this works for any sided shape.
+ *
+ * +--------+
+ * | .
+ * | .
+ * | .
+ * | .
+ * +........+ <-- starts out free standing.
+ *
+ */
+
+ /* Here we check for consistancy and create 2 edges */
+ if (totf == 0 && totv >= 4 && totv == tote + 2) {
+ /* find a free standing vertex and 2 endpoint verts */
+ BMVert *v_free = NULL, *v_a = NULL, *v_b = NULL;
+ int ok = TRUE;
+
+
+ BMO_ITER(v, &oiter, bm, op, "geom", BM_VERT) {
+ /* count how many flagged edges this vertex uses */
+ int tot_edges = 0;
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) {
+ if (BMO_elem_flag_test(bm, e, ELE_NEW)) {
+ tot_edges++;
+ if (tot_edges > 2) {
+ break;
+ }
+ }
+ }
+
+ if (tot_edges == 0) {
+ /* only accept 1 free vert */
+ if (v_free == NULL) v_free = v;
+ else ok = FALSE; /* only ever want one of these */
+ }
+ else if (tot_edges == 1) {
+ if (v_a == NULL) v_a = v;
+ else if (v_b == NULL) v_b = v;
+ else ok = FALSE; /* only ever want 2 of these */
+ }
+ else if (tot_edges == 2) {
+ /* do nothing, regular case */
+ }
+ else {
+ ok = FALSE; /* if a vertex has 3+ edge users then cancel - this is only simple cases */
+ }
+
+ if (ok == FALSE) {
+ break;
+ }
+ }
+
+ if (ok == TRUE && v_free && v_a && v_b) {
+ e = BM_edge_create(bm, v_free, v_a, NULL, TRUE);
+ BMO_elem_flag_enable(bm, e, ELE_NEW);
+
+ e = BM_edge_create(bm, v_free, v_b, NULL, TRUE);
+ BMO_elem_flag_enable(bm, e, ELE_NEW);
+ }
+ }
+ /* --- end special case support, continue as normal --- */
+
+
+ /* possible bug?, selecting 2 triangles and pressing F will make a quad rather then joining them,
+ * perhaps this should be looked into? - campbell */
+
+ /* call edgenet create */
+ /* call edgenet prepare op so additional face creation cases wor */
+ BMO_op_initf(bm, &op2, "edgenet_prepare edges=%fe", ELE_NEW);
+ BMO_op_exec(bm, &op2);
+ BMO_slot_buffer_flag_enable(bm, &op2, "edgeout", ELE_NEW, BM_EDGE);
+ BMO_op_finish(bm, &op2);
+
+ BMO_op_initf(bm, &op2, "edgenet_fill edges=%fe", ELE_NEW);
+ BMO_op_exec(bm, &op2);
+
+ /* return if edge net create did somethin */
+ if (BMO_slot_buf_count(bm, &op2, "faceout")) {
+ BMO_slot_copy(&op2, op, "faceout", "faceout");
+ BMO_op_finish(bm, &op2);
+ return;
+ }
+
+ BMO_op_finish(bm, &op2);
+
+ /* now call dissolve face */
+ BMO_op_initf(bm, &op2, "dissolvefaces faces=%ff", ELE_NEW);
+ BMO_op_exec(bm, &op2);
+
+ /* if we dissolved anything, then return */
+ if (BMO_slot_buf_count(bm, &op2, "regionout")) {
+ BMO_slot_copy(&op2, op, "regionout", "faceout");
+ BMO_op_finish(bm, &op2);
+ return;
+ }
+
+ BMO_op_finish(bm, &op2);
+
+ /* now, count how many verts we hav */
+ amount = 0;
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, v, ELE_NEW)) {
+ verts[amount] = v;
+ amount++;
+
+ if (amount > 4) break;
+ }
+ }
+
+ if (amount == 2) {
+ /* create edg */
+ e = BM_edge_create(bm, verts[0], verts[1], NULL, TRUE);
+ BMO_elem_flag_enable(bm, e, ELE_OUT);
+ }
+ else if (amount == 3) {
+ /* create triangl */
+ BM_face_create_quad_tri(bm, verts[0], verts[1], verts[2], NULL, NULL, TRUE);
+ }
+ else if (amount == 4) {
+ f = NULL;
+
+ /* the order of vertices can be anything, 6 cases to check */
+ if (is_quad_convex_v3(verts[0]->co, verts[1]->co, verts[2]->co, verts[3]->co)) {
+ f = BM_face_create_quad_tri(bm, verts[0], verts[1], verts[2], verts[3], NULL, TRUE);
+ }
+ else if (is_quad_convex_v3(verts[0]->co, verts[2]->co, verts[3]->co, verts[1]->co)) {
+ f = BM_face_create_quad_tri(bm, verts[0], verts[2], verts[3], verts[1], NULL, TRUE);
+ }
+ else if (is_quad_convex_v3(verts[0]->co, verts[2]->co, verts[1]->co, verts[3]->co)) {
+ f = BM_face_create_quad_tri(bm, verts[0], verts[2], verts[1], verts[3], NULL, TRUE);
+ }
+ else if (is_quad_convex_v3(verts[0]->co, verts[1]->co, verts[3]->co, verts[2]->co)) {
+ f = BM_face_create_quad_tri(bm, verts[0], verts[1], verts[3], verts[2], NULL, TRUE);
+ }
+ else if (is_quad_convex_v3(verts[0]->co, verts[3]->co, verts[2]->co, verts[1]->co)) {
+ f = BM_face_create_quad_tri(bm, verts[0], verts[3], verts[2], verts[1], NULL, TRUE);
+ }
+ else if (is_quad_convex_v3(verts[0]->co, verts[3]->co, verts[1]->co, verts[2]->co)) {
+ f = BM_face_create_quad_tri(bm, verts[0], verts[3], verts[1], verts[2], NULL, TRUE);
+ }
+ else {
+ printf("cannot find nice quad from concave set of vertices\n");
+ }
+
+ if (f) BMO_elem_flag_enable(bm, f, ELE_OUT);
+ }
+}
diff --git a/source/blender/bmesh/operators/bmo_dissolve.c b/source/blender/bmesh/operators/bmo_dissolve.c
new file mode 100644
index 00000000000..05aead466d1
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_dissolve.c
@@ -0,0 +1,559 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Joseph Eagar.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_array.h"
+#include "BLI_math.h"
+
+#include "bmesh.h"
+#include "bmesh_private.h"
+
+#include "bmesh_operators_private.h" /* own include */
+
+#define FACE_MARK 1
+#define FACE_ORIG 2
+#define FACE_NEW 4
+#define EDGE_MARK 1
+
+#define VERT_MARK 1
+
+static int UNUSED_FUNCTION(check_hole_in_region)(BMesh *bm, BMFace *f)
+{
+ BMWalker regwalker;
+ BMIter liter2;
+ BMLoop *l2, *l3;
+ BMFace *f2;
+
+ /* checks if there are any unmarked boundary edges in the face regio */
+
+ BMW_init(&regwalker, bm, BMW_ISLAND,
+ BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, FACE_MARK,
+ BMW_NIL_LAY);
+ f2 = BMW_begin(&regwalker, f);
+ for ( ; f2; f2 = BMW_step(&regwalker)) {
+ l2 = BM_iter_new(&liter2, bm, BM_LOOPS_OF_FACE, f2);
+ for ( ; l2; l2 = BM_iter_step(&liter2)) {
+ l3 = bmesh_radial_nextloop(l2);
+ if ( BMO_elem_flag_test(bm, l3->f, FACE_MARK) !=
+ BMO_elem_flag_test(bm, l2->f, FACE_MARK))
+ {
+ if (!BMO_elem_flag_test(bm, l2->e, EDGE_MARK)) {
+ return FALSE;
+ }
+ }
+ }
+ }
+ BMW_end(&regwalker);
+
+ return TRUE;
+}
+
+void dissolvefaces_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter oiter;
+ BMFace *f, *f2 /* , *nf = NULL */;
+ BLI_array_declare(faces);
+ BLI_array_declare(regions);
+ BMFace ***regions = NULL;
+ BMFace **faces = NULL;
+ BMWalker regwalker;
+ int i;
+
+ int use_verts = BMO_slot_int_get(op, "use_verts");
+
+ if (use_verts) {
+ /* tag verts that start out with only 2 edges,
+ * don't remove these later */
+ BMIter viter;
+ BMVert *v;
+
+ BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (BM_vert_edge_count(v) == 2) {
+ BMO_elem_flag_disable(bm, v, VERT_MARK);
+ }
+ else {
+ BMO_elem_flag_enable(bm, v, VERT_MARK);
+ }
+ }
+ }
+
+ BMO_slot_buffer_flag_enable(bm, op, "faces", FACE_MARK, BM_FACE);
+
+ /* collect region */
+ BMO_ITER(f, &oiter, bm, op, "faces", BM_FACE) {
+ if (!BMO_elem_flag_test(bm, f, FACE_MARK)) continue;
+
+ BLI_array_empty(faces);
+ faces = NULL; /* forces different allocatio */
+
+ /* yay, walk */
+ BMW_init(&regwalker, bm, BMW_ISLAND,
+ BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, FACE_MARK,
+ BMW_NIL_LAY);
+
+ f2 = BMW_begin(&regwalker, f);
+ for ( ; f2; f2 = BMW_step(&regwalker)) {
+ BLI_array_append(faces, f2);
+ }
+ BMW_end(&regwalker);
+
+ for (i = 0; i < BLI_array_count(faces); i++) {
+ f2 = faces[i];
+ BMO_elem_flag_disable(bm, f2, FACE_MARK);
+ BMO_elem_flag_enable(bm, f2, FACE_ORIG);
+ }
+
+ if (BMO_error_occurred(bm)) {
+ BMO_error_clear(bm);
+ BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED, NULL);
+ goto cleanup;
+ }
+
+ BLI_array_append(faces, NULL);
+ BLI_array_append(regions, faces);
+ }
+
+ for (i = 0; i < BLI_array_count(regions); i++) {
+ int tot = 0;
+
+ faces = regions[i];
+ if (!faces[0]) {
+ BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED,
+ "Could not find boundary of dissolve region");
+ goto cleanup;
+ }
+
+ while (faces[tot])
+ tot++;
+
+ f = BM_faces_join(bm, faces, tot);
+ if (!f) {
+ BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED,
+ "Could not create merged face");
+ goto cleanup;
+ }
+
+ /* if making the new face failed (e.g. overlapping test)
+ * unmark the original faces for deletion */
+ BMO_elem_flag_disable(bm, f, FACE_ORIG);
+ BMO_elem_flag_enable(bm, f, FACE_NEW);
+
+ }
+
+ BMO_op_callf(bm, "del geom=%ff context=%d", FACE_ORIG, DEL_FACES);
+
+
+ if (use_verts) {
+ BMIter viter;
+ BMVert *v;
+
+ BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
+ if (BM_vert_edge_count(v) == 2) {
+ BM_vert_collapse_edges(bm, v->e, v);
+ }
+ }
+ }
+ }
+
+ if (BMO_error_occurred(bm)) goto cleanup;
+
+ BMO_slot_from_flag(bm, op, "regionout", FACE_NEW, BM_FACE);
+
+cleanup:
+ /* free/cleanu */
+ for (i = 0; i < BLI_array_count(regions); i++) {
+ if (regions[i]) MEM_freeN(regions[i]);
+ }
+
+ BLI_array_free(regions);
+}
+
+/* almost identical to dissolve edge, except it cleans up vertice */
+void dissolve_edgeloop_exec(BMesh *bm, BMOperator *op)
+{
+ /* BMOperator fop; */
+ BMOIter oiter;
+ BMIter iter;
+ BMVert *v, **verts = NULL;
+ BLI_array_declare(verts);
+ BMEdge *e;
+ /* BMFace *f; */
+ int i;
+
+ BMO_ITER(e, &oiter, bm, op, "edges", BM_EDGE) {
+ if (BM_edge_face_count(e) == 2) {
+ BMO_elem_flag_enable(bm, e->v1, VERT_MARK);
+ BMO_elem_flag_enable(bm, e->v2, VERT_MARK);
+
+ BM_faces_join_pair(bm, e->l->f,
+ e->l->radial_next->f,
+ e);
+ }
+ }
+
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, v, VERT_MARK) && BM_vert_edge_count(v) == 2) {
+ BLI_array_append(verts, v);
+ }
+ }
+
+ /* clean up extreneous 2-valence vertice */
+ for (i = 0; i < BLI_array_count(verts); i++) {
+ if (verts[i]->e) {
+ BM_vert_collapse_edges(bm, verts[i]->e, verts[i]);
+ }
+ }
+
+ BLI_array_free(verts);
+
+ //BMO_op_initf(bm, &fop, "dissolvefaces faces=%ff", FACE_MARK);
+ //BMO_op_exec(bm, &fop);
+
+ //BMO_slot_copy(op, &fop, "regionout", "regionout");
+
+ //BMO_op_finish(bm, &fop);
+}
+
+
+void dissolveedges_exec(BMesh *bm, BMOperator *op)
+{
+ /* might want to make this an option or mode - campbell */
+
+ /* BMOperator fop; */
+ BMOIter eiter;
+ BMEdge *e;
+
+ BMIter viter;
+ BMVert *v;
+
+ int use_verts = BMO_slot_int_get(op, "use_verts");
+
+ if (use_verts) {
+ BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (BM_vert_edge_count(v) == 2) {
+ BMO_elem_flag_disable(bm, v, VERT_MARK);
+ }
+ else {
+ BMO_elem_flag_enable(bm, v, VERT_MARK);
+ }
+ }
+ }
+
+ BMO_ITER(e, &eiter, bm, op, "edges", BM_EDGE) {
+ const int edge_face_count = BM_edge_face_count(e);
+ if (edge_face_count == 2) {
+
+ /* join faces */
+ BM_faces_join_pair(bm, e->l->f,
+ e->l->radial_next->f,
+ e);
+ }
+ }
+
+ if (use_verts) {
+ BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
+ if (BM_vert_edge_count(v) == 2) {
+ BM_vert_collapse_edges(bm, v->e, v);
+ }
+ }
+ }
+ }
+}
+
+static int test_extra_verts(BMesh *bm, BMVert *v)
+{
+ BMIter iter, liter, iter2, iter3;
+ BMFace *f, *f2;
+ BMLoop *l;
+ BMEdge *e;
+ int found;
+
+ /* test faces around verts for verts that would be wronly killed
+ * by dissolve faces. */
+ f = BM_iter_new(&iter, bm, BM_FACES_OF_VERT, v);
+ for ( ; f; f = BM_iter_step(&iter)) {
+ l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f);
+ for ( ; l; l = BM_iter_step(&liter)) {
+ if (!BMO_elem_flag_test(bm, l->v, VERT_MARK)) {
+ /* if an edge around a vert is a boundary edge,
+ * then dissolve faces won't destroy it.
+ * also if it forms a boundary with one
+ * of the face region */
+ found = FALSE;
+ e = BM_iter_new(&iter2, bm, BM_EDGES_OF_VERT, l->v);
+ for ( ; e; e = BM_iter_step(&iter2)) {
+ if (BM_edge_face_count(e) == 1) {
+ found = TRUE;
+ }
+ f2 = BM_iter_new(&iter3, bm, BM_FACES_OF_EDGE, e);
+ for ( ; f2; f2 = BM_iter_step(&iter3)) {
+ if (!BMO_elem_flag_test(bm, f2, FACE_MARK)) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (found == TRUE) {
+ break;
+ }
+ }
+ if (found == FALSE) {
+ return FALSE;
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+void dissolveverts_exec(BMesh *bm, BMOperator *op)
+{
+ BMOpSlot *vinput;
+ BMIter iter, fiter;
+ BMVert *v;
+ BMFace *f;
+ /* int i; */
+
+ vinput = BMO_slot_get(op, "verts");
+ BMO_slot_buffer_flag_enable(bm, op, "verts", VERT_MARK, BM_VERT);
+
+ for (v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); v; v = BM_iter_step(&iter)) {
+ if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
+ /* check if it's a two-valence ver */
+ if (BM_vert_edge_count(v) == 2) {
+
+ /* collapse the ver */
+ BM_vert_collapse_faces(bm, v->e, v, 1.0f, TRUE);
+ continue;
+ }
+
+ f = BM_iter_new(&fiter, bm, BM_FACES_OF_VERT, v);
+ for ( ; f; f = BM_iter_step(&fiter)) {
+ BMO_elem_flag_enable(bm, f, FACE_ORIG);
+ BMO_elem_flag_enable(bm, f, FACE_MARK);
+ }
+
+ /* check if our additions to the input to face dissolve
+ * will destroy nonmarked vertices. */
+ if (!test_extra_verts(bm, v)) {
+ f = BM_iter_new(&fiter, bm, BM_FACES_OF_VERT, v);
+ for ( ; f; f = BM_iter_step(&fiter)) {
+ if (BMO_elem_flag_test(bm, f, FACE_ORIG)) {
+ BMO_elem_flag_disable(bm, f, FACE_MARK);
+ BMO_elem_flag_disable(bm, f, FACE_ORIG);
+ }
+ }
+ }
+ else {
+ f = BM_iter_new(&fiter, bm, BM_FACES_OF_VERT, v);
+ for ( ; f; f = BM_iter_step(&fiter)) {
+ BMO_elem_flag_disable(bm, f, FACE_ORIG);
+ }
+ }
+ }
+ }
+
+ BMO_op_callf(bm, "dissolvefaces faces=%ff", FACE_MARK);
+ if (BMO_error_occurred(bm)) {
+ const char *msg;
+
+ BMO_error_get(bm, &msg, NULL);
+ BMO_error_clear(bm);
+ BMO_error_raise(bm, op, BMERR_DISSOLVEVERTS_FAILED, msg);
+ }
+
+ /* clean up any remainin */
+ for (v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); v; v = BM_iter_step(&iter)) {
+ if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
+ if (!BM_vert_dissolve(bm, v)) {
+ BMO_error_raise(bm, op, BMERR_DISSOLVEVERTS_FAILED, NULL);
+ return;
+ }
+ }
+ }
+
+}
+
+/* this code is for cleaning up two-edged faces, it shall become
+ * it's own function one day */
+#if 0
+void dummy_exec(BMesh *bm, BMOperator *op)
+{
+ {
+ /* clean up two-edged face */
+ /* basic idea is to keep joining 2-edged faces until their
+ * gone. this however relies on joining two 2-edged faces
+ * together to work, which doesn't */
+ found3 = 1;
+ while (found3) {
+ found3 = 0;
+ for (f = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); f; f = BM_iter_step(&iter)) {
+ if (!BM_face_validate(bm, f, stderr)) {
+ printf("error.\n");
+ }
+
+ if (f->len == 2) {
+ //this design relies on join faces working
+ //with two-edged faces properly.
+ //commenting this line disables the
+ //outermost loop.
+ //found3 = 1;
+ found2 = 0;
+ l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f);
+ fe = l->e;
+ for ( ; l; l = BM_iter_step(&liter)) {
+ f2 = BM_iter_new(&fiter, bm,
+ BM_FACES_OF_EDGE, l->e);
+ for ( ; f2; f2 = BM_iter_step(&fiter)) {
+ if (f2 != f) {
+ BM_faces_join_pair(bm, f, f2, l->e);
+ found2 = 1;
+ break;
+ }
+ }
+ if (found2) break;
+ }
+
+ if (!found2) {
+ bmesh_kf(bm, f);
+ bmesh_ke(bm, fe);
+ }
+ } /* else if (f->len == 3) {
+ BMEdge *ed[3];
+ BMVert *vt[3];
+ BMLoop *lp[3];
+ int i = 0;
+
+ //check for duplicate edges
+ l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f);
+ for ( ; l; l = BM_iter_step(&liter)) {
+ ed[i] = l->e;
+ lp[i] = l;
+ vt[i++] = l->v;
+ }
+ if (vt[0] == vt[1] || vt[0] == vt[2]) {
+ i += 1;
+ }
+ */
+ }
+ }
+ if (oldlen == len) break;
+ oldlen = len;
+ }
+}
+
+#endif
+
+/**/
+typedef struct DissolveElemWeight_t {
+ BMHeader *ele;
+ float weight;
+} DissolveElemWeight_t;
+
+static int dissolve_elem_cmp(const void *a1, const void *a2)
+{
+ const struct DissolveElemWeight_t *d1 = a1, *d2 = a2;
+
+ if (d1->weight > d2->weight) return 1;
+ else if (d1->weight < d2->weight) return -1;
+ return 0;
+}
+
+void dissolvelimit_exec(BMesh *bm, BMOperator *op)
+{
+ BMOpSlot *einput = BMO_slot_get(op, "edges");
+ BMOpSlot *vinput = BMO_slot_get(op, "verts");
+ const float angle_max = (float)M_PI / 2.0f;
+ const float angle_limit = minf(angle_max, BMO_slot_float_get(op, "angle_limit"));
+ DissolveElemWeight_t *weight_elems = MEM_mallocN(MAX2(einput->len, vinput->len) *
+ sizeof(DissolveElemWeight_t), __func__);
+ int i, tot_found;
+
+ /* --- first edges --- */
+
+ /* go through and split edge */
+ for (i = 0, tot_found = 0; i < einput->len; i++) {
+ BMEdge *e = ((BMEdge **)einput->data.p)[i];
+ const float angle = BM_edge_face_angle(bm, e);
+
+ if (angle < angle_limit) {
+ weight_elems[i].ele = (BMHeader *)e;
+ weight_elems[i].weight = angle;
+ tot_found++;
+ }
+ else {
+ weight_elems[i].ele = NULL;
+ weight_elems[i].weight = angle_max;
+ }
+ }
+
+ if (tot_found != 0) {
+ qsort(weight_elems, einput->len, sizeof(DissolveElemWeight_t), dissolve_elem_cmp);
+
+ for (i = 0; i < tot_found; i++) {
+ BMEdge *e = (BMEdge *)weight_elems[i].ele;
+ /* check twice because cumulative effect could disolve over angle limit */
+ if (BM_edge_face_angle(bm, e) < angle_limit) {
+ BMFace *nf = BM_faces_join_pair(bm, e->l->f,
+ e->l->radial_next->f,
+ e); /* join faces */
+
+ /* there may be some errors, we dont mind, just move on */
+ if (nf == NULL) {
+ BMO_error_clear(bm);
+ }
+ }
+ }
+ }
+
+ /* --- second verts --- */
+ for (i = 0, tot_found = 0; i < vinput->len; i++) {
+ BMVert *v = ((BMVert **)vinput->data.p)[i];
+ const float angle = BM_vert_edge_angle(bm, v);
+
+ if (angle < angle_limit) {
+ weight_elems[i].ele = (BMHeader *)v;
+ weight_elems[i].weight = angle;
+ tot_found++;
+ }
+ else {
+ weight_elems[i].ele = NULL;
+ weight_elems[i].weight = angle_max;
+ }
+ }
+
+ if (tot_found != 0) {
+ qsort(weight_elems, vinput->len, sizeof(DissolveElemWeight_t), dissolve_elem_cmp);
+
+ for (i = 0; i < tot_found; i++) {
+ BMVert *v = (BMVert *)weight_elems[i].ele;
+ /* check twice because cumulative effect could disolve over angle limit */
+ if (BM_vert_edge_angle(bm, v) < angle_limit) {
+ BM_vert_collapse_edges(bm, v->e, v); /* join edges */
+ }
+ }
+ }
+
+ MEM_freeN(weight_elems);
+}
diff --git a/source/blender/bmesh/operators/bmo_dupe.c b/source/blender/bmesh/operators/bmo_dupe.c
new file mode 100644
index 00000000000..2e1c91d920c
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_dupe.c
@@ -0,0 +1,512 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Joseph Eagar.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+
+#include "BLI_array.h"
+#include "BLI_math.h"
+
+#include "bmesh.h"
+
+/* local flag define */
+#define DUPE_INPUT 1 /* input from operato */
+#define DUPE_NEW 2
+#define DUPE_DONE 4
+#define DUPE_MAPPED 8
+
+/*
+ * COPY VERTEX
+ *
+ * Copy an existing vertex from one bmesh to another.
+ *
+ */
+static BMVert *copy_vertex(BMesh *source_mesh, BMVert *source_vertex, BMesh *target_mesh, GHash *vhash)
+{
+ BMVert *target_vertex = NULL;
+
+ /* Create a new verte */
+ target_vertex = BM_vert_create(target_mesh, source_vertex->co, NULL);
+
+ /* Insert new vertex into the vert has */
+ BLI_ghash_insert(vhash, source_vertex, target_vertex);
+
+ /* Copy attribute */
+ BM_elem_attrs_copy(source_mesh, target_mesh, source_vertex, target_vertex);
+
+ /* Set internal op flag */
+ BMO_elem_flag_enable(target_mesh, target_vertex, DUPE_NEW);
+
+ return target_vertex;
+}
+
+/*
+ * COPY EDGE
+ *
+ * Copy an existing edge from one bmesh to another.
+ *
+ */
+static BMEdge *copy_edge(BMOperator *op, BMesh *source_mesh,
+ BMEdge *source_edge, BMesh *target_mesh,
+ GHash *vhash, GHash *ehash)
+{
+ BMEdge *target_edge = NULL;
+ BMVert *target_vert1, *target_vert2;
+ BMFace *face;
+ BMIter fiter;
+ int rlen;
+
+ /* see if any of the neighboring faces are
+ * not being duplicated. in that case,
+ * add it to the new/old map. */
+ rlen = 0;
+ for (face = BM_iter_new(&fiter, source_mesh, BM_FACES_OF_EDGE, source_edge);
+ face;
+ face = BM_iter_step(&fiter))
+ {
+ if (BMO_elem_flag_test(source_mesh, face, DUPE_INPUT)) {
+ rlen++;
+ }
+ }
+
+ /* Lookup v1 and v2 */
+ target_vert1 = BLI_ghash_lookup(vhash, source_edge->v1);
+ target_vert2 = BLI_ghash_lookup(vhash, source_edge->v2);
+
+ /* Create a new edg */
+ target_edge = BM_edge_create(target_mesh, target_vert1, target_vert2, NULL, FALSE);
+
+ /* add to new/old edge map if necassar */
+ if (rlen < 2) {
+ /* not sure what non-manifold cases of greater then three
+ * radial should do. */
+ BMO_slot_map_ptr_insert(source_mesh, op, "boundarymap",
+ source_edge, target_edge);
+ }
+
+ /* Insert new edge into the edge hash */
+ BLI_ghash_insert(ehash, source_edge, target_edge);
+
+ /* Copy attributes */
+ BM_elem_attrs_copy(source_mesh, target_mesh, source_edge, target_edge);
+
+ /* Set internal op flags */
+ BMO_elem_flag_enable(target_mesh, target_edge, DUPE_NEW);
+
+ return target_edge;
+}
+
+/*
+ * COPY FACE
+ *
+ * Copy an existing face from one bmesh to another.
+ */
+
+static BMFace *copy_face(BMOperator *op, BMesh *source_mesh,
+ BMFace *source_face, BMesh *target_mesh,
+ BMVert **vtar, BMEdge **edar, GHash *vhash, GHash *ehash)
+{
+ /* BMVert *target_vert1, *target_vert2; */ /* UNUSED */
+ BMLoop *source_loop, *target_loop;
+ BMFace *target_face = NULL;
+ BMIter iter, iter2;
+ int i;
+
+ /* lookup the first and second vert */
+#if 0 /* UNUSED */
+ target_vert1 = BLI_ghash_lookup(vhash, BM_iter_new(&iter, source_mesh, BM_VERTS_OF_FACE, source_face));
+ target_vert2 = BLI_ghash_lookup(vhash, BM_iter_step(&iter));
+#else
+ BM_iter_new(&iter, source_mesh, BM_VERTS_OF_FACE, source_face);
+ BM_iter_step(&iter);
+#endif
+
+ /* lookup edge */
+ for (i = 0, source_loop = BM_iter_new(&iter, source_mesh, BM_LOOPS_OF_FACE, source_face);
+ source_loop;
+ source_loop = BM_iter_step(&iter), i++)
+ {
+ vtar[i] = BLI_ghash_lookup(vhash, source_loop->v);
+ edar[i] = BLI_ghash_lookup(ehash, source_loop->e);
+ }
+
+ /* create new fac */
+ target_face = BM_face_create(target_mesh, vtar, edar, source_face->len, FALSE);
+ BMO_slot_map_ptr_insert(source_mesh, op,
+ "facemap", source_face, target_face);
+ BMO_slot_map_ptr_insert(source_mesh, op,
+ "facemap", target_face, source_face);
+
+ BM_elem_attrs_copy(source_mesh, target_mesh, source_face, target_face);
+
+ /* mark the face for outpu */
+ BMO_elem_flag_enable(target_mesh, target_face, DUPE_NEW);
+
+ /* copy per-loop custom dat */
+ BM_ITER(source_loop, &iter, source_mesh, BM_LOOPS_OF_FACE, source_face) {
+ BM_ITER(target_loop, &iter2, target_mesh, BM_LOOPS_OF_FACE, target_face) {
+ if (BLI_ghash_lookup(vhash, source_loop->v) == target_loop->v) {
+ BM_elem_attrs_copy(source_mesh, target_mesh, source_loop, target_loop);
+ break;
+ }
+ }
+ }
+
+ return target_face;
+}
+
+/*
+ * COPY MESH
+ *
+ * Internal Copy function.
+ */
+static void copy_mesh(BMOperator *op, BMesh *source, BMesh *target)
+{
+
+ BMVert *v = NULL, *v2;
+ BMEdge *e = NULL;
+ BMFace *f = NULL;
+
+ BLI_array_declare(vtar);
+ BLI_array_declare(edar);
+ BMVert **vtar = NULL;
+ BMEdge **edar = NULL;
+
+ BMIter verts;
+ BMIter edges;
+ BMIter faces;
+
+ GHash *vhash;
+ GHash *ehash;
+
+ /* initialize pointer hashe */
+ vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh dupeops v");
+ ehash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh dupeops e");
+
+ for (v = BM_iter_new(&verts, source, BM_VERTS_OF_MESH, source); v; v = BM_iter_step(&verts)) {
+ if ( BMO_elem_flag_test(source, v, DUPE_INPUT) &&
+ !BMO_elem_flag_test(source, v, DUPE_DONE))
+ {
+ BMIter iter;
+ int iso = 1;
+
+ v2 = copy_vertex(source, v, target, vhash);
+
+ BM_ITER(f, &iter, source, BM_FACES_OF_VERT, v) {
+ if (BMO_elem_flag_test(source, f, DUPE_INPUT)) {
+ iso = 0;
+ break;
+ }
+ }
+
+ if (iso) {
+ BM_ITER(e, &iter, source, BM_EDGES_OF_VERT, v) {
+ if (BMO_elem_flag_test(source, e, DUPE_INPUT)) {
+ iso = 0;
+ break;
+ }
+ }
+ }
+
+ if (iso) {
+ BMO_slot_map_ptr_insert(source, op, "isovertmap", v, v2);
+ }
+
+ BMO_elem_flag_enable(source, v, DUPE_DONE);
+ }
+ }
+
+ /* now we dupe all the edge */
+ for (e = BM_iter_new(&edges, source, BM_EDGES_OF_MESH, source); e; e = BM_iter_step(&edges)) {
+ if ( BMO_elem_flag_test(source, e, DUPE_INPUT) &&
+ !BMO_elem_flag_test(source, e, DUPE_DONE))
+ {
+ /* make sure that verts are copie */
+ if (!BMO_elem_flag_test(source, e->v1, DUPE_DONE)) {
+ copy_vertex(source, e->v1, target, vhash);
+ BMO_elem_flag_enable(source, e->v1, DUPE_DONE);
+ }
+ if (!BMO_elem_flag_test(source, e->v2, DUPE_DONE)) {
+ copy_vertex(source, e->v2, target, vhash);
+ BMO_elem_flag_enable(source, e->v2, DUPE_DONE);
+ }
+ /* now copy the actual edg */
+ copy_edge(op, source, e, target, vhash, ehash);
+ BMO_elem_flag_enable(source, e, DUPE_DONE);
+ }
+ }
+
+ /* first we dupe all flagged faces and their elements from sourc */
+ for (f = BM_iter_new(&faces, source, BM_FACES_OF_MESH, source); f; f = BM_iter_step(&faces)) {
+ if (BMO_elem_flag_test(source, f, DUPE_INPUT)) {
+ /* vertex pas */
+ for (v = BM_iter_new(&verts, source, BM_VERTS_OF_FACE, f); v; v = BM_iter_step(&verts)) {
+ if (!BMO_elem_flag_test(source, v, DUPE_DONE)) {
+ copy_vertex(source, v, target, vhash);
+ BMO_elem_flag_enable(source, v, DUPE_DONE);
+ }
+ }
+
+ /* edge pas */
+ for (e = BM_iter_new(&edges, source, BM_EDGES_OF_FACE, f); e; e = BM_iter_step(&edges)) {
+ if (!BMO_elem_flag_test(source, e, DUPE_DONE)) {
+ copy_edge(op, source, e, target, vhash, ehash);
+ BMO_elem_flag_enable(source, e, DUPE_DONE);
+ }
+ }
+
+ /* ensure arrays are the right size */
+ BLI_array_empty(vtar);
+ BLI_array_empty(edar);
+
+ BLI_array_growitems(vtar, f->len);
+ BLI_array_growitems(edar, f->len);
+
+ copy_face(op, source, f, target, vtar, edar, vhash, ehash);
+ BMO_elem_flag_enable(source, f, DUPE_DONE);
+ }
+ }
+
+ /* free pointer hashe */
+ BLI_ghash_free(vhash, NULL, NULL);
+ BLI_ghash_free(ehash, NULL, NULL);
+
+ BLI_array_free(vtar); /* free vert pointer array */
+ BLI_array_free(edar); /* free edge pointer array */
+}
+
+/*
+ * Duplicate Operator
+ *
+ * Duplicates verts, edges and faces of a mesh.
+ *
+ * INPUT SLOTS:
+ *
+ * BMOP_DUPE_VINPUT: Buffer containing pointers to mesh vertices to be duplicated
+ * BMOP_DUPE_EINPUT: Buffer containing pointers to mesh edges to be duplicated
+ * BMOP_DUPE_FINPUT: Buffer containing pointers to mesh faces to be duplicated
+ *
+ * OUTPUT SLOTS:
+ *
+ * BMOP_DUPE_VORIGINAL: Buffer containing pointers to the original mesh vertices
+ * BMOP_DUPE_EORIGINAL: Buffer containing pointers to the original mesh edges
+ * BMOP_DUPE_FORIGINAL: Buffer containing pointers to the original mesh faces
+ * BMOP_DUPE_VNEW: Buffer containing pointers to the new mesh vertices
+ * BMOP_DUPE_ENEW: Buffer containing pointers to the new mesh edges
+ * BMOP_DUPE_FNEW: Buffer containing pointers to the new mesh faces
+ *
+ */
+
+void dupeop_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator *dupeop = op;
+ BMesh *bm2 = BMO_slot_ptr_get(op, "dest");
+
+ if (!bm2)
+ bm2 = bm;
+
+ /* flag inpu */
+ BMO_slot_buffer_flag_enable(bm, dupeop, "geom", DUPE_INPUT, BM_ALL);
+
+ /* use the internal copy functio */
+ copy_mesh(dupeop, bm, bm2);
+
+ /* Outpu */
+ /* First copy the input buffers to output buffers - original dat */
+ BMO_slot_copy(dupeop, dupeop, "geom", "origout");
+
+ /* Now alloc the new output buffer */
+ BMO_slot_from_flag(bm, dupeop, "newout", DUPE_NEW, BM_ALL);
+}
+
+#if 0 /* UNUSED */
+/* executes the duplicate operation, feeding elements of
+ * type flag etypeflag and header flag flag to it. note,
+ * to get more useful information (such as the mapping from
+ * original to new elements) you should run the dupe op manually */
+void BMO_dupe_from_flag(BMesh *bm, int etypeflag, const char hflag)
+{
+ BMOperator dupeop;
+
+ BMO_op_init(bm, &dupeop, "dupe");
+ BMO_slot_from_hflag(bm, &dupeop, "geom", hflag, etypeflag);
+
+ BMO_op_exec(bm, &dupeop);
+ BMO_op_finish(bm, &dupeop);
+}
+#endif
+
+/*
+ * Split Operator
+ *
+ * Duplicates verts, edges and faces of a mesh but also deletes the originals.
+ *
+ * INPUT SLOTS:
+ *
+ * BMOP_DUPE_VINPUT: Buffer containing pointers to mesh vertices to be split
+ * BMOP_DUPE_EINPUT: Buffer containing pointers to mesh edges to be split
+ * BMOP_DUPE_FINPUT: Buffer containing pointers to mesh faces to be split
+ *
+ * OUTPUT SLOTS:
+ *
+ * BMOP_DUPE_VOUTPUT: Buffer containing pointers to the split mesh vertices
+ * BMOP_DUPE_EOUTPUT: Buffer containing pointers to the split mesh edges
+ * BMOP_DUPE_FOUTPUT: Buffer containing pointers to the split mesh faces
+ */
+
+#define SPLIT_INPUT 1
+void splitop_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator *splitop = op;
+ BMOperator dupeop;
+ BMOperator delop;
+ BMVert *v;
+ BMEdge *e;
+ BMFace *f;
+ BMIter iter, iter2;
+ int found;
+
+ /* initialize our sub-operator */
+ BMO_op_init(bm, &dupeop, "dupe");
+ BMO_op_init(bm, &delop, "del");
+
+ BMO_slot_copy(splitop, &dupeop, "geom", "geom");
+ BMO_op_exec(bm, &dupeop);
+
+ BMO_slot_buffer_flag_enable(bm, splitop, "geom", SPLIT_INPUT, BM_ALL);
+
+ /* make sure to remove edges and verts we don't need */
+ for (e = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, NULL); e; e = BM_iter_step(&iter)) {
+ found = 0;
+ f = BM_iter_new(&iter2, bm, BM_FACES_OF_EDGE, e);
+ for ( ; f; f = BM_iter_step(&iter2)) {
+ if (!BMO_elem_flag_test(bm, f, SPLIT_INPUT)) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) BMO_elem_flag_enable(bm, e, SPLIT_INPUT);
+ }
+
+ for (v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); v; v = BM_iter_step(&iter)) {
+ found = 0;
+ e = BM_iter_new(&iter2, bm, BM_EDGES_OF_VERT, v);
+ for ( ; e; e = BM_iter_step(&iter2)) {
+ if (!BMO_elem_flag_test(bm, e, SPLIT_INPUT)) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) BMO_elem_flag_enable(bm, v, SPLIT_INPUT);
+
+ }
+
+ /* connect outputs of dupe to delete, exluding keep geometr */
+ BMO_slot_int_set(&delop, "context", DEL_FACES);
+ BMO_slot_from_flag(bm, &delop, "geom", SPLIT_INPUT, BM_ALL);
+
+ BMO_op_exec(bm, &delop);
+
+ /* now we make our outputs by copying the dupe output */
+ BMO_slot_copy(&dupeop, splitop, "newout", "geomout");
+ BMO_slot_copy(&dupeop, splitop, "boundarymap",
+ "boundarymap");
+ BMO_slot_copy(&dupeop, splitop, "isovertmap",
+ "isovertmap");
+
+ /* cleanu */
+ BMO_op_finish(bm, &delop);
+ BMO_op_finish(bm, &dupeop);
+}
+
+
+void delop_exec(BMesh *bm, BMOperator *op)
+{
+#define DEL_INPUT 1
+
+ BMOperator *delop = op;
+
+ /* Mark Buffer */
+ BMO_slot_buffer_flag_enable(bm, delop, "geom", DEL_INPUT, BM_ALL);
+
+ BMO_remove_tagged_context(bm, DEL_INPUT, BMO_slot_int_get(op, "context"));
+
+#undef DEL_INPUT
+}
+
+/*
+ * Spin Operator
+ *
+ * Extrude or duplicate geometry a number of times,
+ * rotating and possibly translating after each step
+ */
+
+void spinop_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator dupop, extop;
+ float cent[3], dvec[3];
+ float axis[3] = {0.0f, 0.0f, 1.0f};
+ float q[4];
+ float rmat[3][3];
+ float phi, si;
+ int steps, dupli, a, usedvec;
+
+ BMO_slot_vec_get(op, "cent", cent);
+ BMO_slot_vec_get(op, "axis", axis);
+ normalize_v3(axis);
+ BMO_slot_vec_get(op, "dvec", dvec);
+ usedvec = !is_zero_v3(dvec);
+ steps = BMO_slot_int_get(op, "steps");
+ phi = BMO_slot_float_get(op, "ang") * (float)M_PI / (360.0f * steps);
+ dupli = BMO_slot_int_get(op, "dupli");
+
+ si = (float)sin(phi);
+ q[0] = (float)cos(phi);
+ q[1] = axis[0]*si;
+ q[2] = axis[1]*si;
+ q[3] = axis[2]*si;
+ quat_to_mat3(rmat, q);
+
+ BMO_slot_copy(op, op, "geom", "lastout");
+ for (a = 0; a < steps; a++) {
+ if (dupli) {
+ BMO_op_initf(bm, &dupop, "dupe geom=%s", op, "lastout");
+ BMO_op_exec(bm, &dupop);
+ BMO_op_callf(bm, "rotate cent=%v mat=%m3 verts=%s",
+ cent, rmat, &dupop, "newout");
+ BMO_slot_copy(&dupop, op, "newout", "lastout");
+ BMO_op_finish(bm, &dupop);
+ }
+ else {
+ BMO_op_initf(bm, &extop, "extrudefaceregion edgefacein=%s",
+ op, "lastout");
+ BMO_op_exec(bm, &extop);
+ BMO_op_callf(bm, "rotate cent=%v mat=%m3 verts=%s",
+ cent, rmat, &extop, "geomout");
+ BMO_slot_copy(&extop, op, "geomout", "lastout");
+ BMO_op_finish(bm, &extop);
+ }
+
+ if (usedvec)
+ BMO_op_callf(bm, "translate vec=%v verts=%s", dvec, op, "lastout");
+ }
+}
diff --git a/source/blender/bmesh/operators/bmo_edgesplit.c b/source/blender/bmesh/operators/bmo_edgesplit.c
new file mode 100644
index 00000000000..7d719e1ddc9
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_edgesplit.c
@@ -0,0 +1,426 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Joseph Eagar
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_array.h"
+
+#include "bmesh.h"
+
+#include "bmesh_operators_private.h" /* own include */
+
+typedef struct EdgeTag {
+ BMVert *newv1, *newv2;
+ BMEdge *newe1, *newe2;
+ int tag;
+} EdgeTag;
+
+/* (EDGE_DEL == FACE_DEL) - this must be the case */
+#define EDGE_DEL 1
+#define EDGE_SEAM 2
+#define EDGE_MARK 4
+#define EDGE_RET1 8
+#define EDGE_RET2 16
+
+#define FACE_DEL 1
+#define FACE_NEW 2
+
+static BMFace *remake_face(BMesh *bm, EdgeTag *etags, BMFace *f, BMVert **verts, BMEdge **edges_tmp)
+{
+ BMIter liter1, liter2;
+ EdgeTag *et;
+ BMFace *f2;
+ BMLoop *l, *l2;
+ BMEdge *e;
+ BMVert *lastv1, *lastv2 /* , *v1, *v2 */ /* UNUSED */;
+ int i;
+
+ /* we do final edge last */
+ lastv1 = verts[f->len - 1];
+ lastv2 = verts[0];
+ /* v1 = verts[0]; */ /* UNUSED */
+ /* v2 = verts[1]; */ /* UNUSED */
+ for (i = 0; i < f->len - 1; i++) {
+ e = BM_edge_create(bm, verts[i], verts[i + 1], NULL, TRUE);
+ if (!e) {
+ return NULL;
+ }
+ edges_tmp[i] = e;
+ }
+
+ edges_tmp[i] = BM_edge_create(bm, lastv1, lastv2, NULL, TRUE);
+
+ f2 = BM_face_create(bm, verts, edges_tmp, f->len, FALSE);
+ if (!f2) {
+ return NULL;
+ }
+
+ BM_elem_attrs_copy(bm, bm, f, f2);
+
+ l = BM_iter_new(&liter1, bm, BM_LOOPS_OF_FACE, f);
+ l2 = BM_iter_new(&liter2, bm, BM_LOOPS_OF_FACE, f2);
+ for ( ; l && l2; l = BM_iter_step(&liter1), l2 = BM_iter_step(&liter2)) {
+ BM_elem_attrs_copy(bm, bm, l, l2);
+ if (l->e != l2->e) {
+ /* set up data for figuring out the two sides of
+ * the split */
+
+ /* set edges index as dirty after running all */
+ BM_elem_index_set(l2->e, BM_elem_index_get(l->e)); /* set_dirty! */
+ et = &etags[BM_elem_index_get(l->e)];
+
+ if (!et->newe1) {
+ et->newe1 = l2->e;
+ }
+ else if (!et->newe2) {
+ et->newe2 = l2->e;
+ }
+ else {
+ /* Only two new edges should be created from each original edge
+ * for edge split operation */
+
+ //BLI_assert(et->newe1 == l2->e || et->newe2 == l2->e);
+ et->newe2 = l2->e;
+ }
+
+ if (BMO_elem_flag_test(bm, l->e, EDGE_SEAM)) {
+ BMO_elem_flag_enable(bm, l2->e, EDGE_SEAM);
+ }
+
+ BM_elem_attrs_copy(bm, bm, l->e, l2->e);
+ }
+
+ BMO_elem_flag_enable(bm, l->e, EDGE_MARK);
+ BMO_elem_flag_enable(bm, l2->e, EDGE_MARK);
+ }
+
+ return f2;
+}
+
+static void tag_out_edges(BMesh *bm, EdgeTag *etags, BMOperator *UNUSED(op))
+{
+ EdgeTag *et;
+ BMIter iter;
+ BMLoop *l, *startl;
+ BMEdge *e;
+ BMVert *v;
+ int i, ok;
+
+ ok = 0;
+ while (ok++ < 100000) {
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, e, EDGE_SEAM))
+ continue;
+
+ et = &etags[BM_elem_index_get(e)];
+ if (!et->tag && e->l) {
+ break;
+ }
+ }
+
+ if (!e) {
+ break;
+ }
+
+ /* ok we found an edge, part of a region of splits we need
+ * to identify. now walk along it */
+ for (i = 0; i < 2; i++) {
+ l = e->l;
+
+ v = i ? l->next->v : l->v;
+
+ while (1) {
+ et = &etags[BM_elem_index_get(l->e)];
+ if (et->newe1 == l->e) {
+ if (et->newe1) {
+ BMO_elem_flag_enable(bm, et->newe1, EDGE_RET1);
+ BMO_elem_flag_disable(bm, et->newe1, EDGE_SEAM);
+ }
+ if (et->newe2) {
+ BMO_elem_flag_enable(bm, et->newe2, EDGE_RET2);
+ BMO_elem_flag_disable(bm, et->newe2, EDGE_SEAM);
+ }
+ }
+ else {
+ if (et->newe1) {
+ BMO_elem_flag_enable(bm, et->newe1, EDGE_RET2);
+ BMO_elem_flag_disable(bm, et->newe1, EDGE_SEAM);
+ }
+ if (et->newe2) {
+ BMO_elem_flag_enable(bm, et->newe2, EDGE_RET1);
+ BMO_elem_flag_disable(bm, et->newe2, EDGE_SEAM);
+ }
+ }
+
+ /* If the original edge was non-manifold edges, then it is
+ * possible l->e is not et->newe1 or et->newe2. So always clear
+ * the flag on l->e as well, to prevent infinite looping. */
+ BMO_elem_flag_disable(bm, l->e, EDGE_SEAM);
+
+ startl = l;
+ do {
+ l = BM_face_other_loop(l->e, l->f, v);
+ if (l == startl || BM_edge_face_count(l->e) != 2) {
+ break;
+ }
+ l = l->radial_next;
+ } while (l != startl && !BMO_elem_flag_test(bm, l->e, EDGE_SEAM));
+
+ if (l == startl || !BMO_elem_flag_test(bm, l->e, EDGE_SEAM)) {
+ break;
+ }
+
+ v = (l->v == v) ? l->next->v : l->v;
+ }
+ }
+ }
+}
+
+void bmesh_edgesplitop_exec(BMesh *bm, BMOperator *op)
+{
+ EdgeTag *etags, *et;
+ BMIter iter, liter;
+ BMOIter siter;
+ BMFace *f, *f2;
+ BMLoop *l, *nextl, *prevl, *l2, *l3;
+ BMEdge *e, *e2;
+ BMVert *v, *v2, **verts = NULL;
+ BLI_array_declare(verts);
+ BMEdge **edges_tmp = NULL;
+ BLI_array_declare(edges_tmp);
+ int i, j;
+
+ BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_SEAM, BM_EDGE);
+
+ /* single marked edges unconnected to any other marked edges
+ * are illegal, go through and unmark them */
+ BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
+ for (i = 0; i < 2; i++) {
+ BM_ITER(e2, &iter, bm, BM_EDGES_OF_VERT, i ? e->v2 : e->v1) {
+ if (e != e2 && BMO_elem_flag_test(bm, e2, EDGE_SEAM)) {
+ break;
+ }
+ }
+ if (e2) {
+ break;
+ }
+ }
+
+ if (!e2) {
+ BMO_elem_flag_disable(bm, e, EDGE_SEAM);
+ }
+ }
+
+ etags = MEM_callocN(sizeof(EdgeTag)*bm->totedge, "EdgeTag");
+
+ BM_mesh_elem_index_ensure(bm, BM_EDGE);
+
+#ifdef ETV
+# undef ETV
+#endif
+#ifdef SETETV
+# undef SETETV
+#endif
+
+#define ETV(et, v, l) (l->e->v1 == v ? et->newv1 : et->newv2)
+#define SETETV(et, v, l, vs) l->e->v1 == v ? (et->newv1 = vs) : (et->newv2 = vs)
+
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+
+ if (BMO_elem_flag_test(bm, f, FACE_NEW)) {
+ continue;
+ }
+
+ BLI_array_empty(verts);
+ BLI_array_growitems(verts, f->len);
+ memset(verts, 0, sizeof(BMVert *) * f->len);
+
+ /* this is passed onto remake_face() so it doesnt need to allocate
+ * a new array on each call. */
+ BLI_array_empty(edges_tmp);
+ BLI_array_growitems(edges_tmp, f->len);
+
+ i = 0;
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ if (!BMO_elem_flag_test(bm, l->e, EDGE_SEAM)) {
+ if (!verts[i]) {
+
+ et = &etags[BM_elem_index_get(l->e)];
+ if (ETV(et, l->v, l)) {
+ verts[i] = ETV(et, l->v, l);
+ }
+ else
+ {
+ verts[i] = l->v;
+ }
+ }
+ i++;
+ continue;
+ }
+
+ nextl = l->next;
+ prevl = l->prev;
+
+ for (j = 0; j < 2; j++) {
+ /* correct as long as i & j dont change during the loop */
+ const int fv_index = j ? (i + 1) % f->len : i; /* face vert index */
+ l2 = j ? nextl : prevl;
+ v = j ? l2->v : l->v;
+
+ if (BMO_elem_flag_test(bm, l2->e, EDGE_SEAM)) {
+ if (verts[fv_index] == NULL) {
+ /* make unique vert here for this face only */
+ v2 = BM_vert_create(bm, v->co, v);
+ verts[fv_index] = v2;
+ }
+ else {
+ v2 = verts[fv_index];
+ }
+ }
+ else {
+ /* generate unique vert for non-seam edge(s)
+ * around the manifold vert fan if necassary */
+
+ /* first check that we have two seam edges
+ * somewhere within this fa */
+ l3 = l2;
+ do {
+ if (BM_edge_face_count(l3->e) != 2) {
+ /* if we hit a boundary edge, tag
+ * l3 as null so we know to disconnect
+ * it */
+ if (BM_edge_face_count(l3->e) == 1) {
+ l3 = NULL;
+ }
+ break;
+ }
+
+ l3 = l3->radial_next;
+ l3 = BM_face_other_loop(l3->e, l3->f, v);
+ } while (l3 != l2 && !BMO_elem_flag_test(bm, l3->e, EDGE_SEAM));
+
+ if (l3 == NULL || (BMO_elem_flag_test(bm, l3->e, EDGE_SEAM) && l3->e != l->e)) {
+ et = &etags[BM_elem_index_get(l2->e)];
+ if (ETV(et, v, l2) == NULL) {
+ v2 = BM_vert_create(bm, v->co, v);
+
+ l3 = l2;
+ do {
+ SETETV(et, v, l3, v2);
+ if (BM_edge_face_count(l3->e) != 2) {
+ break;
+ }
+
+ l3 = l3->radial_next;
+ l3 = BM_face_other_loop(l3->e, l3->f, v);
+
+ et = &etags[BM_elem_index_get(l3->e)];
+ } while (l3 != l2 && !BMO_elem_flag_test(bm, l3->e, EDGE_SEAM));
+ }
+ else {
+ v2 = ETV(et, v, l2);
+ }
+
+ verts[fv_index] = v2;
+ }
+ else {
+ verts[fv_index] = v;
+ }
+ }
+ }
+
+ i++;
+ }
+
+ /* debugging code, quick way to find the face/vert combination
+ * which is failing assuming quads start planer - campbell */
+#if 0
+ if (f->len == 4) {
+ float no1[3];
+ float no2[3];
+ float angle_error;
+ printf(" ** found QUAD\n");
+ normal_tri_v3(no1, verts[0]->co, verts[1]->co, verts[2]->co);
+ normal_tri_v3(no2, verts[0]->co, verts[2]->co, verts[3]->co);
+ if ((angle_error = angle_v3v3(no1, no2)) > 0.05) {
+ printf(" ERROR %.4f\n", angle_error);
+ print_v3("0", verts[0]->co);
+ print_v3("1", verts[1]->co);
+ print_v3("2", verts[2]->co);
+ print_v3("3", verts[3]->co);
+
+ }
+ }
+ else {
+ printf(" ** fount %d len face\n", f->len);
+ }
+#endif
+
+ f2 = remake_face(bm, etags, f, verts, edges_tmp);
+ if (!f2) {
+ continue;
+ }
+
+ BMO_elem_flag_enable(bm, f, FACE_DEL);
+ BMO_elem_flag_enable(bm, f2, FACE_NEW);
+ }
+
+ /* remake_face() sets invalid indecies,
+ * likely these will be corrected on operator exit anyway */
+ bm->elem_index_dirty &= ~BM_EDGE;
+
+ /* cant call the operator because 'tag_out_edges'
+ * relies on original index values, from before editing geometry */
+
+#if 0
+ BMO_op_callf(bm, "del geom=%ff context=%i", FACE_DEL, DEL_ONLYFACES);
+#else
+ BMO_remove_tagged_context(bm, FACE_DEL, DEL_ONLYFACES);
+#endif
+
+ /* test EDGE_MARK'd edges if we need to delete them, EDGE_MARK
+ * is set in remake_face */
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, e, EDGE_MARK)) {
+ if (!e->l) {
+ BMO_elem_flag_enable(bm, e, EDGE_DEL);
+ }
+ }
+ }
+
+#if 0
+ BMO_op_callf(bm, "del geom=%fe context=%i", EDGE_DEL, DEL_EDGES);
+#else
+ BMO_remove_tagged_context(bm, EDGE_DEL, DEL_EDGES);
+#endif
+
+ tag_out_edges(bm, etags, op);
+ BMO_slot_from_flag(bm, op, "edgeout1", EDGE_RET1, BM_EDGE);
+ BMO_slot_from_flag(bm, op, "edgeout2", EDGE_RET2, BM_EDGE);
+
+ BLI_array_free(verts);
+ BLI_array_free(edges_tmp);
+ if (etags) MEM_freeN(etags);
+}
+
+#undef ETV
+#undef SETETV
diff --git a/source/blender/bmesh/operators/bmo_extrude.c b/source/blender/bmesh/operators/bmo_extrude.c
new file mode 100644
index 00000000000..b6a87e604ec
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_extrude.c
@@ -0,0 +1,591 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Joseph Eagar.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_array.h"
+
+#include "bmesh.h"
+
+#include "bmesh_operators_private.h" /* own include */
+
+#define EXT_INPUT 1
+#define EXT_KEEP 2
+#define EXT_DEL 4
+
+#define VERT_MARK 1
+#define EDGE_MARK 1
+#define FACE_MARK 1
+#define VERT_NONMAN 2
+#define EDGE_NONMAN 2
+
+void bmesh_extrude_face_indiv_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMIter liter, liter2;
+ BMFace *f, *f2, *f3;
+ BMLoop *l, *l2, *l3, *l4, *l_tmp;
+ BMEdge **edges = NULL, *e, *laste;
+ BMVert *v, *lastv, *firstv;
+ BLI_array_declare(edges);
+ int i;
+
+ BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) {
+ BLI_array_empty(edges);
+ i = 0;
+ firstv = lastv = NULL;
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ BLI_array_growone(edges);
+
+ v = BM_vert_create(bm, l->v->co, l->v);
+
+ if (lastv) {
+ e = BM_edge_create(bm, lastv, v, l->e, FALSE);
+ edges[i++] = e;
+ }
+
+ lastv = v;
+ laste = l->e;
+ if (!firstv) firstv = v;
+ }
+
+ BLI_array_growone(edges);
+ e = BM_edge_create(bm, v, firstv, laste, FALSE);
+ edges[i++] = e;
+
+ BMO_elem_flag_enable(bm, f, EXT_DEL);
+
+ f2 = BM_face_create_ngon(bm, firstv, BM_edge_other_vert(edges[0], firstv), edges, f->len, FALSE);
+ if (!f2) {
+ BMO_error_raise(bm, op, BMERR_MESH_ERROR, "Extrude failed; could not create face");
+ BLI_array_free(edges);
+ return;
+ }
+
+ BMO_elem_flag_enable(bm, f2, EXT_KEEP);
+ BM_elem_attrs_copy(bm, bm, f, f2);
+
+ l2 = BM_iter_new(&liter2, bm, BM_LOOPS_OF_FACE, f2);
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ BM_elem_attrs_copy(bm, bm, l, l2);
+
+ l3 = l->next;
+ l4 = l2->next;
+
+ f3 = BM_face_create_quad_tri(bm, l3->v, l4->v, l2->v, l->v, f, FALSE);
+
+ l_tmp = BM_FACE_FIRST_LOOP(f3);
+
+ BM_elem_attrs_copy(bm, bm, l->next, l_tmp); l_tmp = l_tmp->next;
+ BM_elem_attrs_copy(bm, bm, l->next, l_tmp); l_tmp = l_tmp->next;
+ BM_elem_attrs_copy(bm, bm, l, l_tmp); l_tmp = l_tmp->next;
+ BM_elem_attrs_copy(bm, bm, l, l_tmp);
+
+ l2 = BM_iter_step(&liter2);
+ }
+ }
+
+ BLI_array_free(edges);
+
+ BMO_op_callf(bm, "del geom=%ff context=%d", EXT_DEL, DEL_ONLYFACES);
+ BMO_slot_from_flag(bm, op, "faceout", EXT_KEEP, BM_FACE);
+}
+
+void bmesh_extrude_onlyedge_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMOperator dupeop;
+ BMVert *v1, *v2, *v3, *v4;
+ BMEdge *e, *e2;
+ BMFace *f;
+
+ BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
+ BMO_elem_flag_enable(bm, e, EXT_INPUT);
+ BMO_elem_flag_enable(bm, e->v1, EXT_INPUT);
+ BMO_elem_flag_enable(bm, e->v2, EXT_INPUT);
+ }
+
+ BMO_op_initf(bm, &dupeop, "dupe geom=%fve", EXT_INPUT);
+ BMO_op_exec(bm, &dupeop);
+
+ e = BMO_iter_new(&siter, bm, &dupeop, "boundarymap", 0);
+ for ( ; e; e = BMO_iter_step(&siter)) {
+ e2 = BMO_iter_map_value(&siter);
+ e2 = *(BMEdge **)e2;
+
+ if (e->l && e->v1 != e->l->v) {
+ v1 = e->v1;
+ v2 = e->v2;
+ v3 = e2->v2;
+ v4 = e2->v1;
+ }
+ else {
+ v1 = e2->v1;
+ v2 = e2->v2;
+ v3 = e->v2;
+ v4 = e->v1;
+ }
+ /* not sure what to do about example face, pass NULL for now */
+ f = BM_face_create_quad_tri(bm, v1, v2, v3, v4, NULL, FALSE);
+
+ if (BMO_elem_flag_test(bm, e, EXT_INPUT))
+ e = e2;
+
+ BMO_elem_flag_enable(bm, f, EXT_KEEP);
+ BMO_elem_flag_enable(bm, e, EXT_KEEP);
+ BMO_elem_flag_enable(bm, e->v1, EXT_KEEP);
+ BMO_elem_flag_enable(bm, e->v2, EXT_KEEP);
+
+ }
+
+ BMO_op_finish(bm, &dupeop);
+
+ BMO_slot_from_flag(bm, op, "geomout", EXT_KEEP, BM_ALL);
+}
+
+void extrude_vert_indiv_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMVert *v, *dupev;
+ BMEdge *e;
+
+ v = BMO_iter_new(&siter, bm, op, "verts", BM_VERT);
+ for ( ; v; v = BMO_iter_step(&siter)) {
+ dupev = BM_vert_create(bm, v->co, v);
+
+ e = BM_edge_create(bm, v, dupev, NULL, FALSE);
+
+ BMO_elem_flag_enable(bm, e, EXT_KEEP);
+ BMO_elem_flag_enable(bm, dupev, EXT_KEEP);
+ }
+
+ BMO_slot_from_flag(bm, op, "vertout", EXT_KEEP, BM_VERT);
+ BMO_slot_from_flag(bm, op, "edgeout", EXT_KEEP, BM_EDGE);
+}
+
+void extrude_edge_context_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator dupeop, delop;
+ BMOIter siter;
+ BMIter iter, fiter, viter;
+ BMEdge *e, *newedge;
+ BMLoop *l, *l2;
+ BMVert *verts[4], *v, *v2;
+ BMFace *f;
+ int rlen, found, fwd, delorig = 0;
+
+ /* initialize our sub-operators */
+ BMO_op_init(bm, &dupeop, "dupe");
+
+ BMO_slot_buffer_flag_enable(bm, op, "edgefacein", EXT_INPUT, BM_EDGE|BM_FACE);
+
+ /* if one flagged face is bordered by an unflagged face, then we delete
+ * original geometry unless caller explicitly asked to keep it. */
+ if (!BMO_slot_int_get(op, "alwayskeeporig")) {
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, e, EXT_INPUT)) continue;
+
+ found = 0;
+ f = BM_iter_new(&fiter, bm, BM_FACES_OF_EDGE, e);
+ for (rlen = 0; f; f = BM_iter_step(&fiter), rlen++) {
+ if (!BMO_elem_flag_test(bm, f, EXT_INPUT)) {
+ found = 1;
+ delorig = 1;
+ break;
+ }
+ }
+
+ if (!found && (rlen > 1)) BMO_elem_flag_enable(bm, e, EXT_DEL);
+ }
+ }
+
+ /* calculate verts to delet */
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ found = 0;
+
+ BM_ITER(e, &viter, bm, BM_EDGES_OF_VERT, v) {
+ if (!BMO_elem_flag_test(bm, e, EXT_INPUT) || !BMO_elem_flag_test(bm, e, EXT_DEL)) {
+ found = 1;
+ break;
+ }
+ }
+
+ BM_ITER(f, &viter, bm, BM_FACES_OF_VERT, v) {
+ if (!BMO_elem_flag_test(bm, f, EXT_INPUT)) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ BMO_elem_flag_enable(bm, v, EXT_DEL);
+ }
+ }
+
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, f, EXT_INPUT))
+ BMO_elem_flag_enable(bm, f, EXT_DEL);
+ }
+
+ if (delorig) {
+ BMO_op_initf(bm, &delop, "del geom=%fvef context=%d",
+ EXT_DEL, DEL_ONLYTAGGED);
+ }
+
+ BMO_slot_copy(op, &dupeop, "edgefacein", "geom");
+ BMO_op_exec(bm, &dupeop);
+
+ if (bm->act_face && BMO_elem_flag_test(bm, bm->act_face, EXT_INPUT))
+ bm->act_face = BMO_slot_map_ptr_get(bm, &dupeop, "facemap", bm->act_face);
+
+ if (delorig) BMO_op_exec(bm, &delop);
+
+ /* if not delorig, reverse loops of original face */
+ if (!delorig) {
+ for (f = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); f; f = BM_iter_step(&iter)) {
+ if (BMO_elem_flag_test(bm, f, EXT_INPUT)) {
+ BM_face_normal_flip(bm, f);
+ }
+ }
+ }
+
+ BMO_slot_copy(&dupeop, op, "newout", "geomout");
+ e = BMO_iter_new(&siter, bm, &dupeop, "boundarymap", 0);
+ for ( ; e; e = BMO_iter_step(&siter)) {
+ if (BMO_slot_map_contains(bm, op, "exclude", e)) continue;
+
+ newedge = BMO_iter_map_value(&siter);
+ newedge = *(BMEdge **)newedge;
+ if (!newedge) continue;
+
+ /* orient loop to give same normal as a loop of newedge
+ * if it exists (will be an extruded face),
+ * else same normal as a loop of e, if it exists */
+ if (!newedge->l)
+ fwd = !e->l || !(e->l->v == e->v1);
+ else
+ fwd = (newedge->l->v == newedge->v1);
+
+
+ if (fwd) {
+ verts[0] = e->v1;
+ verts[1] = e->v2;
+ verts[2] = newedge->v2;
+ verts[3] = newedge->v1;
+ }
+ else {
+ verts[3] = e->v1;
+ verts[2] = e->v2;
+ verts[1] = newedge->v2;
+ verts[0] = newedge->v1;
+ }
+
+ /* not sure what to do about example face, pass NULL for now */
+ f = BM_face_create_quad_tri_v(bm, verts, 4, NULL, FALSE);
+
+ /* copy attribute */
+ l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, f);
+ for ( ; l; l = BM_iter_step(&iter)) {
+ if (l->e != e && l->e != newedge) continue;
+ l2 = l->radial_next;
+
+ if (l2 == l) {
+ l2 = newedge->l;
+ BM_elem_attrs_copy(bm, bm, l2->f, l->f);
+
+ BM_elem_attrs_copy(bm, bm, l2, l);
+ l2 = l2->next;
+ l = l->next;
+ BM_elem_attrs_copy(bm, bm, l2, l);
+ }
+ else {
+ BM_elem_attrs_copy(bm, bm, l2->f, l->f);
+
+ /* copy dat */
+ if (l2->v == l->v) {
+ BM_elem_attrs_copy(bm, bm, l2, l);
+ l2 = l2->next;
+ l = l->next;
+ BM_elem_attrs_copy(bm, bm, l2, l);
+ }
+ else {
+ l2 = l2->next;
+ BM_elem_attrs_copy(bm, bm, l2, l);
+ l2 = l2->prev;
+ l = l->next;
+ BM_elem_attrs_copy(bm, bm, l2, l);
+ }
+ }
+ }
+ }
+
+ /* link isolated vert */
+ v = BMO_iter_new(&siter, bm, &dupeop, "isovertmap", 0);
+ for ( ; v; v = BMO_iter_step(&siter)) {
+ v2 = *((void **)BMO_iter_map_value(&siter));
+ BM_edge_create(bm, v, v2, v->e, TRUE);
+ }
+
+ /* cleanu */
+ if (delorig) BMO_op_finish(bm, &delop);
+ BMO_op_finish(bm, &dupeop);
+}
+
+/*
+ * Compute higher-quality vertex normals used by solidify.
+ * Only considers geometry in the marked solidify region.
+ * Note that this does not work so well for non-manifold
+ * regions.
+ */
+static void calc_solidify_normals(BMesh *bm)
+{
+ BMIter viter, eiter, fiter;
+ BMVert *v;
+ BMEdge *e;
+ BMFace *f, *f1, *f2;
+ float edge_normal[3];
+ int i;
+
+ /* can't use BM_edge_face_count because we need to count only marked faces */
+ int *edge_face_count = MEM_callocN(sizeof(int) * bm->totedge, __func__);
+
+ BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
+ BM_elem_flag_enable(v, BM_ELEM_TAG);
+ }
+
+ BM_mesh_elem_index_ensure(bm, BM_EDGE);
+
+ BM_ITER(f, &fiter, bm, BM_FACES_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, f, FACE_MARK)) {
+ continue;
+ }
+
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_FACE, f) {
+
+ /* And mark all edges and vertices on the
+ * marked faces */
+ BMO_elem_flag_enable(bm, e, EDGE_MARK);
+ BMO_elem_flag_enable(bm, e->v1, VERT_MARK);
+ BMO_elem_flag_enable(bm, e->v2, VERT_MARK);
+ edge_face_count[BM_elem_index_get(e)]++;
+ }
+ }
+
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) {
+ continue;
+ }
+
+ i = edge_face_count[BM_elem_index_get(e)]++;
+
+ if (i == 0 || i > 2) {
+ /* Edge & vertices are non-manifold even when considering
+ * only marked faces */
+ BMO_elem_flag_enable(bm, e, EDGE_NONMAN);
+ BMO_elem_flag_enable(bm, e->v1, VERT_NONMAN);
+ BMO_elem_flag_enable(bm, e->v2, VERT_NONMAN);
+ }
+ }
+ MEM_freeN(edge_face_count);
+ edge_face_count = NULL; /* dont re-use */
+
+ BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (!BM_vert_is_manifold(bm, v)) {
+ BMO_elem_flag_enable(bm, v, VERT_NONMAN);
+ continue;
+ }
+
+ if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
+ zero_v3(v->no);
+ }
+ }
+
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_MESH, NULL) {
+
+ /* If the edge is not part of a the solidify region
+ * its normal should not be considered */
+ if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) {
+ continue;
+ }
+
+ /* If the edge joins more than two marked faces high
+ * quality normal computation won't work */
+ if (BMO_elem_flag_test(bm, e, EDGE_NONMAN)) {
+ continue;
+ }
+
+ f1 = f2 = NULL;
+
+ BM_ITER(f, &fiter, bm, BM_FACES_OF_EDGE, e) {
+ if (BMO_elem_flag_test(bm, f, FACE_MARK)) {
+ if (f1 == NULL) {
+ f1 = f;
+ }
+ else {
+ BLI_assert(f2 == NULL);
+ f2 = f;
+ }
+ }
+ }
+
+ BLI_assert(f1 != NULL);
+
+ if (f2 != NULL) {
+ const float angle = angle_normalized_v3v3(f1->no, f2->no);
+
+ if (angle > 0.0f) {
+ /* two faces using this edge, calculate the edge normal
+ * using the angle between the faces as a weighting */
+ add_v3_v3v3(edge_normal, f1->no, f2->no);
+ normalize_v3(edge_normal);
+ mul_v3_fl(edge_normal, angle);
+ }
+ else {
+ /* can't do anything useful here!
+ * Set the face index for a vert incase it gets a zero normal */
+ BM_elem_flag_disable(e->v1, BM_ELEM_TAG);
+ BM_elem_flag_disable(e->v2, BM_ELEM_TAG);
+ continue;
+ }
+ }
+ else {
+ /* only one face attached to that edge */
+ /* an edge without another attached- the weight on this is
+ * undefined, M_PI / 2 is 90d in radians and that seems good enough */
+ copy_v3_v3(edge_normal, f1->no);
+ mul_v3_fl(edge_normal, M_PI / 2);
+ }
+
+ add_v3_v3(e->v1->no, edge_normal);
+ add_v3_v3(e->v2->no, edge_normal);
+ }
+
+ /* normalize accumulated vertex normal */
+ BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, v, VERT_MARK)) {
+ continue;
+ }
+
+ if (BMO_elem_flag_test(bm, v, VERT_NONMAN)) {
+ /* use standard normals for vertices connected to non-manifold edges */
+ BM_vert_normal_update(bm, v);
+ }
+ else if (normalize_v3(v->no) == 0.0f && !BM_elem_flag_test(v, BM_ELEM_TAG)) {
+ /* exceptional case, totally flat. use the normal
+ * of any marked face around the vertex */
+ BM_ITER(f, &fiter, bm, BM_FACES_OF_VERT, v) {
+ if (BMO_elem_flag_test(bm, f, FACE_MARK)) {
+ break;
+ }
+ }
+ copy_v3_v3(v->no, f->no);
+ }
+ }
+}
+
+static void solidify_add_thickness(BMesh *bm, float dist)
+{
+ BMFace *f;
+ BMVert *v;
+ BMLoop *l;
+ BMIter iter, loopIter;
+ float *vert_angles = MEM_callocN(sizeof(float) * bm->totvert * 2, "solidify"); /* 2 in 1 */
+ float *vert_accum = vert_angles + bm->totvert;
+ float angle;
+ int i, index;
+ float maxdist = dist * sqrtf(3.0f);
+
+ /* array for passing verts to angle_poly_v3 */
+ float **verts = NULL;
+ BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE);
+ /* array for receiving angles from angle_poly_v3 */
+ float *angles = NULL;
+ BLI_array_staticdeclare(angles, BM_NGON_STACK_SIZE);
+
+ BM_mesh_elem_index_ensure(bm, BM_VERT);
+
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, f, FACE_MARK)) {
+ continue;
+ }
+
+ BM_ITER(l, &loopIter, bm, BM_LOOPS_OF_FACE, f) {
+ BLI_array_append(verts, l->v->co);
+ BLI_array_growone(angles);
+ }
+
+ angle_poly_v3(angles, (const float **)verts, f->len);
+
+ i = 0;
+ BM_ITER(l, &loopIter, bm, BM_LOOPS_OF_FACE, f) {
+ v = l->v;
+ index = BM_elem_index_get(v);
+ angle = angles[i];
+ vert_accum[index] += angle;
+ vert_angles[index] += shell_angle_to_dist(angle_normalized_v3v3(v->no, f->no)) * angle;
+ i++;
+ }
+
+ BLI_array_empty(verts);
+ BLI_array_empty(angles);
+ }
+
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ index = BM_elem_index_get(v);
+ if (vert_accum[index]) { /* zero if unselected */
+ float vdist = MIN2(maxdist, dist * vert_angles[index] / vert_accum[index]);
+ madd_v3_v3fl(v->co, v->no, vdist);
+ }
+ }
+
+ MEM_freeN(vert_angles);
+}
+
+void bmesh_solidify_face_region_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator extrudeop;
+ BMOperator reverseop;
+ float thickness;
+
+ thickness = BMO_slot_float_get(op, "thickness");
+
+ /* Flip original faces (so the shell is extruded inward) */
+ BMO_op_init(bm, &reverseop, "reversefaces");
+ BMO_slot_copy(op, &reverseop, "geom", "faces");
+ BMO_op_exec(bm, &reverseop);
+ BMO_op_finish(bm, &reverseop);
+
+ /* Extrude the region */
+ BMO_op_initf(bm, &extrudeop, "extrudefaceregion alwayskeeporig=%i", TRUE);
+ BMO_slot_copy(op, &extrudeop, "geom", "edgefacein");
+ BMO_op_exec(bm, &extrudeop);
+
+ /* Push the verts of the extruded faces inward to create thickness */
+ BMO_slot_buffer_flag_enable(bm, &extrudeop, "geomout", FACE_MARK, BM_FACE);
+ calc_solidify_normals(bm);
+ solidify_add_thickness(bm, thickness);
+
+ BMO_slot_copy(&extrudeop, op, "geomout", "geomout");
+
+ BMO_op_finish(bm, &extrudeop);
+}
diff --git a/source/blender/bmesh/operators/bmo_join_triangles.c b/source/blender/bmesh/operators/bmo_join_triangles.c
new file mode 100644
index 00000000000..921c579447b
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_join_triangles.c
@@ -0,0 +1,373 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Joseph Eagar.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_meshdata_types.h"
+
+#include "BKE_customdata.h"
+
+#include "BLI_math.h"
+#include "BLI_array.h"
+
+#include "bmesh.h"
+
+#include "bmesh_operators_private.h" /* own include */
+
+/*
+ * JOIN_TRIANGLES.C
+ *
+ * utility bmesh operators, e.g. transform,
+ * translate, rotate, scale, etc.
+ *
+ */
+
+/* Bitflags for edges */
+#define T2QDELETE 1
+#define T2QCOMPLEX 2
+#define T2QJOIN 4
+
+/* assumes edges are validated before reaching this poin */
+static float measure_facepair(BMesh *UNUSED(bm), BMVert *v1, BMVert *v2,
+ BMVert *v3, BMVert *v4, float limit)
+{
+ /* gives a 'weight' to a pair of triangles that join an edge to decide how good a join they would mak */
+ /* Note: this is more complicated than it needs to be and should be cleaned up.. */
+ float n1[3], n2[3], measure = 0.0f, angle1, angle2, diff;
+ float edgeVec1[3], edgeVec2[3], edgeVec3[3], edgeVec4[3];
+ float minarea, maxarea, areaA, areaB;
+
+ /* First Test: Normal differenc */
+ normal_tri_v3(n1, v1->co, v2->co, v3->co);
+ normal_tri_v3(n2, v1->co, v3->co, v4->co);
+
+ if (n1[0] == n2[0] && n1[1] == n2[1] && n1[2] == n2[2]) angle1 = 0.0f;
+ else angle1 = angle_v3v3(n1, n2);
+
+ normal_tri_v3(n1, v2->co, v3->co, v4->co);
+ normal_tri_v3(n2, v4->co, v1->co, v2->co);
+
+ if (n1[0] == n2[0] && n1[1] == n2[1] && n1[2] == n2[2]) angle2 = 0.0f;
+ else angle2 = angle_v3v3(n1, n2);
+
+ measure += (angle1 + angle2) * 0.5f;
+ if (measure > limit) {
+ return measure;
+ }
+
+ /* Second test: Colinearit */
+ sub_v3_v3v3(edgeVec1, v1->co, v2->co);
+ sub_v3_v3v3(edgeVec2, v2->co, v3->co);
+ sub_v3_v3v3(edgeVec3, v3->co, v4->co);
+ sub_v3_v3v3(edgeVec4, v4->co, v1->co);
+
+ /* a completely skinny face is 'pi' after halving */
+ diff = 0.25f * (fabsf(angle_v3v3(edgeVec1, edgeVec2) - (float)M_PI_2) +
+ fabsf(angle_v3v3(edgeVec2, edgeVec3) - (float)M_PI_2) +
+ fabsf(angle_v3v3(edgeVec3, edgeVec4) - (float)M_PI_2) +
+ fabsf(angle_v3v3(edgeVec4, edgeVec1) - (float)M_PI_2));
+
+ if (!diff) {
+ return 0.0;
+ }
+
+ measure += diff;
+ if (measure > limit) {
+ return measure;
+ }
+
+ /* Third test: Concavit */
+ areaA = area_tri_v3(v1->co, v2->co, v3->co) + area_tri_v3(v1->co, v3->co, v4->co);
+ areaB = area_tri_v3(v2->co, v3->co, v4->co) + area_tri_v3(v4->co, v1->co, v2->co);
+
+ if (areaA <= areaB) minarea = areaA;
+ else minarea = areaB;
+
+ if (areaA >= areaB) maxarea = areaA;
+ else maxarea = areaB;
+
+ if (!maxarea) measure += 1;
+ else measure += (1 - (minarea / maxarea));
+
+ return measure;
+}
+
+#define T2QUV_LIMIT 0.005f
+#define T2QCOL_LIMIT 3
+
+static int compareFaceAttribs(BMesh *bm, BMEdge *e, int douvs, int dovcols)
+{
+ MTexPoly *tp1, *tp2;
+ MLoopCol *lcol1, *lcol2, *lcol3, *lcol4;
+ MLoopUV *luv1, *luv2, *luv3, *luv4;
+ BMLoop *l1, *l2, *l3, *l4;
+ int mergeok_uvs = !douvs, mergeok_vcols = !dovcols;
+
+ l1 = e->l;
+ l3 = e->l->radial_next;
+
+ /* match up loops on each side of an edge corrusponding to each ver */
+ if (l1->v == l3->v) {
+ l2 = l1->next;
+ l4 = l2->next;
+ }
+ else {
+ l2 = l1->next;
+
+ l4 = l3;
+ l3 = l4->next;
+ }
+
+ lcol1 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPCOL);
+ lcol2 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPCOL);
+ lcol3 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPCOL);
+ lcol4 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPCOL);
+
+ luv1 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPUV);
+ luv2 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPUV);
+ luv3 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPUV);
+ luv4 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPUV);
+
+ tp1 = CustomData_bmesh_get(&bm->pdata, l1->f->head.data, CD_MTEXPOLY);
+ tp2 = CustomData_bmesh_get(&bm->pdata, l2->f->head.data, CD_MTEXPOLY);
+
+ if (!lcol1)
+ mergeok_vcols = 1;
+
+ if (!luv1)
+ mergeok_uvs = 1;
+
+ /* compare faceedges for each face attribute. Additional per face attributes can be added late */
+
+ /* do VCOL */
+ if (lcol1 && dovcols) {
+ char *cols[4] = {(char *)lcol1, (char *)lcol2, (char *)lcol3, (char *)lcol4};
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ if (cols[0][i] + T2QCOL_LIMIT < cols[2][i] - T2QCOL_LIMIT)
+ break;
+ if (cols[1][i] + T2QCOL_LIMIT < cols[3][i] - T2QCOL_LIMIT)
+ break;
+ }
+
+ if (i == 3)
+ mergeok_vcols = 1;
+ }
+
+ /* do UV */
+ if (luv1 && douvs) {
+ if (tp1->tpage != tp2->tpage); /* do nothin */
+ else {
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ if (luv1->uv[0] + T2QUV_LIMIT > luv3->uv[0] && luv1->uv[0] - T2QUV_LIMIT < luv3->uv[0] &&
+ luv1->uv[1] + T2QUV_LIMIT > luv3->uv[1] && luv1->uv[1] - T2QUV_LIMIT < luv3->uv[1])
+ {
+ if (luv2->uv[0] + T2QUV_LIMIT > luv4->uv[0] && luv2->uv[0] - T2QUV_LIMIT < luv4->uv[0] &&
+ luv2->uv[1] + T2QUV_LIMIT > luv4->uv[1] && luv2->uv[1] - T2QUV_LIMIT < luv4->uv[1])
+ {
+ mergeok_uvs = 1;
+ }
+ }
+ }
+ }
+ }
+
+ if (douvs == mergeok_uvs && dovcols == mergeok_vcols) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+typedef struct JoinEdge {
+ float weight;
+ BMEdge *e;
+} JoinEdge;
+
+#define EDGE_MARK 1
+#define EDGE_CHOSEN 2
+
+#define FACE_MARK 1
+#define FACE_INPUT 2
+
+static int fplcmp(const void *v1, const void *v2)
+{
+ const JoinEdge *e1 = (JoinEdge *)v1, *e2 = (JoinEdge *)v2;
+
+ if (e1->weight > e2->weight) return 1;
+ else if (e1->weight < e2->weight) return -1;
+
+ return 0;
+}
+
+void bmesh_jointriangles_exec(BMesh *bm, BMOperator *op)
+{
+ BMIter iter, liter;
+ BMOIter siter;
+ BMFace *f1, *f2;
+ BMLoop *l;
+ BMEdge *e;
+ BLI_array_declare(jedges);
+ JoinEdge *jedges = NULL;
+ int dosharp = BMO_slot_int_get(op, "compare_sharp"), douvs = BMO_slot_int_get(op, "compare_uvs");
+ int dovcols = BMO_slot_int_get(op, "compare_vcols"), domat = BMO_slot_int_get(op, "compare_materials");
+ float limit = BMO_slot_float_get(op, "limit");
+ int i, totedge;
+
+ /* flag all edges of all input face */
+ BMO_ITER(f1, &siter, bm, op, "faces", BM_FACE) {
+ BMO_elem_flag_enable(bm, f1, FACE_INPUT);
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f1) {
+ BMO_elem_flag_enable(bm, l->e, EDGE_MARK);
+ }
+ }
+
+ /* unflag edges that are invalid; e.g. aren't surrounded by triangle */
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, e, EDGE_MARK))
+ continue;
+
+ if (BM_edge_face_count(e) != 2) {
+ BMO_elem_flag_disable(bm, e, EDGE_MARK);
+ continue;
+ }
+
+ f1 = e->l->f;
+ f2 = e->l->radial_next->f;
+
+ if (f1->len != 3 || f2->len != 3) {
+ BMO_elem_flag_disable(bm, e, EDGE_MARK);
+ continue;
+ }
+
+ if (!BMO_elem_flag_test(bm, f1, FACE_INPUT) || !BMO_elem_flag_test(bm, f2, FACE_INPUT)) {
+ BMO_elem_flag_disable(bm, e, EDGE_MARK);
+ continue;
+ }
+ }
+
+ i = 0;
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ BMVert *v1, *v2, *v3, *v4;
+ BMFace *f1, *f2;
+ float measure;
+
+ if (!BMO_elem_flag_test(bm, e, EDGE_MARK))
+ continue;
+
+ f1 = e->l->f;
+ f2 = e->l->radial_next->f;
+
+ v1 = e->l->v;
+ v2 = e->l->prev->v;
+ v3 = e->l->next->v;
+ v4 = e->l->radial_next->prev->v;
+
+ if (dosharp && !BM_elem_flag_test(e, BM_ELEM_SMOOTH))
+ continue;
+
+ if ((douvs || dovcols) && compareFaceAttribs(bm, e, douvs, dovcols))
+ continue;
+
+ if (domat && f1->mat_nr != f2->mat_nr)
+ continue;
+
+ measure = measure_facepair(bm, v1, v2, v3, v4, limit);
+ if (measure < limit) {
+ BLI_array_growone(jedges);
+
+ jedges[i].e = e;
+ jedges[i].weight = measure;
+
+ i++;
+ }
+ }
+
+ if (!jedges)
+ return;
+
+ qsort(jedges, BLI_array_count(jedges), sizeof(JoinEdge), fplcmp);
+
+ totedge = BLI_array_count(jedges);
+ for (i = 0; i < totedge; i++) {
+ BMFace *f1, *f2;
+
+ e = jedges[i].e;
+ f1 = e->l->f;
+ f2 = e->l->radial_next->f;
+
+ if (BMO_elem_flag_test(bm, f1, FACE_MARK) || BMO_elem_flag_test(bm, f2, FACE_MARK))
+ continue;
+
+ BMO_elem_flag_enable(bm, f1, FACE_MARK);
+ BMO_elem_flag_enable(bm, f2, FACE_MARK);
+ BMO_elem_flag_enable(bm, e, EDGE_CHOSEN);
+ }
+
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, e, EDGE_CHOSEN))
+ continue;
+
+ f1 = e->l->f;
+ f2 = e->l->radial_next->f;
+
+ BM_faces_join_pair(bm, f1, f2, e);
+ }
+
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, e, EDGE_MARK)) {
+ /* ok, this edge wasn't merged, check if it's
+ * in a 2-tri-pair island, and if so merg */
+
+ f1 = e->l->f;
+ f2 = e->l->radial_next->f;
+
+ if (f1->len != 3 || f2->len != 3)
+ continue;
+
+ for (i = 0; i < 2; i++) {
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, i ? f2 : f1) {
+ if (l->e != e && BMO_elem_flag_test(bm, l->e, EDGE_MARK)) {
+ break;
+ }
+ }
+
+ /* if l isn't NULL, we broke out of the loo */
+ if (l) {
+ break;
+ }
+ }
+
+ /* if i isn't 2, we broke out of that loo */
+ if (i != 2) {
+ continue;
+ }
+
+ BM_faces_join_pair(bm, f1, f2, e);
+ }
+ }
+
+ BLI_array_free(jedges);
+}
diff --git a/source/blender/bmesh/operators/bmo_mesh_conv.c b/source/blender/bmesh/operators/bmo_mesh_conv.c
new file mode 100644
index 00000000000..4f6db9056e5
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_mesh_conv.c
@@ -0,0 +1,906 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Joseph Eagar.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_key_types.h"
+#include "DNA_modifier_types.h"
+
+#include "BKE_mesh.h"
+#include "BLI_listbase.h"
+#include "BKE_global.h"
+#include "BKE_key.h"
+#include "BKE_main.h"
+#include "BKE_customdata.h"
+
+#include "BLI_math.h"
+#include "BLI_array.h"
+
+#include "bmesh.h"
+#include "bmesh_private.h"
+
+#include "bmesh_operators_private.h" /* own include */
+
+/*
+ * MESH CONV.C
+ *
+ * This file contains functions
+ * for converting a Mesh
+ * into a Bmesh, and back again.
+ *
+ */
+
+void mesh_to_bmesh_exec(BMesh *bm, BMOperator *op)
+{
+ Object *ob = BMO_slot_ptr_get(op, "object");
+ Mesh *me = BMO_slot_ptr_get(op, "mesh");
+ MVert *mvert;
+ BLI_array_declare(verts);
+ MEdge *medge;
+ MLoop *ml;
+ MPoly *mpoly;
+ KeyBlock *actkey, *block;
+ BMVert *v, **vt = NULL, **verts = NULL;
+ BMEdge *e, **fedges = NULL, **et = NULL;
+ BMFace *f;
+ BMLoop *l;
+ BLI_array_declare(fedges);
+ float (*keyco)[3] = NULL;
+ int *keyi;
+ int set_key = BMO_slot_int_get(op, "set_shapekey");
+ int totuv, i, j;
+
+ if (!me || !me->totvert) {
+ return; /* sanity check */
+ }
+
+ vt = MEM_mallocN(sizeof(void **) * me->totvert, "mesh to bmesh vtable");
+
+ CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0);
+ CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0);
+ CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_CALLOC, 0);
+ CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0);
+
+ /* make sure uv layer names are consisten */
+ totuv = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY);
+ for (i = 0; i < totuv; i++) {
+ int li = CustomData_get_layer_index_n(&bm->pdata, CD_MTEXPOLY, i);
+ CustomData_set_layer_name(&bm->ldata, CD_MLOOPUV, i, bm->pdata.layers[li].name);
+ }
+
+ if (!CustomData_has_layer(&bm->edata, CD_CREASE))
+ CustomData_add_layer(&bm->edata, CD_CREASE, CD_ASSIGN, NULL, 0);
+
+ if (!CustomData_has_layer(&bm->edata, CD_BWEIGHT))
+ CustomData_add_layer(&bm->edata, CD_BWEIGHT, CD_ASSIGN, NULL, 0);
+
+ if (!CustomData_has_layer(&bm->vdata, CD_BWEIGHT))
+ CustomData_add_layer(&bm->vdata, CD_BWEIGHT, CD_ASSIGN, NULL, 0);
+
+
+ if (me->key && ob->shapenr > me->key->totkey) {
+ ob->shapenr = me->key->totkey - 1;
+ }
+
+ actkey = ob_get_keyblock(ob);
+ if (actkey && actkey->totelem == me->totvert) {
+ CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0);
+
+ /* check if we need to generate unique ids for the shapekeys.
+ * this also exists in the file reading code, but is here for
+ * a sanity chec */
+ if (!me->key->uidgen) {
+ fprintf(stderr,
+ "%s had to generate shape key uid's in a situation we shouldn't need to! "
+ "(bmesh internal error)\n",
+ __func__);
+
+ me->key->uidgen = 1;
+ for (block = me->key->block.first; block; block = block->next) {
+ block->uid = me->key->uidgen++;
+ }
+ }
+
+ keyco = actkey->data;
+ bm->shapenr = ob->shapenr;
+ for (i = 0, block = me->key->block.first; block; block = block->next, i++) {
+ CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY,
+ CD_ASSIGN, NULL, 0, block->name);
+
+ j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i);
+ bm->vdata.layers[j].uid = block->uid;
+ }
+ }
+ else if (actkey) {
+ printf("shapekey<->mesh mismatch!\n");
+ }
+
+ CustomData_bmesh_init_pool(&bm->vdata, bm_mesh_allocsize_default[0]);
+ CustomData_bmesh_init_pool(&bm->edata, bm_mesh_allocsize_default[1]);
+ CustomData_bmesh_init_pool(&bm->ldata, bm_mesh_allocsize_default[2]);
+ CustomData_bmesh_init_pool(&bm->pdata, bm_mesh_allocsize_default[3]);
+
+ for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) {
+ v = BM_vert_create(bm, keyco && set_key ? keyco[i] : mvert->co, NULL);
+ BM_elem_index_set(v, i); /* set_ok */
+ vt[i] = v;
+
+ /* transfer flag */
+ v->head.hflag = BM_vert_flag_from_mflag(mvert->flag);
+
+ /* this is necassary for selection counts to work properl */
+ if (BM_elem_flag_test(v, BM_ELEM_SELECT)) BM_vert_select_set(bm, v, TRUE);
+
+ normal_short_to_float_v3(v->no, mvert->no);
+
+ BM_elem_float_data_set(&bm->vdata, v, CD_BWEIGHT, (float)mvert->bweight / 255.0f);
+
+ /* Copy Custom Dat */
+ CustomData_to_bmesh_block(&me->vdata, &bm->vdata, i, &v->head.data);
+
+ /* set shapekey dat */
+ if (me->key) {
+ /* set shape key original inde */
+ keyi = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_SHAPE_KEYINDEX);
+ if (keyi) {
+ *keyi = i;
+ }
+
+ for (block = me->key->block.first, j = 0; block; block = block->next, j++) {
+ float *co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, j);
+
+ if (co) {
+ copy_v3_v3(co, ((float *)block->data) + 3 * i);
+ }
+ }
+ }
+ }
+
+ bm->elem_index_dirty &= ~BM_VERT; /* added in order, clear dirty flag */
+
+ if (!me->totedge) {
+ MEM_freeN(vt);
+ return;
+ }
+
+ et = MEM_mallocN(sizeof(void **) * me->totedge, "mesh to bmesh etable");
+
+ medge = me->medge;
+ for (i = 0; i < me->totedge; i++, medge++) {
+ e = BM_edge_create(bm, vt[medge->v1], vt[medge->v2], NULL, FALSE);
+ BM_elem_index_set(e, i); /* set_ok */
+ et[i] = e;
+
+ /* transfer flags */
+ e->head.hflag = BM_edge_flag_from_mflag(medge->flag);
+
+ /* this is necassary for selection counts to work properly */
+ if (BM_elem_flag_test(e, BM_ELEM_SELECT)) BM_elem_select_set(bm, e, TRUE);
+
+ /* Copy Custom Dat */
+ CustomData_to_bmesh_block(&me->edata, &bm->edata, i, &e->head.data);
+
+ BM_elem_float_data_set(&bm->edata, e, CD_CREASE, (float)medge->crease / 255.0f);
+ BM_elem_float_data_set(&bm->edata, e, CD_BWEIGHT, (float)medge->bweight / 255.0f);
+ }
+
+ bm->elem_index_dirty &= ~BM_EDGE; /* added in order, clear dirty flag */
+
+ mpoly = me->mpoly;
+ for (i = 0; i < me->totpoly; i++, mpoly++) {
+ BMIter iter;
+
+ BLI_array_empty(fedges);
+ BLI_array_empty(verts);
+
+ BLI_array_growitems(fedges, mpoly->totloop);
+ BLI_array_growitems(verts, mpoly->totloop);
+
+ for (j = 0; j < mpoly->totloop; j++) {
+ ml = &me->mloop[mpoly->loopstart + j];
+ v = vt[ml->v];
+ e = et[ml->e];
+
+ fedges[j] = e;
+ verts[j] = v;
+ }
+
+ /* not sure what this block is supposed to do,
+ * but its unused. so commenting - campbell */
+#if 0
+ {
+ BMVert *v1, *v2;
+ v1 = vt[me->mloop[mpoly->loopstart].v];
+ v2 = vt[me->mloop[mpoly->loopstart + 1].v];
+
+ if (v1 == fedges[0]->v1) {
+ v2 = fedges[0]->v2;
+ }
+ else {
+ v1 = fedges[0]->v2;
+ v2 = fedges[0]->v1;
+ }
+ }
+#endif
+
+ f = BM_face_create(bm, verts, fedges, mpoly->totloop, FALSE);
+
+ if (!f) {
+ printf("%s: Warning! Bad face in mesh"
+ " \"%s\" at index %d!, skipping\n",
+ __func__, me->id.name + 2, i);
+ continue;
+ }
+
+ /* dont use 'i' since we may have skipped the face */
+ BM_elem_index_set(f, bm->totface - 1); /* set_ok */
+
+ /* transfer flag */
+ f->head.hflag = BM_face_flag_from_mflag(mpoly->flag);
+
+ /* this is necassary for selection counts to work properl */
+ if (BM_elem_flag_test(f, BM_ELEM_SELECT)) BM_elem_select_set(bm, f, TRUE);
+
+ f->mat_nr = mpoly->mat_nr;
+ if (i == me->act_face) bm->act_face = f;
+
+ j = 0;
+ BM_ITER_INDEX(l, &iter, bm, BM_LOOPS_OF_FACE, f, j) {
+ /* Save index of correspsonding MLoop */
+ BM_elem_index_set(l, mpoly->loopstart + j); /* set_loop */
+ }
+
+ /* Copy Custom Dat */
+ CustomData_to_bmesh_block(&me->pdata, &bm->pdata, i, &f->head.data);
+ }
+
+ bm->elem_index_dirty &= ~BM_FACE; /* added in order, clear dirty flag */
+
+ {
+ BMIter fiter;
+ BMIter liter;
+
+ /* Copy over loop CustomData. Doing this in a separate loop isn't necessary
+ * but is an optimization, to avoid copying a bunch of interpolated customdata
+ * for each BMLoop (from previous BMLoops using the same edge), always followed
+ * by freeing the interpolated data and overwriting it with data from the Mesh. */
+ BM_ITER(f, &fiter, bm, BM_FACES_OF_MESH, NULL) {
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ int li = BM_elem_index_get(l);
+ CustomData_to_bmesh_block(&me->ldata, &bm->ldata, li, &l->head.data);
+ BM_elem_index_set(l, 0); /* set_loop */
+ }
+ }
+ }
+
+ if (me->mselect && me->totselect != 0) {
+ BMIter iter;
+ BMVert *vertex;
+ BMEdge *edge;
+ BMFace *face;
+ BMVert **vertex_array = MEM_callocN(sizeof(BMVert *) * bm->totvert,
+ "Selection Conversion Vertex Pointer Array");
+ BMEdge **edge_array = MEM_callocN(sizeof(BMEdge *) * bm->totedge,
+ "Selection Conversion Edge Pointer Array");
+ BMFace **face_array = MEM_callocN(sizeof(BMFace *) * bm->totface,
+ "Selection Conversion Face Pointer Array");
+
+ for (i = 0, vertex = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL);
+ vertex;
+ i++, vertex = BM_iter_step(&iter))
+ {
+ vertex_array[i] = vertex;
+ }
+
+ for (i = 0, edge = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, NULL);
+ edge;
+ i++, edge = BM_iter_step(&iter))
+ {
+ edge_array[i] = edge;
+ }
+
+ for (i = 0, face = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL);
+ face;
+ i++, face = BM_iter_step(&iter))
+ {
+ face_array[i] = face;
+ }
+
+ if (me->mselect) {
+ for (i = 0; i < me->totselect; i++) {
+ if (me->mselect[i].type == ME_VSEL) {
+ BM_select_history_store(bm, vertex_array[me->mselect[i].index]);
+ }
+ else if (me->mselect[i].type == ME_ESEL) {
+ BM_select_history_store(bm, edge_array[me->mselect[i].index]);
+ }
+ else if (me->mselect[i].type == ME_FSEL) {
+ BM_select_history_store(bm, face_array[me->mselect[i].index]);
+ }
+ }
+ }
+ else {
+ me->totselect = 0;
+ }
+
+ MEM_freeN(vertex_array);
+ MEM_freeN(edge_array);
+ MEM_freeN(face_array);
+ }
+ else {
+ me->totselect = 0;
+ if (me->mselect) {
+ MEM_freeN(me->mselect);
+ me->mselect = NULL;
+ }
+ }
+
+ BLI_array_free(fedges);
+ BLI_array_free(verts);
+
+ MEM_freeN(vt);
+ MEM_freeN(et);
+}
+
+void object_load_bmesh_exec(BMesh *bm, BMOperator *op)
+{
+ Object *ob = BMO_slot_ptr_get(op, "object");
+ /* Scene *scene = BMO_slot_ptr_get(op, "scene"); */
+ Mesh *me = ob->data;
+
+ BMO_op_callf(bm, "bmesh_to_mesh mesh=%p object=%p notesselation=%i", me, ob, TRUE);
+}
+
+
+static BMVert **bmesh_to_mesh_vertex_map(BMesh *bm, int ototvert)
+{
+ BMVert **vertMap = NULL;
+ BMVert *eve;
+ int index;
+ int i = 0;
+ BMIter iter;
+
+ /* caller needs to ensure this */
+ BLI_assert(ototvert > 0);
+
+ vertMap = MEM_callocN(sizeof(*vertMap)*ototvert, "vertMap");
+ if (CustomData_has_layer(&bm->vdata, CD_SHAPE_KEYINDEX)) {
+ int *keyi;
+ BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ keyi = CustomData_bmesh_get(&bm->vdata, eve->head.data, CD_SHAPE_KEYINDEX);
+ if (keyi) {
+ if (((index = *keyi) != ORIGINDEX_NONE) && (index < ototvert)) {
+ vertMap[index] = eve;
+ }
+ }
+ else {
+ if (i < ototvert) {
+ vertMap[i] = eve;
+ }
+ }
+ i++;
+ }
+ }
+ else {
+ BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (i < ototvert) {
+ vertMap[i] = eve;
+ }
+ else {
+ break;
+ }
+ i++;
+ }
+ }
+
+ return vertMap;
+}
+
+BM_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e)
+{
+ /* this is a cheap way to set the edge draw, its not precise and will
+ * pick the first 2 faces an edge uses */
+
+
+ if ( /* (med->flag & ME_EDGEDRAW) && */ /* assume to be true */
+ (e->l && (e->l != e->l->radial_next)) &&
+ (dot_v3v3(e->l->f->no, e->l->radial_next->f->no) > 0.998f))
+ {
+ med->flag &= ~ME_EDGEDRAW;
+ }
+}
+
+
+void bmesh_to_mesh_exec(BMesh *bm, BMOperator *op)
+{
+ Mesh *me = BMO_slot_ptr_get(op, "mesh");
+ /* Object *ob = BMO_slot_ptr_get(op, "object"); */
+ MLoop *mloop;
+ MPoly *mpoly;
+ MVert *mvert, *oldverts;
+ MEdge *med, *medge;
+ BMVert *v, *eve;
+ BMEdge *e;
+ BMLoop *l;
+ BMFace *f;
+ BMIter iter, liter;
+ int i, j, *keyi, ototvert, totloop;
+ int dotess = !BMO_slot_int_get(op, "notesselation");
+
+ ototvert = me->totvert;
+
+ /* new Vertex block */
+ if (bm->totvert == 0) mvert = NULL;
+ else mvert = MEM_callocN(bm->totvert * sizeof(MVert), "loadeditbMesh vert");
+
+ /* new Edge block */
+ if (bm->totedge == 0) medge = NULL;
+ else medge = MEM_callocN(bm->totedge * sizeof(MEdge), "loadeditbMesh edge");
+
+ /* build ngon dat */
+ /* new Ngon Face block */
+ if (bm->totface == 0) mpoly = NULL;
+ else mpoly = MEM_callocN(bm->totface * sizeof(MPoly), "loadeditbMesh poly");
+
+ /* find number of loops to allocat */
+ totloop = 0;
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ totloop += f->len;
+ }
+
+ if (totloop == 0) mloop = NULL;
+ else mloop = MEM_callocN(totloop * sizeof(MLoop), "loadeditbMesh loop");
+
+ /* lets save the old verts just in case we are actually working on
+ * a key ... we now do processing of the keys at the end */
+ oldverts = me->mvert;
+
+ /* don't free this yet */
+ CustomData_set_layer(&me->vdata, CD_MVERT, NULL);
+
+ /* free custom data */
+ CustomData_free(&me->vdata, me->totvert);
+ CustomData_free(&me->edata, me->totedge);
+ CustomData_free(&me->fdata, me->totface);
+ CustomData_free(&me->ldata, me->totloop);
+ CustomData_free(&me->pdata, me->totpoly);
+
+ /* add new custom data */
+ me->totvert = bm->totvert;
+ me->totedge = bm->totedge;
+ me->totloop = totloop;
+ me->totpoly = bm->totface;
+ /* will be overwritten with a valid value if 'dotess' is set, otherwise we
+ * end up with 'me->totface' and me->mface == NULL which can crash [#28625]
+ */
+ me->totface = 0;
+
+ CustomData_copy(&bm->vdata, &me->vdata, CD_MASK_MESH, CD_CALLOC, me->totvert);
+ CustomData_copy(&bm->edata, &me->edata, CD_MASK_MESH, CD_CALLOC, me->totedge);
+ CustomData_copy(&bm->ldata, &me->ldata, CD_MASK_MESH, CD_CALLOC, me->totloop);
+ CustomData_copy(&bm->pdata, &me->pdata, CD_MASK_MESH, CD_CALLOC, me->totpoly);
+
+ CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, mvert, me->totvert);
+ CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, me->totedge);
+ CustomData_add_layer(&me->ldata, CD_MLOOP, CD_ASSIGN, mloop, me->totloop);
+ CustomData_add_layer(&me->pdata, CD_MPOLY, CD_ASSIGN, mpoly, me->totpoly);
+
+ /* this is called again, 'dotess' arg is used there */
+ mesh_update_customdata_pointers(me, 0);
+
+ i = 0;
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ float *bweight = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_BWEIGHT);
+
+ mvert->bweight = bweight ? (char)((*bweight) * 255) : 0;
+
+ copy_v3_v3(mvert->co, v->co);
+ normal_float_to_short_v3(mvert->no, v->no);
+
+ mvert->flag = BM_vert_flag_to_mflag(v);
+
+ BM_elem_index_set(v, i); /* set_inline */
+
+ /* copy over customdat */
+ CustomData_from_bmesh_block(&bm->vdata, &me->vdata, v->head.data, i);
+
+ i++;
+ mvert++;
+
+ BM_CHECK_ELEMENT(bm, v);
+ }
+ bm->elem_index_dirty &= ~BM_VERT;
+
+ med = medge;
+ i = 0;
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ float *crease = CustomData_bmesh_get(&bm->edata, e->head.data, CD_CREASE);
+ float *bweight = CustomData_bmesh_get(&bm->edata, e->head.data, CD_BWEIGHT);
+
+ med->v1 = BM_elem_index_get(e->v1);
+ med->v2 = BM_elem_index_get(e->v2);
+ med->crease = crease ? (char)((*crease) * 255) : 0;
+ med->bweight = bweight ? (char)((*bweight) * 255) : 0;
+
+ med->flag = BM_edge_flag_to_mflag(e);
+
+ BM_elem_index_set(e, i); /* set_inline */
+
+ /* copy over customdat */
+ CustomData_from_bmesh_block(&bm->edata, &me->edata, e->head.data, i);
+
+ bmesh_quick_edgedraw_flag(med, e);
+
+ i++;
+ med++;
+ BM_CHECK_ELEMENT(bm, e);
+ }
+ bm->elem_index_dirty &= ~BM_EDGE;
+
+ i = 0;
+ j = 0;
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ mpoly->loopstart = j;
+ mpoly->totloop = f->len;
+ mpoly->mat_nr = f->mat_nr;
+ mpoly->flag = BM_face_flag_to_mflag(f);
+
+ l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f);
+ for ( ; l; l = BM_iter_step(&liter), j++, mloop++) {
+ mloop->e = BM_elem_index_get(l->e);
+ mloop->v = BM_elem_index_get(l->v);
+
+ /* copy over customdat */
+ CustomData_from_bmesh_block(&bm->ldata, &me->ldata, l->head.data, j);
+ BM_CHECK_ELEMENT(bm, l);
+ BM_CHECK_ELEMENT(bm, l->e);
+ BM_CHECK_ELEMENT(bm, l->v);
+ }
+
+ if (f == bm->act_face) me->act_face = i;
+
+ /* copy over customdat */
+ CustomData_from_bmesh_block(&bm->pdata, &me->pdata, f->head.data, i);
+
+ i++;
+ mpoly++;
+ BM_CHECK_ELEMENT(bm, f);
+ }
+
+ /* patch hook indices and vertex parents */
+ if (ototvert > 0) {
+ Object *ob;
+ ModifierData *md;
+ BMVert **vertMap = NULL;
+ int i, j;
+
+ for (ob = G.main->object.first; ob; ob = ob->id.next) {
+ if (ob->parent == bm->ob && ELEM(ob->partype, PARVERT1, PARVERT3)) {
+
+ if (vertMap == NULL) {
+ vertMap = bmesh_to_mesh_vertex_map(bm, ototvert);
+ }
+
+ if (ob->par1 < ototvert) {
+ eve = vertMap[ob->par1];
+ if (eve) ob->par1 = BM_elem_index_get(eve);
+ }
+ if (ob->par2 < ototvert) {
+ eve = vertMap[ob->par2];
+ if (eve) ob->par2 = BM_elem_index_get(eve);
+ }
+ if (ob->par3 < ototvert) {
+ eve = vertMap[ob->par3];
+ if (eve) ob->par3 = BM_elem_index_get(eve);
+ }
+
+ }
+ if (ob->data == me) {
+ for (md = ob->modifiers.first; md; md = md->next) {
+ if (md->type == eModifierType_Hook) {
+ HookModifierData *hmd = (HookModifierData *) md;
+
+ if (vertMap == NULL) {
+ vertMap = bmesh_to_mesh_vertex_map(bm, ototvert);
+ }
+
+ for (i = j = 0; i < hmd->totindex; i++) {
+ if (hmd->indexar[i] < ototvert) {
+ eve = vertMap[hmd->indexar[i]];
+
+ if (eve) {
+ hmd->indexar[j++] = BM_elem_index_get(eve);
+ }
+ }
+ else j++;
+ }
+
+ hmd->totindex = j;
+ }
+ }
+ }
+ }
+
+ if (vertMap) MEM_freeN(vertMap);
+ }
+
+ if (dotess) {
+ BKE_mesh_tessface_calc(me);
+ }
+
+ mesh_update_customdata_pointers(me, dotess);
+
+ {
+ BMEditSelection *selected;
+ me->totselect = BLI_countlist(&(bm->selected));
+
+ if (me->mselect) MEM_freeN(me->mselect);
+
+ me->mselect = MEM_callocN(sizeof(MSelect) * me->totselect, "Mesh selection history");
+
+
+ for (i = 0, selected = bm->selected.first; selected; i++, selected = selected->next) {
+ if (selected->htype == BM_VERT) {
+ me->mselect[i].type = ME_VSEL;
+
+ }
+ else if (selected->htype == BM_EDGE) {
+ me->mselect[i].type = ME_ESEL;
+
+ }
+ else if (selected->htype == BM_FACE) {
+ me->mselect[i].type = ME_FSEL;
+ }
+
+ me->mselect[i].index = BM_elem_index_get(selected->data);
+ }
+ }
+
+ /* see comment below, this logic is in twice */
+
+ if (me->key) {
+ KeyBlock *currkey;
+ KeyBlock *actkey = BLI_findlink(&me->key->block, bm->shapenr - 1);
+
+ float (*ofs)[3] = NULL;
+
+ /* go through and find any shapekey customdata layers
+ * that might not have corrusponding KeyBlocks, and add them if
+ * necassary */
+ j = 0;
+ for (i = 0; i < bm->vdata.totlayer; i++) {
+ if (bm->vdata.layers[i].type != CD_SHAPEKEY)
+ continue;
+
+ for (currkey = me->key->block.first; currkey; currkey = currkey->next) {
+ if (currkey->uid == bm->vdata.layers[i].uid)
+ break;
+ }
+
+ if (!currkey) {
+ currkey = MEM_callocN(sizeof(KeyBlock), "KeyBlock mesh_conv.c");
+ currkey->type = KEY_LINEAR;
+ currkey->slidermin = 0.0f;
+ currkey->slidermax = 1.0f;
+
+ BLI_addtail(&me->key->block, currkey);
+ me->key->totkey++;
+ }
+
+ j++;
+ }
+
+
+ /* editing the base key should update others */
+ if (me->key->type == KEY_RELATIVE && oldverts) {
+ int act_is_basis = 0;
+ /* find if this key is a basis for any others */
+ for (currkey = me->key->block.first; currkey; currkey = currkey->next) {
+ if (bm->shapenr - 1 == currkey->relative) {
+ act_is_basis = 1;
+ break;
+ }
+ }
+
+ if (act_is_basis) { /* active key is a base */
+ float (*fp)[3] = actkey->data;
+ int *keyi;
+ i = 0;
+ ofs = MEM_callocN(sizeof(float) * 3 * bm->totvert, "currkey->data");
+ mvert = me->mvert;
+ BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ keyi = CustomData_bmesh_get(&bm->vdata, eve->head.data, CD_SHAPE_KEYINDEX);
+ if (keyi && *keyi != ORIGINDEX_NONE) {
+ sub_v3_v3v3(ofs[i], mvert->co, fp[*keyi]);
+ }
+ i++;
+ mvert++;
+ }
+ }
+ }
+
+
+ for (currkey = me->key->block.first; currkey; currkey = currkey->next) {
+ j = 0;
+
+ for (i = 0; i < bm->vdata.totlayer; i++) {
+ if (bm->vdata.layers[i].type != CD_SHAPEKEY)
+ continue;
+
+ if (currkey->uid == bm->vdata.layers[i].uid) {
+ int apply_offset = (ofs && (currkey != actkey) && (bm->shapenr - 1 == currkey->relative));
+ float *fp, *co;
+ float (*ofs_pt)[3] = ofs;
+
+ if (currkey->data)
+ MEM_freeN(currkey->data);
+ currkey->data = fp = MEM_mallocN(sizeof(float) * 3 * bm->totvert, "shape key data");
+ currkey->totelem = bm->totvert;
+
+ BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ co = (currkey == actkey) ?
+ eve->co :
+ CustomData_bmesh_get_n(&bm->vdata, eve->head.data, CD_SHAPEKEY, j);
+
+ copy_v3_v3(fp, co);
+
+ /* propagate edited basis offsets to other shapes */
+ if (apply_offset) {
+ add_v3_v3(fp, *ofs_pt++);
+ }
+
+ fp += 3;
+ }
+ break;
+ }
+
+ j++;
+ }
+
+ /* if we didn't find a shapekey, tag the block to be reconstructed
+ * via the old method below */
+ if (j == CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY)) {
+ currkey->flag |= KEYBLOCK_MISSING;
+ }
+ }
+
+ if (ofs) MEM_freeN(ofs);
+ }
+
+ /* XXX, code below is from trunk and a duplicate functionality
+ * to the block above.
+ * We should use one or the other, having both means we have to maintain
+ * both and keep them working the same way which is a hassle - campbell */
+
+ /* old method of reconstructing keys via vertice's original key indices,
+ * currently used if the new method above fails (which is theoretically
+ * possible in certain cases of undo) */
+ if (me->key) {
+ float *fp, *newkey, *oldkey;
+ KeyBlock *currkey;
+ KeyBlock *actkey = BLI_findlink(&me->key->block, bm->shapenr - 1);
+
+ float (*ofs)[3] = NULL;
+
+ /* editing the base key should update others */
+ if (me->key->type == KEY_RELATIVE && oldverts) {
+ int act_is_basis = 0;
+ /* find if this key is a basis for any others */
+ for (currkey = me->key->block.first; currkey; currkey = currkey->next) {
+ if (bm->shapenr - 1 == currkey->relative) {
+ act_is_basis = 1;
+ break;
+ }
+ }
+
+ if (act_is_basis) { /* active key is a base */
+ float (*fp)[3] = actkey->data;
+ int *keyi;
+ i = 0;
+ ofs = MEM_callocN(sizeof(float) * 3 * bm->totvert, "currkey->data");
+ mvert = me->mvert;
+ BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ keyi = CustomData_bmesh_get(&bm->vdata, eve->head.data, CD_SHAPE_KEYINDEX);
+ if (keyi && *keyi != ORIGINDEX_NONE) {
+ sub_v3_v3v3(ofs[i], mvert->co, fp[*keyi]);
+ }
+ i++;
+ mvert++;
+ }
+ }
+ }
+
+ /* Lets reorder the key data so that things line up roughly
+ * with the way things were before editmode */
+ currkey = me->key->block.first;
+ while (currkey) {
+ int apply_offset = (ofs && (currkey != actkey) && (bm->shapenr - 1 == currkey->relative));
+
+ if (!(currkey->flag & KEYBLOCK_MISSING)) {
+ currkey = currkey->next;
+ continue;
+ }
+
+ printf("warning: had to hackishly reconstruct shape key \"%s\","
+ " it may not be correct anymore.\n", currkey->name);
+
+ currkey->flag &= ~KEYBLOCK_MISSING;
+
+ fp = newkey = MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data");
+ oldkey = currkey->data;
+
+ eve = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL);
+
+ i = 0;
+ mvert = me->mvert;
+ while (eve) {
+ keyi = CustomData_bmesh_get(&bm->vdata, eve->head.data, CD_SHAPE_KEYINDEX);
+ if (!keyi) {
+ break;
+ }
+ if (*keyi >= 0 && *keyi < currkey->totelem) { // valid old vertex
+ if (currkey == actkey) {
+ if (actkey == me->key->refkey) {
+ copy_v3_v3(fp, mvert->co);
+ }
+ else {
+ copy_v3_v3(fp, mvert->co);
+ if (oldverts) {
+ copy_v3_v3(mvert->co, oldverts[*keyi].co);
+ }
+ }
+ }
+ else {
+ if (oldkey) {
+ copy_v3_v3(fp, oldkey + 3 * *keyi);
+ }
+ }
+ }
+ else {
+ copy_v3_v3(fp, mvert->co);
+ }
+
+ /* propagate edited basis offsets to other shapes */
+ if (apply_offset) {
+ add_v3_v3(fp, ofs[i]);
+ }
+
+ fp+= 3;
+ ++i;
+ ++mvert;
+ eve = BM_iter_step(&iter);
+ }
+ currkey->totelem = bm->totvert;
+ if (currkey->data) MEM_freeN(currkey->data);
+ currkey->data = newkey;
+
+ currkey = currkey->next;
+ }
+
+ if (ofs) MEM_freeN(ofs);
+ }
+
+ if (oldverts) MEM_freeN(oldverts);
+}
diff --git a/source/blender/bmesh/operators/bmo_mirror.c b/source/blender/bmesh/operators/bmo_mirror.c
new file mode 100644
index 00000000000..82e77fc9a96
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_mirror.c
@@ -0,0 +1,126 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Joseph Eagar.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_meshdata_types.h"
+
+#include "BLI_math.h"
+#include "BLI_array.h"
+
+#include "BKE_customdata.h"
+
+#include "bmesh.h"
+#include "bmesh_operators_private.h" /* own include */
+
+#define ELE_NEW 1
+
+void bmesh_mirror_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator dupeop, weldop;
+ BMOIter siter;
+ BMIter iter;
+ BMVert *v, *v2, **vmap = NULL;
+ BLI_array_declare(vmap);
+ BMEdge /* *e, */ **emap = NULL;
+ BLI_array_declare(emap);
+ float mtx[4][4];
+ float imtx[4][4];
+ float scale[3] = {1.0f, 1.0f, 1.0f};
+ float dist = BMO_slot_float_get(op, "mergedist");
+ int i, ototvert, ototedge, axis = BMO_slot_int_get(op, "axis");
+ int mirroru = BMO_slot_int_get(op, "mirror_u");
+ int mirrorv = BMO_slot_int_get(op, "mirror_v");
+
+ ototvert = bm->totvert;
+ ototedge = bm->totedge;
+
+ BMO_slot_mat4_get(op, "mat", mtx);
+ invert_m4_m4(imtx, mtx);
+
+ BMO_op_initf(bm, &dupeop, "dupe geom=%s", op, "geom");
+ BMO_op_exec(bm, &dupeop);
+
+ BMO_slot_buffer_flag_enable(bm, &dupeop, "newout", ELE_NEW, BM_ALL);
+
+ /* create old -> new mappin */
+ i = 0;
+ v2 = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL);
+ BMO_ITER(v, &siter, bm, &dupeop, "newout", BM_VERT) {
+ BLI_array_growone(vmap);
+ vmap[i] = v;
+
+ /* BMESH_TODO, double check this is being used, calling following operators will overwrite anyway - campbell */
+ BM_elem_index_set(v2, i); /* set_dirty! */
+ v2 = BM_iter_step(&iter);
+
+ i++;
+ }
+ bm->elem_index_dirty |= BM_VERT;
+
+ /* feed old data to transform bmo */
+ scale[axis] = -1.0f;
+ BMO_op_callf(bm, "transform verts=%fv mat=%m4", ELE_NEW, mtx);
+ BMO_op_callf(bm, "scale verts=%fv vec=%v", ELE_NEW, scale);
+ BMO_op_callf(bm, "transform verts=%fv mat=%m4", ELE_NEW, imtx);
+
+ BMO_op_init(bm, &weldop, "weldverts");
+
+ v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL);
+ for (i = 0; i < ototvert; i++) {
+ if (ABS(v->co[axis]) <= dist) {
+ BMO_slot_map_ptr_insert(bm, &weldop, "targetmap", vmap[i], v);
+ }
+ v = BM_iter_step(&iter);
+ }
+
+ if (mirroru || mirrorv) {
+ BMFace *f;
+ BMLoop *l;
+ MLoopUV *luv;
+ int totlayer;
+ BMIter liter;
+
+ BMO_ITER(f, &siter, bm, &dupeop, "newout", BM_FACE) {
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ totlayer = CustomData_number_of_layers(&bm->ldata, CD_MLOOPUV);
+ for (i = 0; i < totlayer; i++) {
+ luv = CustomData_bmesh_get_n(&bm->ldata, l->head.data, CD_MLOOPUV, i);
+ if (mirroru)
+ luv->uv[0] = 1.0f - luv->uv[0];
+ if (mirrorv)
+ luv->uv[1] = 1.0f - luv->uv[1];
+ }
+ }
+ }
+ }
+
+ BMO_op_exec(bm, &weldop);
+
+ BMO_op_finish(bm, &weldop);
+ BMO_op_finish(bm, &dupeop);
+
+ BMO_slot_from_flag(bm, op, "newout", ELE_NEW, BM_ALL);
+
+ BLI_array_free(vmap);
+ BLI_array_free(emap);
+}
diff --git a/source/blender/bmesh/operators/bmo_primitive.c b/source/blender/bmesh/operators/bmo_primitive.c
new file mode 100644
index 00000000000..f7a1a8f4d06
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_primitive.c
@@ -0,0 +1,734 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Joseph Eagar.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+
+#include "ED_mesh.h"
+
+#include "bmesh.h"
+#include "bmesh_private.h"
+
+
+
+/* ************************ primitives ******************* */
+
+static float icovert[12][3] = {
+ {0.0f,0.0f,-200.0f},
+ {144.72f, -105.144f,-89.443f},
+ {-55.277f, -170.128,-89.443f},
+ {-178.885f,0.0f,-89.443f},
+ {-55.277f,170.128f,-89.443f},
+ {144.72f,105.144f,-89.443f},
+ {55.277f,-170.128f,89.443f},
+ {-144.72f,-105.144f,89.443f},
+ {-144.72f,105.144f,89.443f},
+ {55.277f,170.128f,89.443f},
+ {178.885f,0.0f,89.443f},
+ {0.0f,0.0f,200.0f}
+};
+
+static short icoface[20][3] = {
+ {0,1,2},
+ {1,0,5},
+ {0,2,3},
+ {0,3,4},
+ {0,4,5},
+ {1,5,10},
+ {2,1,6},
+ {3,2,7},
+ {4,3,8},
+ {5,4,9},
+ {1,10,6},
+ {2,6,7},
+ {3,7,8},
+ {4,8,9},
+ {5,9,10},
+ {6,10,11},
+ {7,6,11},
+ {8,7,11},
+ {9,8,11},
+ {10,9,11}
+};
+
+// HACK: these can also be found in cmoview.tga.c, but are here so that they can be found by linker
+// this hack is only used so that scons & mingw + split-sources hack works
+// ------------------------------- start copied code
+/* these are not the monkeys you are looking for */
+static int monkeyo = 4;
+static int monkeynv = 271;
+static int monkeynf = 250;
+static signed char monkeyv[271][3] = {
+ {-71,21,98},{-63,12,88},{-57,7,74},{-82,-3,79},{-82,4,92},
+ {-82,17,100},{-92,21,102},{-101,12,95},{-107,7,83},
+ {-117,31,84},{-109,31,95},{-96,31,102},{-92,42,102},
+ {-101,50,95},{-107,56,83},{-82,66,79},{-82,58,92},
+ {-82,46,100},{-71,42,98},{-63,50,88},{-57,56,74},
+ {-47,31,72},{-55,31,86},{-67,31,97},{-66,31,99},
+ {-70,43,100},{-82,48,103},{-93,43,105},{-98,31,105},
+ {-93,20,105},{-82,31,106},{-82,15,103},{-70,20,100},
+ {-127,55,95},{-127,45,105},{-127,-87,94},{-127,-41,100},
+ {-127,-24,102},{-127,-99,92},{-127,52,77},{-127,73,73},
+ {-127,115,-70},{-127,72,-109},{-127,9,-106},{-127,-49,-45},
+ {-101,-24,72},{-87,-56,73},{-82,-89,73},{-80,-114,68},
+ {-85,-121,67},{-104,-124,71},{-127,-126,74},{-71,-18,68},
+ {-46,-5,69},{-21,19,57},{-17,55,76},{-36,62,80},
+ {-64,77,88},{-86,97,94},{-107,92,97},{-119,63,96},
+ {-106,53,99},{-111,39,98},{-101,12,95},{-79,2,90},
+ {-64,8,86},{-47,24,83},{-45,38,83},{-50,48,85},
+ {-72,56,92},{-95,60,97},{-127,-98,94},{-113,-92,94},
+ {-112,-107,91},{-119,-113,89},{-127,-114,88},{-127,-25,96},
+ {-127,-18,95},{-114,-19,95},{-111,-29,96},{-116,-37,95},
+ {-76,-6,86},{-48,7,80},{-34,26,77},{-32,48,84},
+ {-39,53,93},{-71,70,102},{-87,82,107},{-101,79,109},
+ {-114,55,108},{-111,-13,104},{-100,-57,91},{-95,-90,88},
+ {-93,-105,85},{-97,-117,81},{-106,-119,81},{-127,-121,82},
+ {-127,6,93},{-127,27,98},{-85,61,95},{-106,18,96},
+ {-110,27,97},{-112,-88,94},{-117,-57,96},{-127,-57,96},
+ {-127,-42,95},{-115,-35,100},{-110,-29,102},{-113,-17,100},
+ {-122,-16,100},{-127,-26,106},{-121,-19,104},{-115,-20,104},
+ {-113,-29,106},{-117,-32,103},{-127,-37,103},{-94,-40,71},
+ {-106,-31,91},{-104,-40,91},{-97,-32,71},{-127,-112,88},
+ {-121,-111,88},{-115,-105,91},{-115,-95,93},{-127,-100,84},
+ {-115,-96,85},{-115,-104,82},{-121,-109,81},{-127,-110,81},
+ {-105,28,100},{-103,20,99},{-84,55,97},{-92,54,99},
+ {-73,51,99},{-55,45,89},{-52,37,88},{-53,25,87},
+ {-66,13,92},{-79,8,95},{-98,14,100},{-104,38,100},
+ {-100,48,100},{-97,46,97},{-102,38,97},{-96,16,97},
+ {-79,11,93},{-68,15,90},{-57,27,86},{-56,36,86},
+ {-59,43,87},{-74,50,96},{-91,51,98},{-84,52,96},
+ {-101,22,96},{-102,29,96},{-113,59,78},{-102,85,79},
+ {-84,88,76},{-65,71,71},{-40,58,63},{-25,52,59},
+ {-28,21,48},{-50,0,53},{-71,-12,60},{-127,115,37},
+ {-127,126,-10},{-127,-25,-86},{-127,-59,24},{-127,-125,59},
+ {-127,-103,44},{-127,-73,41},{-127,-62,36},{-18,30,7},
+ {-17,41,-6},{-28,34,-56},{-68,56,-90},{-33,-6,9},
+ {-51,-16,-21},{-45,-1,-55},{-84,7,-85},{-97,-45,52},
+ {-104,-53,33},{-90,-91,49},{-95,-64,50},{-85,-117,51},
+ {-109,-97,47},{-111,-69,46},{-106,-121,56},{-99,-36,55},
+ {-100,-29,60},{-101,-22,64},{-100,-50,21},{-89,-40,-34},
+ {-83,-19,-69},{-69,111,-49},{-69,119,-9},{-69,109,30},
+ {-68,67,55},{-34,52,43},{-46,58,36},{-45,90,7},
+ {-25,72,16},{-25,79,-15},{-45,96,-25},{-45,87,-57},
+ {-25,69,-46},{-48,42,-75},{-65,3,-70},{-22,42,-26},
+ {-75,-22,19},{-72,-25,-27},{-13,52,-30},{-28,-18,-16},
+ {6,-13,-42},{37,7,-55},{46,41,-54},{31,65,-54},
+ {4,61,-40},{3,53,-37},{25,56,-50},{35,37,-52},
+ {28,10,-52},{5,-5,-39},{-21,-9,-17},{-9,46,-28},
+ {-6,39,-37},{-14,-3,-27},{6,0,-47},{25,12,-57},
+ {31,32,-57},{23,46,-56},{4,44,-46},{-19,37,-27},
+ {-20,22,-35},{-30,12,-35},{-22,11,-35},{-19,2,-35},
+ {-23,-2,-35},{-34,0,-9},{-35,-3,-22},{-35,5,-24},
+ {-25,26,-27},{-13,31,-34},{-13,30,-41},{-23,-2,-41},
+ {-18,2,-41},{-21,10,-41},{-29,12,-41},{-19,22,-41},
+ {6,42,-53},{25,44,-62},{34,31,-63},{28,11,-62},
+ {7,0,-54},{-14,-2,-34},{-5,37,-44},{-13,14,-42},
+ {-7,8,-43},{1,16,-47},{-4,22,-45},{3,30,-48},
+ {8,24,-49},{15,27,-50},{12,35,-50},{4,56,-62},
+ {33,60,-70},{48,38,-64},{41,7,-68},{6,-11,-63},
+ {-26,-16,-42},{-17,49,-49},
+};
+
+static signed char monkeyf[250][4] = {
+ {27,4,5,26}, {25,4,5,24}, {3,6,5,4}, {1,6,5,2}, {5,6,7,4},
+ {3,6,7,2}, {5,8,7,6}, {3,8,7,4}, {7,8,9,6},
+ {5,8,9,4}, {7,10,9,8}, {5,10,9,6}, {9,10,11,8},
+ {7,10,11,6}, {9,12,11,10}, {7,12,11,8}, {11,6,13,12},
+ {5,4,13,12}, {3,-2,13,12}, {-3,-4,13,12}, {-5,-10,13,12},
+ {-11,-12,14,12}, {-13,-18,14,13}, {-19,4,5,13}, {10,12,4,4},
+ {10,11,9,9}, {8,7,9,9}, {7,5,6,6}, {6,3,4,4},
+ {5,1,2,2}, {4,-1,0,0}, {3,-3,-2,-2}, {22,67,68,23},
+ {20,65,66,21}, {18,63,64,19}, {16,61,62,17}, {14,59,60,15},
+ {12,19,48,57}, {18,19,48,47}, {18,19,48,47}, {18,19,48,47},
+ {18,19,48,47}, {18,19,48,47}, {18,19,48,47}, {18,19,48,47},
+ {18,19,48,47}, {18,-9,-8,47}, {18,27,45,46}, {26,55,43,44},
+ {24,41,42,54}, {22,39,40,23}, {20,37,38,21}, {18,35,36,19},
+ {16,33,34,17}, {14,31,32,15}, {12,39,30,13}, {11,48,45,38},
+ {8,36,-19,9}, {8,-20,44,47}, {42,45,46,43}, {18,19,40,39},
+ {16,17,38,37}, {14,15,36,35}, {32,44,43,33}, {12,33,32,42},
+ {19,44,43,42}, {40,41,42,-27}, {8,9,39,-28}, {15,43,42,16},
+ {13,43,42,14}, {11,43,42,12}, {9,-30,42,10}, {37,12,38,-32},
+ {-33,37,45,46}, {-33,40,41,39}, {38,40,41,37}, {36,40,41,35},
+ {34,40,41,33}, {36,39,38,37}, {35,40,39,38}, {1,2,14,21},
+ {1,2,40,13}, {1,2,40,39}, {1,24,12,39}, {-34,36,38,11},
+ {35,38,36,37}, {-37,8,35,37}, {-11,-12,-45,40}, {-11,-12,39,38},
+ {-11,-12,37,36}, {-11,-12,35,34}, {33,34,40,41}, {33,34,38,39},
+ {33,34,36,37}, {33,-52,34,35}, {33,37,36,34}, {33,35,34,34},
+ {8,7,37,36}, {-32,7,35,46}, {-34,-33,45,46}, {4,-33,43,34},
+ {-34,-33,41,42}, {-34,-33,39,40}, {-34,-33,37,38}, {-34,-33,35,36},
+ {-34,-33,33,34}, {-34,-33,31,32}, {-34,-4,28,30}, {-5,-34,28,27},
+ {-35,-44,36,27}, {26,35,36,45}, {24,25,44,45}, {25,23,44,42},
+ {25,24,41,40}, {25,24,39,38}, {25,24,37,36}, {25,24,35,34},
+ {25,24,33,32}, {25,24,31,30}, {15,24,29,38}, {25,24,27,26},
+ {23,12,37,26}, {11,12,35,36}, {-86,-59,36,-80}, {-60,-61,36,35},
+ {-62,-63,36,35}, {-64,-65,36,35}, {-66,-67,36,35}, {-68,-69,36,35},
+ {-70,-71,36,35}, {-72,-73,36,35}, {-74,-75,36,35}, {42,43,53,58},
+ {40,41,57,56}, {38,39,55,57}, {-81,-80,37,56}, {-83,-82,55,52},
+ {-85,-84,51,49}, {-87,-86,48,49}, {47,50,51,48}, {46,48,51,49},
+ {43,46,49,44}, {-92,-91,45,42}, {-23,49,50,-20}, {-94,40,48,-24},
+ {-96,-22,48,49}, {-97,48,21,-90}, {-100,36,50,23}, {22,49,48,-100},
+ {-101,47,46,22}, {21,45,35,25}, {33,34,44,41}, {13,14,28,24},
+ {-107,26,30,-106}, {14,46,45,15}, {14,44,43,-110}, {-111,42,23,-110},
+ {6,7,45,46}, {45,44,47,46}, {45,46,47,48}, {47,46,49,48},
+ {17,49,47,48}, {17,36,46,48}, {35,36,44,45}, {35,36,40,43},
+ {35,36,38,39}, {-4,-3,37,35}, {-123,34,33,1}, {-9,-8,-7,-6},
+ {-10,-7,32,-125}, {-127,-11,-126,-126}, {-7,-6,5,31}, {4,5,33,30},
+ {4,39,33,32}, {4,35,32,38}, {20,21,39,38}, {4,37,38,5},
+ {-11,-10,36,3}, {-11,15,14,35}, {13,16,34,34}, {-13,14,13,13},
+ {-3,1,30,29}, {-3,28,29,1}, {-2,31,28,-1}, {12,13,27,30},
+ {-2,26,12,12}, {35,29,42,36}, {34,35,36,33}, {32,35,36,31},
+ {30,35,36,29}, {28,35,36,27}, {26,35,36,25}, {34,39,38,35},
+ {32,39,38,33}, {30,39,38,31}, {28,39,38,29}, {26,39,38,27},
+ {25,31,32,38}, {-18,-17,45,44}, {-18,17,28,44}, {-24,-20,42,-23},
+ {11,35,27,14}, {25,28,39,41}, {37,41,40,38}, {34,40,36,35},
+ {32,40,39,33}, {30,39,31,40}, {21,29,39,22}, {-31,37,28,4},
+ {-32,33,35,36}, {32,33,34,34}, {18,35,36,48}, {34,25,40,35},
+ {24,25,38,39}, {24,25,36,37}, {24,25,34,35}, {24,25,32,33},
+ {24,13,41,31}, {17,11,41,35}, {15,16,34,35}, {13,14,34,35},
+ {11,12,34,35}, {9,10,34,35}, {7,8,34,35}, {26,25,37,36},
+ {35,36,37,38}, {37,36,39,38}, {37,38,39,40}, {25,31,36,39},
+ {18,34,35,30}, {17,22,30,33}, {19,29,21,20}, {16,26,29,17},
+ {24,29,28,25}, {22,31,28,23}, {20,31,30,21}, {18,31,30,19},
+ {16,30,17,17}, {-21,-22,35,34}, {-21,-22,33,32}, {-21,-22,31,30},
+ {-21,-22,29,28}, {-21,-22,27,26}, {-28,-22,25,31}, {24,28,29,30},
+ {23,24,26,27}, {23,24,25,25}, {-69,-35,-32,27}, {-70,26,25,-66},
+ {-68,-67,24,-33},
+};
+
+#define VERT_MARK 1
+
+#define EDGE_ORIG 1
+#define EDGE_MARK 2
+
+#define FACE_MARK 1
+#define FACE_NEW 2
+
+void bmesh_create_grid_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator bmop, prevop;
+ BMVert *eve, *preveve;
+ BMEdge *e;
+ float vec[3], mat[4][4], phi, phid, dia = BMO_slot_float_get(op, "size");
+ int a, tot = BMO_slot_int_get(op, "xsegments"), seg = BMO_slot_int_get(op, "ysegments");
+
+ if (tot < 2) tot = 2;
+ if (seg < 2) seg = 2;
+
+ BMO_slot_mat4_get(op, "mat", mat);
+
+ /* one segment first: the X axis */
+ phi = 1.0f;
+ phid = 2.0f / ((float)tot - 1);
+ for (a = 0; a < tot; a++) {
+ vec[0] = dia * phi;
+ vec[1] = -dia;
+ vec[2] = 0.0f;
+ mul_m4_v3(mat, vec);
+
+ eve = BM_vert_create(bm, vec, NULL);
+ BMO_elem_flag_enable(bm, eve, VERT_MARK);
+
+ if (a) {
+ e = BM_edge_create(bm, preveve, eve, NULL, TRUE);
+ BMO_elem_flag_enable(bm, e, EDGE_ORIG);
+ }
+
+ preveve = eve;
+ phi -= phid;
+ }
+
+ /* extrude and translate */
+ vec[0] = vec[2] = 0.0f;
+ vec[1] = dia * phid;
+ mul_mat3_m4_v3(mat, vec);
+
+ for (a = 0; a < seg - 1; a++) {
+ if (a) {
+ BMO_op_initf(bm, &bmop, "extrude_edge_only edges=%s", &prevop, "geomout");
+ BMO_op_exec(bm, &bmop);
+ BMO_op_finish(bm, &prevop);
+
+ BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", VERT_MARK, BM_VERT);
+ }
+ else {
+ BMO_op_initf(bm, &bmop, "extrude_edge_only edges=%fe", EDGE_ORIG);
+ BMO_op_exec(bm, &bmop);
+ BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", VERT_MARK, BM_VERT);
+ }
+
+ BMO_op_callf(bm, "translate vec=%v verts=%s", vec, &bmop, "geomout");
+ prevop = bmop;
+ }
+
+ if (a)
+ BMO_op_finish(bm, &bmop);
+
+ BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
+}
+
+void bmesh_create_uvsphere_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator bmop, prevop;
+ BMVert *eve, *preveve;
+ BMEdge *e;
+ BMIter iter;
+ float vec[3], mat[4][4], cmat[3][3], phi, q[4];
+ float phid, dia = BMO_slot_float_get(op, "diameter");
+ int a, seg = BMO_slot_int_get(op, "segments"), tot = BMO_slot_int_get(op, "revolutions");
+
+ BMO_slot_mat4_get(op, "mat", mat);
+
+ phid = 2.0f * (float)M_PI / tot;
+ phi = 0.25f * (float)M_PI;
+
+ /* one segment first */
+ phi = 0;
+ phid /= 2;
+ for (a = 0; a <= tot; a++) {
+ /* Going in this direction, then edge extruding, makes normals face outward */
+ vec[0] = -dia * sinf(phi);
+ vec[1] = 0.0;
+ vec[2] = dia * cosf(phi);
+ eve = BM_vert_create(bm, vec, NULL);
+ BMO_elem_flag_enable(bm, eve, VERT_MARK);
+
+ if (a != 0) {
+ e = BM_edge_create(bm, preveve, eve, NULL, FALSE);
+ BMO_elem_flag_enable(bm, e, EDGE_ORIG);
+ }
+
+ phi+= phid;
+ preveve = eve;
+ }
+
+ /* extrude and rotate; negative phi to make normals face outward */
+ phi = -M_PI / seg;
+ q[0] = cosf(phi);
+ q[3] = sinf(phi);
+ q[1] = q[2] = 0.0f;
+ quat_to_mat3(cmat, q);
+
+ for (a = 0; a < seg; a++) {
+ if (a) {
+ BMO_op_initf(bm, &bmop, "extrude_edge_only edges=%s", &prevop, "geomout");
+ BMO_op_exec(bm, &bmop);
+ BMO_op_finish(bm, &prevop);
+ }
+ else {
+ BMO_op_initf(bm, &bmop, "extrude_edge_only edges=%fe", EDGE_ORIG);
+ BMO_op_exec(bm, &bmop);
+ }
+
+ BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", VERT_MARK, BM_VERT);
+ BMO_op_callf(bm, "rotate cent=%v mat=%m3 verts=%s", vec, cmat, &bmop, "geomout");
+
+ prevop = bmop;
+ }
+
+ if (a)
+ BMO_op_finish(bm, &bmop);
+
+ {
+ float len, len2, vec2[3];
+
+ len= 2*dia*sinf(phid / 2.0f);
+
+ /* length of one segment in shortest parallen */
+ vec[0]= dia*sinf(phid);
+ vec[1]= 0.0;
+ vec[2]= dia*cosf(phid);
+
+ mul_v3_m3v3(vec2, cmat, vec);
+ len2= len_v3v3(vec, vec2);
+
+ /* use shortest segment length divided by 3 as merge threshold */
+ BMO_op_callf(bm, "removedoubles verts=%fv dist=%f", VERT_MARK, MIN2(len, len2) / 3.0f);
+ }
+
+ /* and now do imat */
+ BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, eve, VERT_MARK)) {
+ mul_m4_v3(mat, eve->co);
+ }
+ }
+
+ BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
+}
+
+void bmesh_create_icosphere_exec(BMesh *bm, BMOperator *op)
+{
+ BMVert *eva[12];
+ BMVert *v;
+ BMIter liter;
+ BMIter viter;
+ BMLoop *l;
+ float vec[3], mat[4][4] /* , phi, phid */;
+ float dia = BMO_slot_float_get(op, "diameter");
+ int a, subdiv = BMO_slot_int_get(op, "subdivisions");
+
+ BMO_slot_mat4_get(op, "mat", mat);
+
+ /* phid = 2.0f * (float)M_PI / subdiv; */ /* UNUSED */
+ /* phi = 0.25f * (float)M_PI; */ /* UNUSED */
+
+ dia /= 200.0f;
+ for (a = 0; a < 12; a++) {
+ vec[0] = dia * icovert[a][0];
+ vec[1] = dia * icovert[a][1];
+ vec[2] = dia * icovert[a][2];
+ eva[a] = BM_vert_create(bm, vec, NULL);
+
+ BMO_elem_flag_enable(bm, eva[a], VERT_MARK);
+ }
+
+ for (a = 0; a < 20; a++) {
+ BMFace *eftemp;
+ BMVert *v1, *v2, *v3;
+
+ v1 = eva[icoface[a][0]];
+ v2 = eva[icoface[a][1]];
+ v3 = eva[icoface[a][2]];
+
+ eftemp = BM_face_create_quad_tri(bm, v1, v2, v3, NULL, NULL, FALSE);
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, eftemp) {
+ BMO_elem_flag_enable(bm, l->e, EDGE_MARK);
+ }
+
+ BMO_elem_flag_enable(bm, eftemp, FACE_MARK);
+ }
+
+ dia *= 200.0f;
+
+ for (a = 1; a < subdiv; a++) {
+ BMOperator bmop;
+
+ BMO_op_initf(bm, &bmop,
+ "esubd edges=%fe smooth=%f numcuts=%i gridfill=%i beauty=%i",
+ EDGE_MARK, dia, 1, 1, B_SPHERE);
+ BMO_op_exec(bm, &bmop);
+ BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", VERT_MARK, BM_VERT);
+ BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", EDGE_MARK, BM_EDGE);
+ BMO_op_finish(bm, &bmop);
+ }
+
+ /* must transform after becayse of sphere subdivision */
+ BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
+ mul_m4_v3(mat, v->co);
+ }
+ }
+
+ BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
+}
+
+void bmesh_create_monkey_exec(BMesh *bm, BMOperator *op)
+{
+ BMVert *eve;
+ BMVert **tv = MEM_mallocN(sizeof(*tv)*monkeynv * 2, "tv");
+ float mat[4][4];
+ int i;
+
+ BMO_slot_mat4_get(op, "mat", mat);
+
+ for (i = 0; i < monkeynv; i++) {
+ float v[3];
+
+ v[0] = (monkeyv[i][0] + 127) / 128.0, v[1] = monkeyv[i][1] / 128.0, v[2] = monkeyv[i][2] / 128.0;
+
+ tv[i] = BM_vert_create(bm, v, NULL);
+ BMO_elem_flag_enable(bm, tv[i], VERT_MARK);
+
+ tv[monkeynv + i] = (fabsf(v[0] = -v[0]) < 0.001f) ?
+ tv[i] :
+ (eve = BM_vert_create(bm, v, NULL), mul_m4_v3(mat, eve->co), eve);
+
+ BMO_elem_flag_enable(bm, tv[monkeynv + i], VERT_MARK);
+
+ mul_m4_v3(mat, tv[i]->co);
+ }
+
+ for (i = 0; i < monkeynf; i++) {
+ BM_face_create_quad_tri(bm,
+ tv[monkeyf[i][0] + i - monkeyo],
+ tv[monkeyf[i][1] + i - monkeyo],
+ tv[monkeyf[i][2] + i - monkeyo],
+ (monkeyf[i][3] != monkeyf[i][2]) ? tv[monkeyf[i][3] + i - monkeyo] : NULL,
+ NULL, FALSE);
+
+ BM_face_create_quad_tri(bm,
+ tv[monkeynv + monkeyf[i][2] + i - monkeyo],
+ tv[monkeynv + monkeyf[i][1] + i - monkeyo],
+ tv[monkeynv + monkeyf[i][0] + i - monkeyo],
+ (monkeyf[i][3] != monkeyf[i][2]) ? tv[monkeynv + monkeyf[i][3] + i - monkeyo]: NULL,
+ NULL, FALSE);
+ }
+
+ MEM_freeN(tv);
+
+ BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
+}
+
+
+void bmesh_create_circle_exec(BMesh *bm, BMOperator *op)
+{
+ BMVert *v1, *lastv1 = NULL, *cent1, *firstv1 = NULL;
+ float vec[3], mat[4][4], phi, phid;
+ float dia = BMO_slot_float_get(op, "diameter");
+ int cap_ends = BMO_slot_int_get(op, "cap_ends"), segs = BMO_slot_int_get(op, "segments");
+ int cap_tris = BMO_slot_int_get(op, "cap_tris");
+ int a;
+
+ if (!segs)
+ return;
+
+ BMO_slot_mat4_get(op, "mat", mat);
+
+ phid = 2.0f * (float)M_PI / segs;
+ phi = .25f * (float)M_PI;
+
+ if (cap_ends) {
+ vec[0] = vec[1] = 0.0f;
+ vec[2] = 0.0;
+ mul_m4_v3(mat, vec);
+
+ cent1 = BM_vert_create(bm, vec, NULL);
+ }
+
+ for (a = 0; a < segs; a++, phi += phid) {
+ /* Going this way ends up with normal(s) upward */
+ vec[0] = -dia * sinf(phi);
+ vec[1] = dia * cosf(phi);
+ vec[2] = 0.0f;
+ mul_m4_v3(mat, vec);
+ v1 = BM_vert_create(bm, vec, NULL);
+
+ BMO_elem_flag_enable(bm, v1, VERT_MARK);
+
+ if (lastv1)
+ BM_edge_create(bm, v1, lastv1, NULL, FALSE);
+
+ if (a && cap_ends) {
+ BMFace *f;
+
+ f = BM_face_create_quad_tri(bm, cent1, lastv1, v1, NULL, NULL, FALSE);
+ BMO_elem_flag_enable(bm, f, FACE_NEW);
+ }
+
+ if (!firstv1)
+ firstv1 = v1;
+
+ lastv1 = v1;
+ }
+
+ if (!a)
+ return;
+
+ BM_edge_create(bm, lastv1, firstv1, NULL, FALSE);
+
+ if (cap_ends) {
+ BMFace *f;
+
+ f = BM_face_create_quad_tri(bm, cent1, v1, firstv1, NULL, NULL, FALSE);
+ BMO_elem_flag_enable(bm, f, FACE_NEW);
+ }
+
+ if (!cap_tris) {
+ BMO_op_callf(bm, "dissolvefaces faces=%ff", FACE_NEW);
+ }
+
+ BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
+}
+
+void bmesh_create_cone_exec(BMesh *bm, BMOperator *op)
+{
+ BMVert *v1, *v2, *lastv1 = NULL, *lastv2 = NULL, *cent1, *cent2, *firstv1, *firstv2;
+ float vec[3], mat[4][4], phi, phid;
+ float dia1 = BMO_slot_float_get(op, "diameter1");
+ float dia2 = BMO_slot_float_get(op, "diameter2");
+ float depth = BMO_slot_float_get(op, "depth");
+ int cap_ends = BMO_slot_int_get(op, "cap_ends"), segs = BMO_slot_int_get(op, "segments");
+ int cap_tris = BMO_slot_int_get(op, "cap_tris");
+ int a;
+
+ if (!segs)
+ return;
+
+ BMO_slot_mat4_get(op, "mat", mat);
+
+ phid = 2.0f * (float)M_PI / segs;
+ phi = 0.25f * (float)M_PI;
+
+ depth *= 0.5f;
+ if (cap_ends) {
+ vec[0] = vec[1] = 0.0f;
+ vec[2] = -depth;
+ mul_m4_v3(mat, vec);
+
+ cent1 = BM_vert_create(bm, vec, NULL);
+
+ vec[0] = vec[1] = 0.0f;
+ vec[2] = depth;
+ mul_m4_v3(mat, vec);
+
+ cent2 = BM_vert_create(bm, vec, NULL);
+
+ BMO_elem_flag_enable(bm, cent1, VERT_MARK);
+ BMO_elem_flag_enable(bm, cent2, VERT_MARK);
+ }
+
+ for (a = 0; a < segs; a++, phi += phid) {
+ vec[0] = dia1 * sinf(phi);
+ vec[1] = dia1 * cosf(phi);
+ vec[2] = -depth;
+ mul_m4_v3(mat, vec);
+ v1 = BM_vert_create(bm, vec, NULL);
+
+ vec[0] = dia2 * sinf(phi);
+ vec[1] = dia2 * cosf(phi);
+ vec[2] = depth;
+ mul_m4_v3(mat, vec);
+ v2 = BM_vert_create(bm, vec, NULL);
+
+ BMO_elem_flag_enable(bm, v1, VERT_MARK);
+ BMO_elem_flag_enable(bm, v2, VERT_MARK);
+
+ if (a) {
+ if (cap_ends) {
+ BMFace *f;
+
+ f = BM_face_create_quad_tri(bm, cent1, lastv1, v1, NULL, NULL, FALSE);
+ BMO_elem_flag_enable(bm, f, FACE_NEW);
+ f = BM_face_create_quad_tri(bm, cent2, v2, lastv2, NULL, NULL, FALSE);
+ BMO_elem_flag_enable(bm, f, FACE_NEW);
+ }
+ BM_face_create_quad_tri(bm, lastv1, lastv2, v2, v1, NULL, FALSE);
+ }
+ else {
+ firstv1 = v1;
+ firstv2 = v2;
+ }
+
+ lastv1 = v1;
+ lastv2 = v2;
+ }
+
+ if (!a)
+ return;
+
+ if (cap_ends) {
+ BMFace *f;
+
+ f = BM_face_create_quad_tri(bm, cent1, v1, firstv1, NULL, NULL, FALSE);
+ BMO_elem_flag_enable(bm, f, FACE_NEW);
+ f = BM_face_create_quad_tri(bm, cent2, firstv2, v2, NULL, NULL, FALSE);
+ BMO_elem_flag_enable(bm, f, FACE_NEW);
+ }
+
+ if (!cap_tris) {
+ BMO_op_callf(bm, "dissolvefaces faces=%ff", FACE_NEW);
+ }
+
+ BM_face_create_quad_tri(bm, v1, v2, firstv2, firstv1, NULL, FALSE);
+
+ BMO_op_callf(bm, "removedoubles verts=%fv dist=%f", VERT_MARK, 0.000001);
+ BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
+}
+
+void bmesh_create_cube_exec(BMesh *bm, BMOperator *op)
+{
+ BMVert *v1, *v2, *v3, *v4, *v5, *v6, *v7, *v8;
+ float vec[3], mat[4][4], off = BMO_slot_float_get(op, "size") / 2.0f;
+
+ BMO_slot_mat4_get(op, "mat", mat);
+
+ if (!off) off = 0.5f;
+
+ vec[0] = -off;
+ vec[1] = -off;
+ vec[2] = -off;
+ mul_m4_v3(mat, vec);
+ v1 = BM_vert_create(bm, vec, NULL);
+ BMO_elem_flag_enable(bm, v1, VERT_MARK);
+
+ vec[0] = -off;
+ vec[1] = off;
+ vec[2] = -off;
+ mul_m4_v3(mat, vec);
+ v2 = BM_vert_create(bm, vec, NULL);
+ BMO_elem_flag_enable(bm, v2, VERT_MARK);
+
+ vec[0] = off;
+ vec[1] = off;
+ vec[2] = -off;
+ mul_m4_v3(mat, vec);
+ v3 = BM_vert_create(bm, vec, NULL);
+ BMO_elem_flag_enable(bm, v3, VERT_MARK);
+
+ vec[0] = off;
+ vec[1] = -off;
+ vec[2] = -off;
+ mul_m4_v3(mat, vec);
+ v4 = BM_vert_create(bm, vec, NULL);
+ BMO_elem_flag_enable(bm, v4, VERT_MARK);
+
+ vec[0] = -off;
+ vec[1] = -off;
+ vec[2] = off;
+ mul_m4_v3(mat, vec);
+ v5 = BM_vert_create(bm, vec, NULL);
+ BMO_elem_flag_enable(bm, v5, VERT_MARK);
+
+ vec[0] = -off;
+ vec[1] = off;
+ vec[2] = off;
+ mul_m4_v3(mat, vec);
+ v6 = BM_vert_create(bm, vec, NULL);
+ BMO_elem_flag_enable(bm, v6, VERT_MARK);
+
+ vec[0] = off;
+ vec[1] = off;
+ vec[2] = off;
+ mul_m4_v3(mat, vec);
+ v7 = BM_vert_create(bm, vec, NULL);
+ BMO_elem_flag_enable(bm, v7, VERT_MARK);
+
+ vec[0] = off;
+ vec[1] = -off;
+ vec[2] = off;
+ mul_m4_v3(mat, vec);
+ v8 = BM_vert_create(bm, vec, NULL);
+ BMO_elem_flag_enable(bm, v8, VERT_MARK);
+
+ /* the four sides */
+ BM_face_create_quad_tri(bm, v5, v6, v2, v1, NULL, FALSE);
+ BM_face_create_quad_tri(bm, v6, v7, v3, v2, NULL, FALSE);
+ BM_face_create_quad_tri(bm, v7, v8, v4, v3, NULL, FALSE);
+ BM_face_create_quad_tri(bm, v8, v5, v1, v4, NULL, FALSE);
+
+ /* top/bottom */
+ BM_face_create_quad_tri(bm, v1, v2, v3, v4, NULL, FALSE);
+ BM_face_create_quad_tri(bm, v8, v7, v6, v5, NULL, FALSE);
+
+ BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
+}
diff --git a/source/blender/bmesh/operators/bmo_removedoubles.c b/source/blender/bmesh/operators/bmo_removedoubles.c
new file mode 100644
index 00000000000..2eb1cf7db3e
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_removedoubles.c
@@ -0,0 +1,590 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Joseph Eagar.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_array.h"
+
+#include "BKE_customdata.h"
+
+#include "bmesh.h"
+#include "bmesh_private.h"
+
+#include "bmesh_operators_private.h" /* own include */
+
+static void remdoubles_splitface(BMFace *f, BMesh *bm, BMOperator *op)
+{
+ BMIter liter;
+ BMLoop *l;
+ BMVert *v2, *doub;
+ int split = FALSE;
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", l->v);
+ /* ok: if v2 is NULL (e.g. not in the map) then it's
+ * a target vert, otherwise it's a doubl */
+ if ((v2 && BM_vert_in_face(f, v2)) &&
+ (v2 != l->prev->v) &&
+ (v2 != l->next->v))
+ {
+ doub = l->v;
+ split = TRUE;
+ break;
+ }
+ }
+
+ if (split && doub != v2) {
+ BMLoop *nl;
+ BMFace *f2 = BM_face_split(bm, f, doub, v2, &nl, NULL);
+
+ remdoubles_splitface(f, bm, op);
+ remdoubles_splitface(f2, bm, op);
+ }
+}
+
+#define ELE_DEL 1
+#define EDGE_COL 2
+#define FACE_MARK 2
+
+#if 0
+int remdoubles_face_overlaps(BMesh *bm, BMVert **varr,
+ int len, BMFace *exclude,
+ BMFace **overlapface)
+{
+ BMIter vertfaces;
+ BMFace *f;
+ int i, amount;
+
+ if (overlapface) *overlapface = NULL;
+
+ for (i = 0; i < len; i++) {
+ f = BM_iter_new(&vertfaces, bm, BM_FACES_OF_VERT, varr[i]);
+ while (f) {
+ amount = BM_verts_in_face(bm, f, varr, len);
+ if (amount >= len) {
+ if (overlapface) *overlapface = f;
+ return TRUE;
+ }
+ f = BM_iter_step(&vertfaces);
+ }
+ }
+ return FALSE;
+}
+#endif
+
+void bmesh_weldverts_exec(BMesh *bm, BMOperator *op)
+{
+ BMIter iter, liter;
+ BMVert *v, *v2;
+ BMEdge *e, *e2, **edges = NULL;
+ BLI_array_declare(edges);
+ BMLoop *l, *l2, **loops = NULL;
+ BLI_array_declare(loops);
+ BMFace *f, *f2;
+ int a, b;
+
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ if ((v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", v))) {
+ BMO_elem_flag_enable(bm, v, ELE_DEL);
+
+ /* merge the vertex flags, else we get randomly selected/unselected verts */
+ BM_elem_flag_merge(v, v2);
+ }
+ }
+
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ remdoubles_splitface(f, bm, op);
+ }
+
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (BMO_elem_flag_test(bm, e->v1, ELE_DEL) || BMO_elem_flag_test(bm, e->v2, ELE_DEL)) {
+ v = BMO_slot_map_ptr_get(bm, op, "targetmap", e->v1);
+ v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", e->v2);
+
+ if (!v) v = e->v1;
+ if (!v2) v2 = e->v2;
+
+ if (v == v2)
+ BMO_elem_flag_enable(bm, e, EDGE_COL);
+ else if (!BM_edge_exists(v, v2))
+ BM_edge_create(bm, v, v2, e, TRUE);
+
+ BMO_elem_flag_enable(bm, e, ELE_DEL);
+ }
+ }
+
+ /* BMESH_TODO, stop abusing face index here */
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ BM_elem_index_set(f, 0); /* set_dirty! */
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ if (BMO_elem_flag_test(bm, l->v, ELE_DEL)) {
+ BMO_elem_flag_enable(bm, f, FACE_MARK|ELE_DEL);
+ }
+ if (BMO_elem_flag_test(bm, l->e, EDGE_COL)) {
+ BM_elem_index_set(f, BM_elem_index_get(f) + 1); /* set_dirty! */
+ }
+ }
+ }
+ bm->elem_index_dirty |= BM_FACE;
+
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, f, FACE_MARK))
+ continue;
+
+ if (f->len - BM_elem_index_get(f) < 3) {
+ BMO_elem_flag_enable(bm, f, ELE_DEL);
+ continue;
+ }
+
+ BLI_array_empty(edges);
+ BLI_array_empty(loops);
+ a = 0;
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ v = l->v;
+ v2 = l->next->v;
+ if (BMO_elem_flag_test(bm, v, ELE_DEL)) {
+ v = BMO_slot_map_ptr_get(bm, op, "targetmap", v);
+ }
+ if (BMO_elem_flag_test(bm, v2, ELE_DEL)) {
+ v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", v2);
+ }
+
+ e2 = v != v2 ? BM_edge_exists(v, v2) : NULL;
+ if (e2) {
+ for (b = 0; b < a; b++) {
+ if (edges[b] == e2) {
+ break;
+ }
+ }
+ if (b != a) {
+ continue;
+ }
+
+ BLI_array_growone(edges);
+ BLI_array_growone(loops);
+
+ edges[a] = e2;
+ loops[a] = l;
+
+ a++;
+ }
+ }
+
+ if (BLI_array_count(loops) < 3)
+ continue;
+ v = loops[0]->v;
+ v2 = loops[1]->v;
+
+ if (BMO_elem_flag_test(bm, v, ELE_DEL)) {
+ v = BMO_slot_map_ptr_get(bm, op, "targetmap", v);
+ }
+ if (BMO_elem_flag_test(bm, v2, ELE_DEL)) {
+ v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", v2);
+ }
+
+ f2 = BM_face_create_ngon(bm, v, v2, edges, a, TRUE);
+ if (f2 && (f2 != f)) {
+ BM_elem_attrs_copy(bm, bm, f, f2);
+
+ a = 0;
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f2) {
+ l2 = loops[a];
+ BM_elem_attrs_copy(bm, bm, l2, l);
+
+ a++;
+ }
+ }
+ }
+
+ BMO_op_callf(bm, "del geom=%fvef context=%i", ELE_DEL, DEL_ONLYTAGGED);
+
+ BLI_array_free(edges);
+ BLI_array_free(loops);
+}
+
+static int vergaverco(const void *e1, const void *e2)
+{
+ const BMVert *v1 = *(void **)e1, *v2 = *(void **)e2;
+ float x1 = v1->co[0] + v1->co[1] + v1->co[2];
+ float x2 = v2->co[0] + v2->co[1] + v2->co[2];
+
+ if (x1 > x2) return 1;
+ else if (x1 < x2) return -1;
+ else return 0;
+}
+
+#define VERT_TESTED 1
+#define VERT_DOUBLE 2
+#define VERT_TARGET 4
+#define VERT_KEEP 8
+#define VERT_MARK 16
+#define VERT_IN 32
+
+#define EDGE_MARK 1
+
+void bmesh_pointmerge_facedata_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMIter iter;
+ BMVert *v, *snapv;
+ BMLoop *l, *firstl = NULL;
+ float fac;
+ int i, tot;
+
+ snapv = BMO_iter_new(&siter, bm, op, "snapv", BM_VERT);
+ tot = BM_vert_face_count(snapv);
+
+ if (!tot)
+ return;
+
+ fac = 1.0f / tot;
+ BM_ITER(l, &iter, bm, BM_LOOPS_OF_VERT, snapv) {
+ if (!firstl) {
+ firstl = l;
+ }
+
+ for (i = 0; i < bm->ldata.totlayer; i++) {
+ if (CustomData_layer_has_math(&bm->ldata, i)) {
+ int type = bm->ldata.layers[i].type;
+ void *e1, *e2;
+
+ e1 = CustomData_bmesh_get_layer_n(&bm->ldata, firstl->head.data, i);
+ e2 = CustomData_bmesh_get_layer_n(&bm->ldata, l->head.data, i);
+
+ CustomData_data_multiply(type, e2, fac);
+
+ if (l != firstl)
+ CustomData_data_add(type, e1, e2);
+ }
+ }
+ }
+
+ BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) {
+ BM_ITER(l, &iter, bm, BM_LOOPS_OF_VERT, v) {
+ if (l == firstl) {
+ continue;
+ }
+
+ CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, firstl->head.data, &l->head.data);
+ }
+ }
+}
+
+void bmesh_vert_average_facedata_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMIter iter;
+ BMVert *v;
+ BMLoop *l /* , *firstl = NULL */;
+ CDBlockBytes min, max;
+ void *block;
+ int i, type;
+
+ for (i = 0; i < bm->ldata.totlayer; i++) {
+ if (!CustomData_layer_has_math(&bm->ldata, i))
+ continue;
+
+ type = bm->ldata.layers[i].type;
+ CustomData_data_initminmax(type, &min, &max);
+
+ BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) {
+ BM_ITER(l, &iter, bm, BM_LOOPS_OF_VERT, v) {
+ block = CustomData_bmesh_get_layer_n(&bm->ldata, l->head.data, i);
+ CustomData_data_dominmax(type, block, &min, &max);
+ }
+ }
+
+ CustomData_data_multiply(type, &min, 0.5f);
+ CustomData_data_multiply(type, &max, 0.5f);
+ CustomData_data_add(type, &min, &max);
+
+ BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) {
+ BM_ITER(l, &iter, bm, BM_LOOPS_OF_VERT, v) {
+ block = CustomData_bmesh_get_layer_n(&bm->ldata, l->head.data, i);
+ CustomData_data_copy_value(type, &min, block);
+ }
+ }
+ }
+}
+
+void bmesh_pointmerge_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator weldop;
+ BMOIter siter;
+ BMVert *v, *snapv = NULL;
+ float vec[3];
+
+ BMO_slot_vec_get(op, "mergeco", vec);
+
+ //BMO_op_callf(bm, "collapse_uvs edges=%s", op, "edges");
+ BMO_op_init(bm, &weldop, "weldverts");
+
+ BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) {
+ if (!snapv) {
+ snapv = v;
+ copy_v3_v3(snapv->co, vec);
+ }
+ else {
+ BMO_slot_map_ptr_insert(bm, &weldop, "targetmap", v, snapv);
+ }
+ }
+
+ BMO_op_exec(bm, &weldop);
+ BMO_op_finish(bm, &weldop);
+}
+
+void bmesh_collapse_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator weldop;
+ BMWalker walker;
+ BMIter iter;
+ BMEdge *e, **edges = NULL;
+ BLI_array_declare(edges);
+ float min[3], max[3];
+ int i, tot;
+
+ BMO_op_callf(bm, "collapse_uvs edges=%s", op, "edges");
+ BMO_op_init(bm, &weldop, "weldverts");
+
+ BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE);
+
+ BMW_init(&walker, bm, BMW_SHELL,
+ BMW_MASK_NOP, EDGE_MARK, BMW_MASK_NOP, BMW_MASK_NOP,
+ BMW_NIL_LAY);
+
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, e, EDGE_MARK))
+ continue;
+
+ e = BMW_begin(&walker, e->v1);
+ BLI_array_empty(edges);
+
+ INIT_MINMAX(min, max);
+ for (tot = 0; e; tot++, e = BMW_step(&walker)) {
+ BLI_array_growone(edges);
+ edges[tot] = e;
+
+ DO_MINMAX(e->v1->co, min, max);
+ DO_MINMAX(e->v2->co, min, max);
+ }
+
+ add_v3_v3v3(min, min, max);
+ mul_v3_fl(min, 0.5f);
+
+ /* snap edges to a point. for initial testing purposes anyway */
+ for (i = 0; i < tot; i++) {
+ copy_v3_v3(edges[i]->v1->co, min);
+ copy_v3_v3(edges[i]->v2->co, min);
+
+ if (edges[i]->v1 != edges[0]->v1)
+ BMO_slot_map_ptr_insert(bm, &weldop, "targetmap", edges[i]->v1, edges[0]->v1);
+ if (edges[i]->v2 != edges[0]->v1)
+ BMO_slot_map_ptr_insert(bm, &weldop, "targetmap", edges[i]->v2, edges[0]->v1);
+ }
+ }
+
+ BMO_op_exec(bm, &weldop);
+ BMO_op_finish(bm, &weldop);
+
+ BMW_end(&walker);
+ BLI_array_free(edges);
+}
+
+/* uv collapse functio */
+static void bmesh_collapsecon_do_layer(BMesh *bm, BMOperator *op, int layer)
+{
+ BMIter iter, liter;
+ BMFace *f;
+ BMLoop *l, *l2;
+ BMWalker walker;
+ void **blocks = NULL;
+ BLI_array_declare(blocks);
+ CDBlockBytes min, max;
+ int i, tot, type = bm->ldata.layers[layer].type;
+
+ /* clear all short flags */
+ BMO_mesh_flag_disable_all(bm, op, BM_ALL, (1 << 16) - 1);
+
+ BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE);
+
+ BMW_init(&walker, bm, BMW_LOOPDATA_ISLAND,
+ BMW_MASK_NOP, EDGE_MARK, BMW_MASK_NOP, BMW_MASK_NOP,
+ layer);
+
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ if (BMO_elem_flag_test(bm, l->e, EDGE_MARK)) {
+ /* wal */
+ BLI_array_empty(blocks);
+ tot = 0;
+ l2 = BMW_begin(&walker, l);
+
+ CustomData_data_initminmax(type, &min, &max);
+ for (tot = 0; l2; tot++, l2 = BMW_step(&walker)) {
+ BLI_array_growone(blocks);
+ blocks[tot] = CustomData_bmesh_get_layer_n(&bm->ldata, l2->head.data, layer);
+ CustomData_data_dominmax(type, blocks[tot], &min, &max);
+ }
+
+ if (tot) {
+ CustomData_data_multiply(type, &min, 0.5f);
+ CustomData_data_multiply(type, &max, 0.5f);
+ CustomData_data_add(type, &min, &max);
+
+ /* snap CD (uv, vcol) points to their centroi */
+ for (i = 0; i < tot; i++) {
+ CustomData_data_copy_value(type, &min, blocks[i]);
+ }
+ }
+ }
+ }
+ }
+
+ BMW_end(&walker);
+ BLI_array_free(blocks);
+}
+
+void bmesh_collapsecon_exec(BMesh *bm, BMOperator *op)
+{
+ int i;
+
+ for (i = 0; i < bm->ldata.totlayer; i++) {
+ if (CustomData_layer_has_math(&bm->ldata, i))
+ bmesh_collapsecon_do_layer(bm, op, i);
+ }
+}
+
+void bmesh_finddoubles_common(BMesh *bm, BMOperator *op, BMOperator *optarget, const char *targetmapname)
+{
+ BMOIter oiter;
+ BMVert *v, *v2;
+ BMVert **verts = NULL;
+ BLI_array_declare(verts);
+ float dist, dist3;
+ int i, j, len, keepvert = 0;
+
+ dist = BMO_slot_float_get(op, "dist");
+ dist3 = dist * 3.0f;
+
+ i = 0;
+ BMO_ITER(v, &oiter, bm, op, "verts", BM_VERT) {
+ BLI_array_growone(verts);
+ verts[i++] = v;
+ }
+
+ /* Test whether keepverts arg exists and is non-empty */
+ if (BMO_slot_exists(op, "keepverts")) {
+ keepvert = BMO_iter_new(&oiter, bm, op, "keepverts", BM_VERT) != NULL;
+ }
+
+ /* sort by vertex coordinates added togethe */
+ qsort(verts, BLI_array_count(verts), sizeof(void *), vergaverco);
+
+ /* Flag keepverts */
+ if (keepvert) {
+ BMO_slot_buffer_flag_enable(bm, op, "keepverts", VERT_KEEP, BM_VERT);
+ }
+
+ len = BLI_array_count(verts);
+ for (i = 0; i < len; i++) {
+ v = verts[i];
+ if (BMO_elem_flag_test(bm, v, VERT_DOUBLE)) continue;
+
+ for (j = i + 1; j < len; j++) {
+ v2 = verts[j];
+
+ /* Compare sort values of the verts using 3x tolerance (allowing for the tolerance
+ * on each of the three axes). This avoids the more expensive length comparison
+ * for most vertex pairs. */
+ if ((v2->co[0]+v2->co[1]+v2->co[2])-(v->co[0]+v->co[1]+v->co[2]) > dist3)
+ break;
+
+ if (keepvert) {
+ if (BMO_elem_flag_test(bm, v2, VERT_KEEP) == BMO_elem_flag_test(bm, v, VERT_KEEP))
+ continue;
+ }
+
+ if (compare_len_v3v3(v->co, v2->co, dist)) {
+
+ /* If one vert is marked as keep, make sure it will be the target */
+ if (BMO_elem_flag_test(bm, v2, VERT_KEEP)) {
+ SWAP(BMVert *, v, v2);
+ }
+
+ BMO_elem_flag_enable(bm, v2, VERT_DOUBLE);
+ BMO_elem_flag_enable(bm, v, VERT_TARGET);
+
+ BMO_slot_map_ptr_insert(bm, optarget, targetmapname, v2, v);
+ }
+ }
+ }
+
+ BLI_array_free(verts);
+}
+
+void bmesh_removedoubles_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator weldop;
+
+ BMO_op_init(bm, &weldop, "weldverts");
+ bmesh_finddoubles_common(bm, op, &weldop, "targetmap");
+ BMO_op_exec(bm, &weldop);
+ BMO_op_finish(bm, &weldop);
+}
+
+
+void bmesh_finddoubles_exec(BMesh *bm, BMOperator *op)
+{
+ bmesh_finddoubles_common(bm, op, op, "targetmapout");
+}
+
+void bmesh_automerge_exec(BMesh *bm, BMOperator *op)
+{
+ BMOperator findop, weldop;
+ BMIter viter;
+ BMVert *v;
+
+ /* The "verts" input sent to this op is the set of verts that
+ * can be merged away into any other verts. Mark all other verts
+ * as VERT_KEEP. */
+ BMO_slot_buffer_flag_enable(bm, op, "verts", VERT_IN, BM_VERT);
+ BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) {
+ if (!BMO_elem_flag_test(bm, v, VERT_IN)) {
+ BMO_elem_flag_enable(bm, v, VERT_KEEP);
+ }
+ }
+
+ /* Search for doubles among all vertices, but only merge non-VERT_KEEP
+ * vertices into VERT_KEEP vertices. */
+ BMO_op_initf(bm, &findop, "finddoubles verts=%av keepverts=%fv", VERT_KEEP);
+ BMO_slot_copy(op, &findop, "dist", "dist");
+ BMO_op_exec(bm, &findop);
+
+ /* weld the vertices */
+ BMO_op_init(bm, &weldop, "weldverts");
+ BMO_slot_copy(&findop, &weldop, "targetmapout", "targetmap");
+ BMO_op_exec(bm, &weldop);
+
+ BMO_op_finish(bm, &findop);
+ BMO_op_finish(bm, &weldop);
+}
diff --git a/source/blender/bmesh/operators/bmo_subdivide.c b/source/blender/bmesh/operators/bmo_subdivide.c
new file mode 100644
index 00000000000..310762e0e37
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_subdivide.c
@@ -0,0 +1,1104 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Joseph Eagar.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_rand.h"
+#include "BLI_array.h"
+#include "BLI_noise.h"
+
+#include "BKE_customdata.h"
+
+#include "DNA_object_types.h"
+
+#include "ED_mesh.h"
+
+#include "bmesh.h"
+#include "bmesh_private.h"
+
+#include "bmesh_operators_private.h" /* own include */
+
+#include "bmo_subdivide.h" /* own include */
+
+/* flags for all elements share a common bitfield space */
+#define SUBD_SPLIT 1
+
+#define EDGE_PERCENT 2
+
+/* I don't think new faces are flagged, currently, but
+ * better safe than sorry. */
+#define FACE_CUSTOMFILL 4
+#define ELE_INNER 8
+#define ELE_SPLIT 16
+
+/*
+ * NOTE: beauty has been renamed to flag!
+ */
+
+/* generic subdivision rules:
+ *
+ * - two selected edges in a face should make a link
+ * between them.
+ *
+ * - one edge should do, what? make pretty topology, or just
+ * split the edge only?
+ */
+
+/* connects face with smallest len, which I think should always be correct for
+ * edge subdivision */
+static BMEdge *connect_smallest_face(BMesh *bm, BMVert *v1, BMVert *v2, BMFace **r_nf)
+{
+ BMIter iter, iter2;
+ BMVert *v;
+ BMLoop *nl;
+ BMFace *face, *curf = NULL;
+
+ /* this isn't the best thing in the world. it doesn't handle cases where there's
+ * multiple faces yet. that might require a convexity test to figure out which
+ * face is "best," and who knows what for non-manifold conditions. */
+ for (face = BM_iter_new(&iter, bm, BM_FACES_OF_VERT, v1); face; face = BM_iter_step(&iter)) {
+ for (v = BM_iter_new(&iter2, bm, BM_VERTS_OF_FACE, face); v; v = BM_iter_step(&iter2)) {
+ if (v == v2) {
+ if (!curf || face->len < curf->len) curf = face;
+ }
+ }
+ }
+
+ if (curf) {
+ face = BM_face_split(bm, curf, v1, v2, &nl, NULL);
+
+ if (r_nf) *r_nf = face;
+ return nl ? nl->e : NULL;
+ }
+
+ return NULL;
+}
+/* calculates offset for co, based on fractal, sphere or smooth settings */
+static void alter_co(BMesh *bm, BMVert *v, BMEdge *UNUSED(origed), const subdparams *params, float perc,
+ BMVert *vsta, BMVert *vend)
+{
+ float tvec[3], prev_co[3], fac;
+ float *co = NULL;
+ int i, totlayer = CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY);
+
+ BM_vert_normal_update_all(bm, v);
+
+ co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, params->origkey);
+ copy_v3_v3(prev_co, co);
+
+ if (params->beauty & B_SMOOTH) {
+ /* we calculate an offset vector vec1[], to be added to *co */
+ float len, nor[3], nor1[3], nor2[3], smooth = params->smooth;
+
+ sub_v3_v3v3(nor, vsta->co, vend->co);
+ len = 0.5f * normalize_v3(nor);
+
+ copy_v3_v3(nor1, vsta->no);
+ copy_v3_v3(nor2, vend->no);
+
+ /* cosine angle */
+ fac= dot_v3v3(nor, nor1);
+ mul_v3_v3fl(tvec, nor1, fac);
+
+ /* cosine angle */
+ fac = -dot_v3v3(nor, nor2);
+ madd_v3_v3fl(tvec, nor2, fac);
+
+ /* falloff for multi subdivide */
+ smooth *= sqrtf(fabsf(1.0f - 2.0f * fabsf(0.5f-perc)));
+
+ mul_v3_fl(tvec, smooth * len);
+
+ add_v3_v3(co, tvec);
+ }
+ else if (params->beauty & B_SPHERE) { /* subdivide sphere */
+ normalize_v3(co);
+ mul_v3_fl(co, params->smooth);
+ }
+
+ if (params->beauty & B_FRACTAL) {
+ float len = len_v3v3(vsta->co, vend->co);
+ float vec2[3] = {0.0f, 0.0f, 0.0f}, co2[3];
+
+ fac = params->fractal * len;
+
+ add_v3_v3(vec2, vsta->no);
+ add_v3_v3(vec2, vend->no);
+ mul_v3_fl(vec2, 0.5f);
+
+ add_v3_v3v3(co2, v->co, params->off);
+ tvec[0] = fac * (BLI_gTurbulence(1.0, co2[0], co2[1], co2[2], 15, 0, 1) - 0.5f);
+ tvec[1] = fac * (BLI_gTurbulence(1.0, co2[0], co2[1], co2[2], 15, 0, 1) - 0.5f);
+ tvec[2] = fac * (BLI_gTurbulence(1.0, co2[0], co2[1], co2[2], 15, 0, 1) - 0.5f);
+
+ mul_v3_v3(vec2, tvec);
+
+ /* add displacemen */
+ add_v3_v3v3(co, co, vec2);
+ }
+
+ /* apply the new difference to the rest of the shape keys,
+ * note that this doent take rotations into account, we _could_ support
+ * this by getting the normals and coords for each shape key and
+ * re-calculate the smooth value for each but this is quite involved.
+ * for now its ok to simply apply the difference IMHO - campbell */
+ sub_v3_v3v3(tvec, prev_co, co);
+
+ for (i = 0; i < totlayer; i++) {
+ if (params->origkey != i) {
+ co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, i);
+ sub_v3_v3(co, tvec);
+ }
+ }
+
+}
+
+/* assumes in the edge is the correct interpolated vertices already */
+/* percent defines the interpolation, rad and flag are for special options */
+/* results in new vertex with correct coordinate, vertex normal and weight group info */
+static BMVert *bm_subdivide_edge_addvert(BMesh *bm, BMEdge *edge, BMEdge *oedge,
+ const subdparams *params, float percent,
+ float percent2,
+ BMEdge **out, BMVert *vsta, BMVert *vend)
+{
+ BMVert *ev;
+
+ ev = BM_edge_split(bm, edge->v1, edge, out, percent);
+
+ BMO_elem_flag_enable(bm, ev, ELE_INNER);
+
+ /* offset for smooth or sphere or fractal */
+ alter_co(bm, ev, oedge, params, percent2, vsta, vend);
+
+#if 0 //BMESH_TODO
+ /* clip if needed by mirror modifier */
+ if (edge->v1->f2) {
+ if (edge->v1->f2 & edge->v2->f2 & 1) {
+ co[0] = 0.0f;
+ }
+ if (edge->v1->f2 & edge->v2->f2 & 2) {
+ co[1] = 0.0f;
+ }
+ if (edge->v1->f2 & edge->v2->f2 & 4) {
+ co[2] = 0.0f;
+ }
+ }
+#endif
+
+ return ev;
+}
+
+static BMVert *subdivideedgenum(BMesh *bm, BMEdge *edge, BMEdge *oedge,
+ int curpoint, int totpoint, const subdparams *params,
+ BMEdge **newe, BMVert *vsta, BMVert *vend)
+{
+ BMVert *ev;
+ float percent, percent2 = 0.0f;
+
+ if (BMO_elem_flag_test(bm, edge, EDGE_PERCENT) && totpoint == 1)
+ percent = BMO_slot_map_float_get(bm, params->op, "edgepercents", edge);
+ else {
+ percent = 1.0f / (float)(totpoint + 1-curpoint);
+ percent2 = (float)(curpoint + 1) / (float)(totpoint + 1);
+
+ }
+
+ ev = bm_subdivide_edge_addvert(bm, edge, oedge, params, percent,
+ percent2, newe, vsta, vend);
+ return ev;
+}
+
+static void bm_subdivide_multicut(BMesh *bm, BMEdge *edge, const subdparams *params,
+ BMVert *vsta, BMVert *vend)
+{
+ BMEdge *eed = edge, *newe, temp = *edge;
+ BMVert *v, ov1 = *edge->v1, ov2 = *edge->v2, *v1 = edge->v1, *v2 = edge->v2;
+ int i, numcuts = params->numcuts;
+
+ temp.v1 = &ov1;
+ temp.v2 = &ov2;
+
+ for (i = 0; i < numcuts; i++) {
+ v = subdivideedgenum(bm, eed, &temp, i, params->numcuts, params, &newe, vsta, vend);
+
+ BMO_elem_flag_enable(bm, v, SUBD_SPLIT);
+ BMO_elem_flag_enable(bm, eed, SUBD_SPLIT);
+ BMO_elem_flag_enable(bm, newe, SUBD_SPLIT);
+
+ BMO_elem_flag_enable(bm, v, ELE_SPLIT);
+ BMO_elem_flag_enable(bm, eed, ELE_SPLIT);
+ BMO_elem_flag_enable(bm, newe, SUBD_SPLIT);
+
+ BM_CHECK_ELEMENT(bm, v);
+ if (v->e) BM_CHECK_ELEMENT(bm, v->e);
+ if (v->e && v->e->l) BM_CHECK_ELEMENT(bm, v->e->l->f);
+ }
+
+ alter_co(bm, v1, &temp, params, 0, &ov1, &ov2);
+ alter_co(bm, v2, &temp, params, 1.0, &ov1, &ov2);
+}
+
+/* note: the patterns are rotated as necassary to
+ * match the input geometry. they're based on the
+ * pre-split state of the face */
+
+/*
+ * v3---------v2
+ * | |
+ * | |
+ * | |
+ * | |
+ * v4---v0---v1
+ */
+static void quad_1edge_split(BMesh *bm, BMFace *UNUSED(face),
+ BMVert **verts, const subdparams *params)
+{
+ BMFace *nf;
+ int i, add, numcuts = params->numcuts;
+
+ /* if it's odd, the middle face is a quad, otherwise it's a triangl */
+ if ((numcuts % 2) == 0) {
+ add = 2;
+ for (i = 0; i < numcuts; i++) {
+ if (i == numcuts / 2) {
+ add -= 1;
+ }
+ connect_smallest_face(bm, verts[i], verts[numcuts + add], &nf);
+ }
+ }
+ else {
+ add = 2;
+ for (i = 0; i < numcuts; i++) {
+ connect_smallest_face(bm, verts[i], verts[numcuts + add], &nf);
+ if (i == numcuts/2) {
+ add -= 1;
+ connect_smallest_face(bm, verts[i], verts[numcuts + add], &nf);
+ }
+ }
+
+ }
+}
+
+static SubDPattern quad_1edge = {
+ {1, 0, 0, 0},
+ quad_1edge_split,
+ 4,
+};
+
+
+/*
+ * v6--------v5
+ * | |
+ * | |v4s
+ * | |v3s
+ * | s s |
+ * v7-v0--v1-v2
+ */
+static void quad_2edge_split_path(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
+ const subdparams *params)
+{
+ BMFace *nf;
+ int i, numcuts = params->numcuts;
+
+ for (i = 0; i < numcuts; i++) {
+ connect_smallest_face(bm, verts[i], verts[numcuts + (numcuts - i)], &nf);
+ }
+ connect_smallest_face(bm, verts[numcuts * 2 + 3], verts[numcuts * 2 + 1], &nf);
+}
+
+static SubDPattern quad_2edge_path = {
+ {1, 1, 0, 0},
+ quad_2edge_split_path,
+ 4,
+};
+
+/*
+ * v6--------v5
+ * | |
+ * | |v4s
+ * | |v3s
+ * | s s |
+ * v7-v0--v1-v2
+ */
+static void quad_2edge_split_innervert(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
+ const subdparams *params)
+{
+ BMFace *nf;
+ BMVert *v, *lastv;
+ BMEdge *e, *ne, olde;
+ int i, numcuts = params->numcuts;
+
+ lastv = verts[numcuts];
+
+ for (i = numcuts - 1; i >= 0; i--) {
+ e = connect_smallest_face(bm, verts[i], verts[numcuts + (numcuts - i)], &nf);
+
+ olde = *e;
+ v = bm_subdivide_edge_addvert(bm, e, &olde, params, 0.5f, 0.5f, &ne, e->v1, e->v2);
+
+ if (i != numcuts - 1) {
+ connect_smallest_face(bm, lastv, v, &nf);
+ }
+
+ lastv = v;
+ }
+
+ connect_smallest_face(bm, lastv, verts[numcuts * 2 + 2], &nf);
+}
+
+static SubDPattern quad_2edge_innervert = {
+ {1, 1, 0, 0},
+ quad_2edge_split_innervert,
+ 4,
+};
+
+/*
+ * v6--------v5
+ * | |
+ * | |v4s
+ * | |v3s
+ * | s s |
+ * v7-v0--v1-v2
+ *
+ */
+static void quad_2edge_split_fan(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
+ const subdparams *params)
+{
+ BMFace *nf;
+ /* BMVert *v; */ /* UNUSED */
+ /* BMVert *lastv = verts[2]; */ /* UNUSED */
+ /* BMEdge *e, *ne; */ /* UNUSED */
+ int i, numcuts = params->numcuts;
+
+ for (i = 0; i < numcuts; i++) {
+ connect_smallest_face(bm, verts[i], verts[numcuts * 2 + 2], &nf);
+ connect_smallest_face(bm, verts[numcuts + (numcuts - i)], verts[numcuts * 2 + 2], &nf);
+ }
+}
+
+static SubDPattern quad_2edge_fan = {
+ {1, 1, 0, 0},
+ quad_2edge_split_fan,
+ 4,
+};
+
+/*
+ * s s
+ * v8--v7--v6-v5
+ * | |
+ * | v4 s
+ * | |
+ * | v3 s
+ * | s s |
+ * v9-v0--v1-v2
+ */
+static void quad_3edge_split(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
+ const subdparams *params)
+{
+ BMFace *nf;
+ int i, add = 0, numcuts = params->numcuts;
+
+ for (i = 0; i < numcuts; i++) {
+ if (i == numcuts / 2) {
+ if (numcuts % 2 != 0) {
+ connect_smallest_face(bm, verts[numcuts - i - 1 + add], verts[i + numcuts + 1], &nf);
+ }
+ add = numcuts * 2 + 2;
+ }
+ connect_smallest_face(bm, verts[numcuts - i - 1 + add], verts[i + numcuts + 1], &nf);
+ }
+
+ for (i = 0; i < numcuts / 2 + 1; i++) {
+ connect_smallest_face(bm, verts[i], verts[(numcuts - i) + numcuts * 2 + 1], &nf);
+ }
+}
+
+static SubDPattern quad_3edge = {
+ {1, 1, 1, 0},
+ quad_3edge_split,
+ 4,
+};
+
+/*
+ * v8--v7-v6--v5
+ * | s |
+ * |v9 s s|v4
+ * first line | | last line
+ * |v10s s s|v3
+ * v11-v0--v1-v2
+ *
+ * it goes from bottom up
+ */
+static void quad_4edge_subdivide(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
+ const subdparams *params)
+{
+ BMFace *nf;
+ BMVert *v, *v1, *v2;
+ BMEdge *e, *ne, temp;
+ BMVert **lines;
+ int numcuts = params->numcuts;
+ int i, j, a, b, s = numcuts + 2 /* , totv = numcuts * 4 + 4 */;
+
+ lines = MEM_callocN(sizeof(BMVert *)*(numcuts + 2)*(numcuts + 2), "q_4edge_split");
+ /* build a 2-dimensional array of verts,
+ * containing every vert (and all new ones)
+ * in the face */
+
+ /* first line */
+ for (i = 0; i < numcuts + 2; i++) {
+ lines[i] = verts[numcuts * 3 + 2 + (numcuts - i + 1)];
+ }
+
+ /* last line */
+ for (i = 0; i < numcuts + 2; i++) {
+ lines[(s - 1) * s + i] = verts[numcuts + i];
+ }
+
+ /* first and last members of middle lines */
+ for (i = 0; i < numcuts; i++) {
+ a = i;
+ b = numcuts + 1 + numcuts + 1 + (numcuts - i - 1);
+
+ e = connect_smallest_face(bm, verts[a], verts[b], &nf);
+ if (!e)
+ continue;
+
+ BMO_elem_flag_enable(bm, e, ELE_INNER);
+ BMO_elem_flag_enable(bm, nf, ELE_INNER);
+
+
+ v1 = lines[(i + 1)*s] = verts[a];
+ v2 = lines[(i + 1)*s + s - 1] = verts[b];
+
+ temp = *e;
+ for (a = 0; a < numcuts; a++) {
+ v = subdivideedgenum(bm, e, &temp, a, numcuts, params, &ne,
+ v1, v2);
+ if (!v)
+ bmesh_error();
+
+ BMO_elem_flag_enable(bm, ne, ELE_INNER);
+ lines[(i + 1) * s + a + 1] = v;
+ }
+ }
+
+ for (i = 1; i < numcuts + 2; i++) {
+ for (j = 1; j < numcuts + 1; j++) {
+ a = i * s + j;
+ b = (i - 1) * s + j;
+ e = connect_smallest_face(bm, lines[a], lines[b], &nf);
+ if (!e)
+ continue;
+
+ BMO_elem_flag_enable(bm, e, ELE_INNER);
+ BMO_elem_flag_enable(bm, nf, ELE_INNER);
+ }
+ }
+
+ MEM_freeN(lines);
+}
+
+/*
+ * v3
+ * / \
+ * / \
+ * / \
+ * / \
+ * / \
+ * v4--v0--v1--v2
+ * s s
+ */
+static void tri_1edge_split(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
+ const subdparams *params)
+{
+ BMFace *nf;
+ int i, numcuts = params->numcuts;
+
+ for (i = 0; i < numcuts; i++) {
+ connect_smallest_face(bm, verts[i], verts[numcuts + 1], &nf);
+ }
+}
+
+static SubDPattern tri_1edge = {
+ {1, 0, 0},
+ tri_1edge_split,
+ 3,
+};
+
+/* v5
+ * / \
+ * s v6/---\ v4 s
+ * / \ / \
+ * sv7/---v---\ v3 s
+ * / \/ \/ \
+ * v8--v0--v1--v2
+ * s s
+ */
+static void tri_3edge_subdivide(BMesh *bm, BMFace *UNUSED(face), BMVert **verts,
+ const subdparams *params)
+{
+ BMFace *nf;
+ BMEdge *e, *ne, temp;
+ BMVert ***lines, *v, ov1, ov2;
+ void *stackarr[1];
+ int i, j, a, b, numcuts = params->numcuts;
+
+ /* number of verts in each lin */
+ lines = MEM_callocN(sizeof(void *)*(numcuts + 2), "triangle vert table");
+
+ lines[0] = (BMVert **) stackarr;
+ lines[0][0] = verts[numcuts * 2 + 1];
+
+ lines[numcuts + 1] = MEM_callocN(sizeof(void *) * (numcuts + 2), "triangle vert table 2");
+ for (i = 0; i < numcuts; i++) {
+ lines[numcuts + 1][i + 1] = verts[i];
+ }
+ lines[numcuts + 1][0] = verts[numcuts * 3 + 2];
+ lines[numcuts + 1][numcuts + 1] = verts[numcuts];
+
+ for (i = 0; i < numcuts; i++) {
+ lines[i + 1] = MEM_callocN(sizeof(void *)*(2 + i), "triangle vert table row");
+ a = numcuts * 2 + 2 + i;
+ b = numcuts + numcuts - i;
+ e = connect_smallest_face(bm, verts[a], verts[b], &nf);
+ if (!e) goto cleanup;
+
+ BMO_elem_flag_enable(bm, e, ELE_INNER);
+ BMO_elem_flag_enable(bm, nf, ELE_INNER);
+
+ lines[i + 1][0] = verts[a];
+ lines[i + 1][i + 1] = verts[b];
+
+ temp = *e;
+ ov1 = *verts[a];
+ ov2 = *verts[b];
+ temp.v1 = &ov1;
+ temp.v2 = &ov2;
+ for (j = 0; j < i; j++) {
+ v = subdivideedgenum(bm, e, &temp, j, i, params, &ne,
+ verts[a], verts[b]);
+ lines[i + 1][j + 1] = v;
+
+ BMO_elem_flag_enable(bm, ne, ELE_INNER);
+ }
+ }
+
+ /*
+ * v5
+ * / \
+ * s v6/---\ v4 s
+ * / \ / \
+ * sv7/---v---\ v3 s
+ * / \/ \/ \
+ * v8--v0--v1--v2
+ * s s
+ */
+ for (i = 1; i < numcuts + 1; i++) {
+ for (j = 0; j < i; j++) {
+ e = connect_smallest_face(bm, lines[i][j], lines[i + 1][j + 1], &nf);
+
+ BMO_elem_flag_enable(bm, e, ELE_INNER);
+ BMO_elem_flag_enable(bm, nf, ELE_INNER);
+
+ e = connect_smallest_face(bm, lines[i][j + 1], lines[i + 1][j + 1], &nf);
+
+ BMO_elem_flag_enable(bm, e, ELE_INNER);
+ BMO_elem_flag_enable(bm, nf, ELE_INNER);
+ }
+ }
+
+cleanup:
+ for (i = 1; i < numcuts + 2; i++) {
+ if (lines[i]) MEM_freeN(lines[i]);
+ }
+
+ MEM_freeN(lines);
+}
+
+static SubDPattern tri_3edge = {
+ {1, 1, 1},
+ tri_3edge_subdivide,
+ 3,
+};
+
+
+static SubDPattern quad_4edge = {
+ {1, 1, 1, 1},
+ quad_4edge_subdivide,
+ 4,
+};
+
+static SubDPattern *patterns[] = {
+ NULL, //quad single edge pattern is inserted here
+ NULL, //quad corner vert pattern is inserted here
+ NULL, //tri single edge pattern is inserted here
+ NULL,
+ &quad_3edge,
+ NULL,
+};
+
+#define PLEN (sizeof(patterns) / sizeof(void *))
+
+typedef struct subd_facedata {
+ BMVert *start; SubDPattern *pat;
+ int totedgesel; //only used if pat was NULL, e.g. no pattern was found
+ BMFace *face;
+} subd_facedata;
+
+void esubdivide_exec(BMesh *bmesh, BMOperator *op)
+{
+ BMOpSlot *einput;
+ SubDPattern *pat;
+ subdparams params;
+ subd_facedata *facedata = NULL;
+ BMIter viter, fiter, liter;
+ BMVert *v, **verts = NULL;
+ BMEdge *edge, **edges = NULL;
+ BMLoop *nl, *l, **splits = NULL, **loops = NULL;
+ BMFace *face;
+ BLI_array_declare(splits);
+ BLI_array_declare(loops);
+ BLI_array_declare(facedata);
+ BLI_array_declare(edges);
+ BLI_array_declare(verts);
+ float smooth, fractal;
+ int beauty, cornertype, singleedge, gridfill;
+ int skey, seed, i, j, matched, a, b, numcuts, totesel;
+
+ BMO_slot_buffer_flag_enable(bmesh, op, "edges", SUBD_SPLIT, BM_EDGE);
+
+ numcuts = BMO_slot_int_get(op, "numcuts");
+ seed = BMO_slot_int_get(op, "seed");
+ smooth = BMO_slot_float_get(op, "smooth");
+ fractal = BMO_slot_float_get(op, "fractal");
+ beauty = BMO_slot_int_get(op, "beauty");
+ cornertype = BMO_slot_int_get(op, "quadcornertype");
+ singleedge = BMO_slot_int_get(op, "singleedge");
+ gridfill = BMO_slot_int_get(op, "gridfill");
+
+ BLI_srandom(seed);
+
+ patterns[1] = NULL;
+ //straight cut is patterns[1] == NULL
+ switch (cornertype) {
+ case SUBD_PATH:
+ patterns[1] = &quad_2edge_path;
+ break;
+ case SUBD_INNERVERT:
+ patterns[1] = &quad_2edge_innervert;
+ break;
+ case SUBD_FAN:
+ patterns[1] = &quad_2edge_fan;
+ break;
+ }
+
+ if (singleedge) {
+ patterns[0] = &quad_1edge;
+ patterns[2] = &tri_1edge;
+ }
+ else {
+ patterns[0] = NULL;
+ patterns[2] = NULL;
+ }
+
+ if (gridfill) {
+ patterns[3] = &quad_4edge;
+ patterns[5] = &tri_3edge;
+ }
+ else {
+ patterns[3] = NULL;
+ patterns[5] = NULL;
+ }
+
+ /* add a temporary shapekey layer to store displacements on current geometr */
+ BM_data_layer_add(bmesh, &bmesh->vdata, CD_SHAPEKEY);
+ skey = CustomData_number_of_layers(&bmesh->vdata, CD_SHAPEKEY) - 1;
+
+ BM_ITER(v, &viter, bmesh, BM_VERTS_OF_MESH, NULL) {
+ float *co = CustomData_bmesh_get_n(&bmesh->vdata, v->head.data, CD_SHAPEKEY, skey);
+ copy_v3_v3(co, v->co);
+ }
+
+ /* first go through and tag edge */
+ BMO_slot_from_flag(bmesh, op, "edges",
+ SUBD_SPLIT, BM_EDGE);
+
+ params.numcuts = numcuts;
+ params.op = op;
+ params.smooth = smooth;
+ params.seed = seed;
+ params.fractal = fractal;
+ params.beauty = beauty;
+ params.origkey = skey;
+ params.off[0] = (float)BLI_drand() * 200.0f;
+ params.off[1] = (float)BLI_drand() * 200.0f;
+ params.off[2] = (float)BLI_drand() * 200.0f;
+
+ BMO_slot_map_to_flag(bmesh, op, "custompatterns",
+ FACE_CUSTOMFILL);
+
+ BMO_slot_map_to_flag(bmesh, op, "edgepercents",
+ EDGE_PERCENT);
+
+ for (face = BM_iter_new(&fiter, bmesh, BM_FACES_OF_MESH, NULL);
+ face;
+ face = BM_iter_step(&fiter))
+ {
+ BMEdge *e1 = NULL, *e2 = NULL;
+ float vec1[3], vec2[3];
+
+ /* figure out which pattern to us */
+
+ BLI_array_empty(edges);
+ BLI_array_empty(verts);
+ matched = 0;
+
+ i = 0;
+ totesel = 0;
+ for (nl = BM_iter_new(&liter, bmesh, BM_LOOPS_OF_FACE, face); nl; nl = BM_iter_step(&liter)) {
+ BLI_array_growone(edges);
+ BLI_array_growone(verts);
+ edges[i] = nl->e;
+ verts[i] = nl->v;
+
+ if (BMO_elem_flag_test(bmesh, edges[i], SUBD_SPLIT)) {
+ if (!e1) e1 = edges[i];
+ else e2 = edges[i];
+
+ totesel++;
+ }
+
+ i++;
+ }
+
+ /* make sure the two edges have a valid angle to each othe */
+ if (totesel == 2 && BM_edge_share_vert(e1, e2)) {
+ float angle;
+
+ sub_v3_v3v3(vec1, e1->v2->co, e1->v1->co);
+ sub_v3_v3v3(vec2, e2->v2->co, e2->v1->co);
+ normalize_v3(vec1);
+ normalize_v3(vec2);
+
+ angle = dot_v3v3(vec1, vec2);
+ angle = fabsf(angle);
+ if (fabsf(angle - 1.0f) < 0.01f) {
+ totesel = 0;
+ }
+ }
+
+ if (BMO_elem_flag_test(bmesh, face, FACE_CUSTOMFILL)) {
+ pat = BMO_slot_map_data_get(bmesh, op,
+ "custompatterns", face);
+ for (i = 0; i < pat->len; i++) {
+ matched = 1;
+ for (j = 0; j < pat->len; j++) {
+ a = (j + i) % pat->len;
+ if ((!!BMO_elem_flag_test(bmesh, edges[a], SUBD_SPLIT)) != (!!pat->seledges[j])) {
+ matched = 0;
+ break;
+ }
+ }
+ if (matched) {
+ BLI_array_growone(facedata);
+ b = BLI_array_count(facedata) - 1;
+ facedata[b].pat = pat;
+ facedata[b].start = verts[i];
+ facedata[b].face = face;
+ facedata[b].totedgesel = totesel;
+ BMO_elem_flag_enable(bmesh, face, SUBD_SPLIT);
+ break;
+ }
+ }
+
+ /* obvously don't test for other patterns matchin */
+ continue;
+ }
+
+ for (i = 0; i < PLEN; i++) {
+ pat = patterns[i];
+ if (!pat) continue;
+
+ if (pat->len == face->len) {
+ for (a = 0; a < pat->len; a++) {
+ matched = 1;
+ for (b = 0; b < pat->len; b++) {
+ j = (b + a) % pat->len;
+ if ((!!BMO_elem_flag_test(bmesh, edges[j], SUBD_SPLIT)) != (!!pat->seledges[b])) {
+ matched = 0;
+ break;
+ }
+ }
+ if (matched) {
+ break;
+ }
+ }
+ if (matched) {
+ BLI_array_growone(facedata);
+ j = BLI_array_count(facedata) - 1;
+
+ BMO_elem_flag_enable(bmesh, face, SUBD_SPLIT);
+
+ facedata[j].pat = pat;
+ facedata[j].start = verts[a];
+ facedata[j].face = face;
+ facedata[j].totedgesel = totesel;
+ break;
+ }
+ }
+
+ }
+
+ if (!matched && totesel) {
+ BLI_array_growone(facedata);
+ j = BLI_array_count(facedata) - 1;
+
+ BMO_elem_flag_enable(bmesh, face, SUBD_SPLIT);
+ facedata[j].totedgesel = totesel;
+ facedata[j].face = face;
+ }
+ }
+
+ einput = BMO_slot_get(op, "edges");
+
+ /* go through and split edge */
+ for (i = 0; i < einput->len; i++) {
+ edge = ((BMEdge **)einput->data.p)[i];
+ bm_subdivide_multicut(bmesh, edge, &params, edge->v1, edge->v2);
+ }
+
+ i = 0;
+ for (i = 0; i < BLI_array_count(facedata); i++) {
+ face = facedata[i].face;
+
+ /* figure out which pattern to us */
+ BLI_array_empty(verts);
+
+ pat = facedata[i].pat;
+
+ if (!pat && facedata[i].totedgesel == 2) {
+ int vlen;
+
+ /* ok, no pattern. we still may be able to do something */
+ BLI_array_empty(loops);
+ BLI_array_empty(splits);
+
+ /* for case of two edges, connecting them shouldn't be too har */
+ BM_ITER(l, &liter, bmesh, BM_LOOPS_OF_FACE, face) {
+ BLI_array_growone(loops);
+ loops[BLI_array_count(loops) - 1] = l;
+ }
+
+ vlen = BLI_array_count(loops);
+
+ /* find the boundary of one of the split edge */
+ for (a = 1; a < vlen; a++) {
+ if (!BMO_elem_flag_test(bmesh, loops[a - 1]->v, ELE_INNER) &&
+ BMO_elem_flag_test(bmesh, loops[a]->v, ELE_INNER))
+ {
+ break;
+ }
+ }
+
+ if (BMO_elem_flag_test(bmesh, loops[(a + numcuts + 1) % vlen]->v, ELE_INNER)) {
+ b = (a + numcuts + 1) % vlen;
+ }
+ else {
+ /* find the boundary of the other edge. */
+ for (j = 0; j < vlen; j++) {
+ b = (j + a + numcuts + 1) % vlen;
+ if (!BMO_elem_flag_test(bmesh, loops[b == 0 ? vlen - 1 : b - 1]->v, ELE_INNER) &&
+ BMO_elem_flag_test(bmesh, loops[b]->v, ELE_INNER))
+ {
+ break;
+ }
+ }
+ }
+
+ b += numcuts - 1;
+
+ for (j = 0; j < numcuts; j++) {
+ BLI_array_growone(splits);
+ splits[BLI_array_count(splits) - 1] = loops[a];
+
+ BLI_array_growone(splits);
+ splits[BLI_array_count(splits) - 1] = loops[b];
+
+ b = (b - 1) % vlen;
+ a = (a + 1) % vlen;
+ }
+
+ //BM_face_legal_splits(bmesh, face, splits, BLI_array_count(splits)/2);
+
+ for (j = 0; j < BLI_array_count(splits) / 2; j++) {
+ if (splits[j * 2]) {
+ /* BMFace *nf = */ /* UNUSED */
+ BM_face_split(bmesh, face, splits[j * 2]->v, splits[j * 2 + 1]->v, &nl, NULL);
+ }
+ }
+
+ continue;
+ }
+ else if (!pat) {
+ continue;
+ }
+
+ j = a = 0;
+ for (nl = BM_iter_new(&liter, bmesh, BM_LOOPS_OF_FACE, face);
+ nl;
+ nl = BM_iter_step(&liter))
+ {
+ if (nl->v == facedata[i].start) {
+ a = j + 1;
+ break;
+ }
+ j++;
+ }
+
+ for (j = 0; j < face->len; j++) {
+ BLI_array_growone(verts);
+ }
+
+ j = 0;
+ for (nl = BM_iter_new(&liter, bmesh, BM_LOOPS_OF_FACE, face); nl; nl = BM_iter_step(&liter)) {
+ b = (j - a + face->len) % face->len;
+ verts[b] = nl->v;
+ j += 1;
+ }
+
+ BM_CHECK_ELEMENT(bmesh, face);
+ pat->connectexec(bmesh, face, verts, &params);
+ }
+
+ /* copy original-geometry displacements to current coordinate */
+ BM_ITER(v, &viter, bmesh, BM_VERTS_OF_MESH, NULL) {
+ float *co = CustomData_bmesh_get_n(&bmesh->vdata, v->head.data, CD_SHAPEKEY, skey);
+ copy_v3_v3(v->co, co);
+ }
+
+ BM_data_layer_free_n(bmesh, &bmesh->vdata, CD_SHAPEKEY, skey);
+
+ if (facedata) BLI_array_free(facedata);
+ if (edges) BLI_array_free(edges);
+ if (verts) BLI_array_free(verts);
+ BLI_array_free(splits);
+ BLI_array_free(loops);
+
+ BMO_slot_from_flag(bmesh, op, "outinner",
+ ELE_INNER, BM_ALL);
+ BMO_slot_from_flag(bmesh, op, "outsplit",
+ ELE_SPLIT, BM_ALL);
+
+ BMO_slot_from_flag(bmesh, op, "geomout",
+ ELE_INNER|ELE_SPLIT|SUBD_SPLIT, BM_ALL);
+}
+
+/* editmesh-emulating functio */
+void BM_mesh_esubdivideflag(Object *UNUSED(obedit), BMesh *bm, int flag, float smooth,
+ float fractal, int beauty, int numcuts,
+ int seltype, int cornertype, int singleedge,
+ int gridfill, int seed)
+{
+ BMOperator op;
+
+ BMO_op_initf(bm, &op, "esubd edges=%he smooth=%f fractal=%f "
+ "beauty=%d numcuts=%d quadcornertype=%d singleedge=%d "
+ "gridfill=%d seed=%d",
+ flag, smooth, fractal, beauty, numcuts,
+ cornertype, singleedge, gridfill, seed);
+
+ BMO_op_exec(bm, &op);
+
+ if (seltype == SUBDIV_SELECT_INNER) {
+ BMOIter iter;
+ BMHeader *ele;
+ // int i;
+
+ ele = BMO_iter_new(&iter, bm, &op, "outinner", BM_EDGE|BM_VERT);
+ for ( ; ele; ele = BMO_iter_step(&iter)) {
+ BM_elem_select_set(bm, ele, TRUE);
+ }
+ }
+ else if (seltype == SUBDIV_SELECT_LOOPCUT) {
+ BMOIter iter;
+ BMHeader *ele;
+ // int i;
+
+ /* deselect input */
+ BM_mesh_elem_flag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT);
+
+ ele = BMO_iter_new(&iter, bm, &op, "outinner", BM_EDGE|BM_VERT);
+ for ( ; ele; ele = BMO_iter_step(&iter)) {
+ BM_elem_select_set(bm, ele, TRUE);
+
+ if (ele->htype == BM_VERT) {
+ BMEdge *e;
+ BMIter eiter;
+
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, ele) {
+ if (!BM_elem_flag_test(e, BM_ELEM_SELECT) &&
+ BM_elem_flag_test(e->v1, BM_ELEM_SELECT) &&
+ BM_elem_flag_test(e->v2, BM_ELEM_SELECT))
+ {
+ BM_elem_select_set(bm, e, TRUE);
+ bm->totedgesel += 1;
+ }
+ else if (BM_elem_flag_test(e, BM_ELEM_SELECT) &&
+ (!BM_elem_flag_test(e->v1, BM_ELEM_SELECT) ||
+ !BM_elem_flag_test(e->v2, BM_ELEM_SELECT)))
+ {
+ BM_elem_select_set(bm, e, FALSE);
+ bm->totedgesel -= 1;
+ }
+ }
+ }
+ }
+ }
+
+ BMO_op_finish(bm, &op);
+}
+
+void esplit_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMEdge *e;
+ subdparams params;
+ int skey;
+
+ params.numcuts = BMO_slot_get(op, "numcuts")->data.i;
+ params.op = op;
+
+ BM_data_layer_add(bm, &bm->vdata, CD_SHAPEKEY);
+ skey = CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY) - 1;
+
+ params.origkey = skey;
+
+ /* go through and split edge */
+ BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
+ bm_subdivide_multicut(bm, e, &params, e->v1, e->v2);
+ }
+
+ BMO_slot_from_flag(bm, op, "outsplit", ELE_SPLIT, BM_ALL);
+
+ BM_data_layer_free_n(bm, &bm->vdata, CD_SHAPEKEY, skey);
+}
diff --git a/source/blender/bmesh/operators/bmo_subdivide.h b/source/blender/bmesh/operators/bmo_subdivide.h
new file mode 100644
index 00000000000..dd5198bdc30
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_subdivide.h
@@ -0,0 +1,66 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Joseph Eagar.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BMO_SUBDIVIDE_H__
+#define __BMO_SUBDIVIDE_H__
+
+/** \file blender/bmesh/operators/bmo_subdivide.h
+ * \ingroup bmesh
+ */
+
+typedef struct subdparams {
+ int numcuts;
+ float smooth;
+ float fractal;
+ int beauty;
+ int seed;
+ int origkey; /* shapekey holding displaced vertex coordinates for current geometry */
+ BMOperator *op;
+ float off[3];
+} subdparams;
+
+typedef void (*subd_pattern_fill_fp)(BMesh *bm, BMFace *face, BMVert **verts,
+ const subdparams *params);
+
+/*
+ * note: this is a pattern-based edge subdivider.
+ * it tries to match a pattern to edge selections on faces,
+ * then executes functions to cut them.
+ */
+typedef struct SubDPattern {
+ int seledges[20]; /* selected edges mask, for splitting */
+
+ /* verts starts at the first new vert cut, not the first vert in the face */
+ subd_pattern_fill_fp connectexec;
+ int len; /* total number of verts, before any subdivision */
+} SubDPattern;
+
+/* generic subdivision rules:
+ *
+ * - two selected edges in a face should make a link
+ * between them.
+ *
+ * - one edge should do, what? make pretty topology, or just
+ * split the edge only?
+ */
+
+#endif /* __BMO_SUBDIVIDE_H__ */
diff --git a/source/blender/bmesh/operators/bmo_triangulate.c b/source/blender/bmesh/operators/bmo_triangulate.c
new file mode 100644
index 00000000000..90efe0b6e44
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_triangulate.c
@@ -0,0 +1,219 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Joseph Eagar.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_scanfill.h"
+#include "BLI_math.h"
+#include "BLI_array.h"
+#include "BLI_editVert.h"
+#include "BLI_smallhash.h"
+
+#include "bmesh.h"
+#include "bmesh_private.h"
+
+#include "bmesh_operators_private.h" /* own include */
+
+#define EDGE_NEW 1
+#define FACE_NEW 1
+
+#define ELE_NEW 1
+#define FACE_MARK 2
+#define EDGE_MARK 4
+
+void triangulate_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMFace *face, **newfaces = NULL;
+ BLI_array_declare(newfaces);
+ float (*projectverts)[3] = NULL;
+ BLI_array_declare(projectverts);
+ int i, lastlen = 0 /* , count = 0 */;
+
+ face = BMO_iter_new(&siter, bm, op, "faces", BM_FACE);
+ for ( ; face; face = BMO_iter_step(&siter)) {
+ if (lastlen < face->len) {
+ BLI_array_empty(projectverts);
+ BLI_array_empty(newfaces);
+ for (lastlen = 0; lastlen < face->len; lastlen++) {
+ BLI_array_growone(projectverts);
+ BLI_array_growone(projectverts);
+ BLI_array_growone(projectverts);
+ BLI_array_growone(newfaces);
+ }
+ }
+
+ BM_face_triangulate(bm, face, projectverts, EDGE_NEW, FACE_NEW, newfaces);
+
+ BMO_slot_map_ptr_insert(bm, op, "facemap", face, face);
+ for (i = 0; newfaces[i]; i++) {
+ BMO_slot_map_ptr_insert(bm, op, "facemap",
+ newfaces[i], face);
+
+ }
+ }
+
+ BMO_slot_from_flag(bm, op, "edgeout", EDGE_NEW, BM_EDGE);
+ BMO_slot_from_flag(bm, op, "faceout", FACE_NEW, BM_FACE);
+
+ BLI_array_free(projectverts);
+ BLI_array_free(newfaces);
+}
+
+void bmesh_beautify_fill_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMIter iter;
+ BMFace *f;
+ BMEdge *e;
+ int stop = 0;
+
+ BMO_slot_buffer_flag_enable(bm, op, "constrain_edges", EDGE_MARK, BM_EDGE);
+
+ BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) {
+ if (f->len == 3)
+ BMO_elem_flag_enable(bm, f, FACE_MARK);
+ }
+
+ while (!stop) {
+ stop = 1;
+
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ BMVert *v1, *v2, *v3, *v4;
+
+ if (BM_edge_face_count(e) != 2 || BMO_elem_flag_test(bm, e, EDGE_MARK)) {
+ continue;
+ }
+
+ if (!BMO_elem_flag_test(bm, e->l->f, FACE_MARK) ||
+ !BMO_elem_flag_test(bm, e->l->radial_next->f, FACE_MARK))
+ {
+ continue;
+ }
+
+ v1 = e->l->prev->v;
+ v2 = e->l->v;
+ v3 = e->l->radial_next->prev->v;
+ v4 = e->l->next->v;
+
+ if (is_quad_convex_v3(v1->co, v2->co, v3->co, v4->co)) {
+ float len1, len2, len3, len4, len5, len6, opp1, opp2, fac1, fac2;
+ /* testing rule:
+ * the area divided by the total edge lengths
+ */
+ len1 = len_v3v3(v1->co, v2->co);
+ len2 = len_v3v3(v2->co, v3->co);
+ len3 = len_v3v3(v3->co, v4->co);
+ len4 = len_v3v3(v4->co, v1->co);
+ len5 = len_v3v3(v1->co, v3->co);
+ len6 = len_v3v3(v2->co, v4->co);
+
+ opp1 = area_tri_v3(v1->co, v2->co, v3->co);
+ opp2 = area_tri_v3(v1->co, v3->co, v4->co);
+
+ fac1 = opp1 / (len1 + len2 + len5) + opp2 / (len3 + len4 + len5);
+
+ opp1 = area_tri_v3(v2->co, v3->co, v4->co);
+ opp2 = area_tri_v3(v2->co, v4->co, v1->co);
+
+ fac2 = opp1 / (len2 + len3 + len6) + opp2 / (len4 + len1 + len6);
+
+ if (fac1 > fac2) {
+ e = BM_edge_rotate(bm, e, 0);
+ if (e) {
+ BMO_elem_flag_enable(bm, e, ELE_NEW);
+
+ BMO_elem_flag_enable(bm, e->l->f, FACE_MARK|ELE_NEW);
+ BMO_elem_flag_enable(bm, e->l->radial_next->f, FACE_MARK|ELE_NEW);
+ stop = 0;
+ }
+ }
+ }
+ }
+ }
+
+ BMO_slot_from_flag(bm, op, "geomout", ELE_NEW, BM_EDGE|BM_FACE);
+}
+
+void bmesh_triangle_fill_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMEdge *e;
+ BMOperator bmop;
+ EditEdge *eed;
+ EditVert *eve, *v1, *v2;
+ EditFace *efa;
+ SmallHash hash;
+
+ BLI_smallhash_init(&hash);
+
+ BLI_begin_edgefill();
+
+ BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
+ BMO_elem_flag_enable(bm, e, EDGE_MARK);
+
+ if (!BLI_smallhash_haskey(&hash, (uintptr_t)e->v1)) {
+ eve = BLI_addfillvert(e->v1->co);
+ eve->tmp.p = e->v1;
+ BLI_smallhash_insert(&hash, (uintptr_t)e->v1, eve);
+ }
+
+ if (!BLI_smallhash_haskey(&hash, (uintptr_t)e->v2)) {
+ eve = BLI_addfillvert(e->v2->co);
+ eve->tmp.p = e->v2;
+ BLI_smallhash_insert(&hash, (uintptr_t)e->v2, eve);
+ }
+
+ v1 = BLI_smallhash_lookup(&hash, (uintptr_t)e->v1);
+ v2 = BLI_smallhash_lookup(&hash, (uintptr_t)e->v2);
+ eed = BLI_addfilledge(v1, v2);
+ eed->tmp.p = e;
+ }
+
+ BLI_edgefill(0);
+
+ for (efa = fillfacebase.first; efa; efa = efa->next) {
+ BMFace *f = BM_face_create_quad_tri(bm,
+ efa->v1->tmp.p, efa->v2->tmp.p, efa->v3->tmp.p, NULL,
+ NULL, TRUE);
+ BMLoop *l;
+ BMIter liter;
+
+ BMO_elem_flag_enable(bm, f, ELE_NEW);
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ if (!BMO_elem_flag_test(bm, l->e, EDGE_MARK)) {
+ BMO_elem_flag_enable(bm, l->e, ELE_NEW);
+ }
+ }
+ }
+
+ BLI_end_edgefill();
+ BLI_smallhash_release(&hash);
+
+ /* clean up fill */
+ BMO_op_initf(bm, &bmop, "beautify_fill faces=%ff constrain_edges=%fe", ELE_NEW, EDGE_MARK);
+ BMO_op_exec(bm, &bmop);
+ BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", ELE_NEW, BM_FACE|BM_EDGE);
+ BMO_op_finish(bm, &bmop);
+
+ BMO_slot_from_flag(bm, op, "geomout", ELE_NEW, BM_EDGE|BM_FACE);
+}
diff --git a/source/blender/bmesh/operators/bmo_utils.c b/source/blender/bmesh/operators/bmo_utils.c
new file mode 100644
index 00000000000..e0187476a6a
--- /dev/null
+++ b/source/blender/bmesh/operators/bmo_utils.c
@@ -0,0 +1,1297 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * Contributor(s): Joseph Eagar.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_meshdata_types.h"
+
+#include "BLI_math.h"
+#include "BLI_array.h"
+#include "BLI_heap.h"
+
+#include "BKE_customdata.h"
+
+#include "bmesh.h"
+
+#include "bmesh_operators_private.h" /* own include */
+
+/*
+ * UTILS.C
+ *
+ * utility bmesh operators, e.g. transform,
+ * translate, rotate, scale, etc.
+ *
+ */
+
+void bmesh_makevert_exec(BMesh *bm, BMOperator *op)
+{
+ float vec[3];
+
+ BMO_slot_vec_get(op, "co", vec);
+
+ BMO_elem_flag_enable(bm, BM_vert_create(bm, vec, NULL), 1);
+ BMO_slot_from_flag(bm, op, "newvertout", 1, BM_VERT);
+}
+
+void bmesh_transform_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter iter;
+ BMVert *v;
+ float mat[4][4];
+
+ BMO_slot_mat4_get(op, "mat", mat);
+
+ BMO_ITER(v, &iter, bm, op, "verts", BM_VERT) {
+ mul_m4_v3(mat, v->co);
+ }
+}
+
+void bmesh_translate_exec(BMesh *bm, BMOperator *op)
+{
+ float mat[4][4], vec[3];
+
+ BMO_slot_vec_get(op, "vec", vec);
+
+ unit_m4(mat);
+ copy_v3_v3(mat[3], vec);
+
+ BMO_op_callf(bm, "transform mat=%m4 verts=%s", mat, op, "verts");
+}
+
+void bmesh_scale_exec(BMesh *bm, BMOperator *op)
+{
+ float mat[3][3], vec[3];
+
+ BMO_slot_vec_get(op, "vec", vec);
+
+ unit_m3(mat);
+ mat[0][0] = vec[0];
+ mat[1][1] = vec[1];
+ mat[2][2] = vec[2];
+
+ BMO_op_callf(bm, "transform mat=%m3 verts=%s", mat, op, "verts");
+}
+
+void bmesh_rotate_exec(BMesh *bm, BMOperator *op)
+{
+ float vec[3];
+
+ BMO_slot_vec_get(op, "cent", vec);
+
+ /* there has to be a proper matrix way to do this, but
+ * this is how editmesh did it and I'm too tired to think
+ * through the math right now. */
+ mul_v3_fl(vec, -1.0f);
+ BMO_op_callf(bm, "translate verts=%s vec=%v", op, "verts", vec);
+
+ BMO_op_callf(bm, "transform mat=%s verts=%s", op, "mat", op, "verts");
+
+ mul_v3_fl(vec, -1.0f);
+ BMO_op_callf(bm, "translate verts=%s vec=%v", op, "verts", vec);
+}
+
+void bmesh_reversefaces_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMFace *f;
+
+ BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) {
+ BM_face_normal_flip(bm, f);
+ }
+}
+
+void bmesh_edgerotate_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMEdge *e, *e2;
+ int ccw = BMO_slot_int_get(op, "ccw");
+
+ BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
+ if (!(e2 = BM_edge_rotate(bm, e, ccw))) {
+ BMO_error_raise(bm, op, BMERR_INVALID_SELECTION, "Could not rotate edge");
+ return;
+ }
+
+ BMO_elem_flag_enable(bm, e2, 1);
+ }
+
+ BMO_slot_from_flag(bm, op, "edgeout", 1, BM_EDGE);
+}
+
+#define SEL_FLAG 1
+#define SEL_ORIG 2
+
+static void bmesh_regionextend_extend(BMesh *bm, BMOperator *op, int usefaces)
+{
+ BMVert *v;
+ BMEdge *e;
+ BMIter eiter;
+ BMOIter siter;
+
+ if (!usefaces) {
+ BMO_ITER(v, &siter, bm, op, "geom", BM_VERT) {
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
+ if (!BMO_elem_flag_test(bm, e, SEL_ORIG))
+ break;
+ }
+
+ if (e) {
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
+ BMO_elem_flag_enable(bm, e, SEL_FLAG);
+ BMO_elem_flag_enable(bm, BM_edge_other_vert(e, v), SEL_FLAG);
+ }
+ }
+ }
+ }
+ else {
+ BMIter liter, fiter;
+ BMFace *f, *f2;
+ BMLoop *l;
+
+ BMO_ITER(f, &siter, bm, op, "geom", BM_FACE) {
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ BM_ITER(f2, &fiter, bm, BM_FACES_OF_EDGE, l->e) {
+ if (!BMO_elem_flag_test(bm, f2, SEL_ORIG))
+ BMO_elem_flag_enable(bm, f2, SEL_FLAG);
+ }
+ }
+ }
+ }
+}
+
+static void bmesh_regionextend_constrict(BMesh *bm, BMOperator *op, int usefaces)
+{
+ BMVert *v;
+ BMEdge *e;
+ BMIter eiter;
+ BMOIter siter;
+
+ if (!usefaces) {
+ BMO_ITER(v, &siter, bm, op, "geom", BM_VERT) {
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
+ if (!BMO_elem_flag_test(bm, e, SEL_ORIG))
+ break;
+ }
+
+ if (e) {
+ BMO_elem_flag_enable(bm, v, SEL_FLAG);
+
+ BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
+ BMO_elem_flag_enable(bm, e, SEL_FLAG);
+ }
+ }
+ }
+ }
+ else {
+ BMIter liter, fiter;
+ BMFace *f, *f2;
+ BMLoop *l;
+
+ BMO_ITER(f, &siter, bm, op, "geom", BM_FACE) {
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ BM_ITER(f2, &fiter, bm, BM_FACES_OF_EDGE, l->e) {
+ if (!BMO_elem_flag_test(bm, f2, SEL_ORIG)) {
+ BMO_elem_flag_enable(bm, f, SEL_FLAG);
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+void bmesh_regionextend_exec(BMesh *bm, BMOperator *op)
+{
+ int usefaces = BMO_slot_int_get(op, "usefaces");
+ int constrict = BMO_slot_int_get(op, "constrict");
+
+ BMO_slot_buffer_flag_enable(bm, op, "geom", SEL_ORIG, BM_ALL);
+
+ if (constrict)
+ bmesh_regionextend_constrict(bm, op, usefaces);
+ else
+ bmesh_regionextend_extend(bm, op, usefaces);
+
+ BMO_slot_from_flag(bm, op, "geomout", SEL_FLAG, BM_ALL);
+}
+
+/********* righthand faces implementation ****** */
+
+#define FACE_VIS 1
+#define FACE_FLAG 2
+#define FACE_MARK 4
+#define FACE_FLIP 8
+
+/* NOTE: these are the original righthandfaces comment in editmesh_mods.c,
+ * copied here for reference. */
+
+/* based at a select-connected to witness loose objects */
+
+/* count per edge the amount of faces
+ * find the ultimate left, front, upper face (not manhattan dist!!)
+ * also evaluate both triangle cases in quad, since these can be non-flat
+ *
+ * put normal to the outside, and set the first direction flags in edges
+ *
+ * then check the object, and set directions / direction-flags: but only for edges with 1 or 2 faces
+ * this is in fact the 'select connected'
+ *
+ * in case (selected) faces were not done: start over with 'find the ultimate ...' */
+
+/* NOTE: this function uses recursion, which is a little unusual for a bmop
+ * function, but acceptable I think. */
+
+/* NOTE: BM_ELEM_TAG is used on faces to tell if they are flipped. */
+
+void bmesh_righthandfaces_exec(BMesh *bm, BMOperator *op)
+{
+ BMIter liter, liter2;
+ BMOIter siter;
+ BMFace *f, *startf, **fstack = NULL;
+ BLI_array_declare(fstack);
+ BMLoop *l, *l2;
+ float maxx, cent[3];
+ int i, maxi, flagflip = BMO_slot_int_get(op, "doflip");
+
+ startf = NULL;
+ maxx = -1.0e10;
+
+ BMO_slot_buffer_flag_enable(bm, op, "faces", FACE_FLAG, BM_FACE);
+
+ /* find a starting face */
+ BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) {
+
+ /* clear dirty flag */
+ BM_elem_flag_disable(f, BM_ELEM_TAG);
+
+ if (BMO_elem_flag_test(bm, f, FACE_VIS))
+ continue;
+
+ if (!startf) startf = f;
+
+ BM_face_center_bounds_calc(bm, f, cent);
+
+ cent[0] = cent[0]*cent[0] + cent[1]*cent[1] + cent[2]*cent[2];
+ if (cent[0] > maxx) {
+ maxx = cent[0];
+ startf = f;
+ }
+ }
+
+ if (!startf) return;
+
+ BM_face_center_bounds_calc(bm, startf, cent);
+
+ /* make sure the starting face has the correct winding */
+ if (dot_v3v3(cent, startf->no) < 0.0f) {
+ BM_face_normal_flip(bm, startf);
+ BMO_elem_flag_toggle(bm, startf, FACE_FLIP);
+
+ if (flagflip)
+ BM_elem_flag_toggle(startf, BM_ELEM_TAG);
+ }
+
+ /* now that we've found our starting face, make all connected faces
+ * have the same winding. this is done recursively, using a manual
+ * stack (if we use simple function recursion, we'd end up overloading
+ * the stack on large meshes). */
+
+ BLI_array_growone(fstack);
+ fstack[0] = startf;
+ BMO_elem_flag_enable(bm, startf, FACE_VIS);
+
+ i = 0;
+ maxi = 1;
+ while (i >= 0) {
+ f = fstack[i];
+ i--;
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_LOOP, l) {
+ if (!BMO_elem_flag_test(bm, l2->f, FACE_FLAG) || l2 == l)
+ continue;
+
+ if (!BMO_elem_flag_test(bm, l2->f, FACE_VIS)) {
+ BMO_elem_flag_enable(bm, l2->f, FACE_VIS);
+ i++;
+
+ if (l2->v == l->v) {
+ BM_face_normal_flip(bm, l2->f);
+
+ BMO_elem_flag_toggle(bm, l2->f, FACE_FLIP);
+ if (flagflip)
+ BM_elem_flag_toggle(l2->f, BM_ELEM_TAG);
+ }
+ else if (BM_elem_flag_test(l2->f, BM_ELEM_TAG) || BM_elem_flag_test(l->f, BM_ELEM_TAG)) {
+ if (flagflip) {
+ BM_elem_flag_disable(l->f, BM_ELEM_TAG);
+ BM_elem_flag_disable(l2->f, BM_ELEM_TAG);
+ }
+ }
+
+ if (i == maxi) {
+ BLI_array_growone(fstack);
+ maxi++;
+ }
+
+ fstack[i] = l2->f;
+ }
+ }
+ }
+ }
+
+ BLI_array_free(fstack);
+
+ /* check if we have faces yet to do. if so, recurse */
+ BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) {
+ if (!BMO_elem_flag_test(bm, f, FACE_VIS)) {
+ bmesh_righthandfaces_exec(bm, op);
+ break;
+ }
+ }
+}
+
+void bmesh_vertexsmooth_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter siter;
+ BMIter iter;
+ BMVert *v;
+ BMEdge *e;
+ BLI_array_declare(cos);
+ float (*cos)[3] = NULL;
+ float *co, *co2, clipdist = BMO_slot_float_get(op, "clipdist");
+ int i, j, clipx, clipy, clipz;
+
+ clipx = BMO_slot_int_get(op, "mirror_clip_x");
+ clipy = BMO_slot_int_get(op, "mirror_clip_y");
+ clipz = BMO_slot_int_get(op, "mirror_clip_z");
+
+ i = 0;
+ BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) {
+ BLI_array_growone(cos);
+ co = cos[i];
+
+ j = 0;
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) {
+ co2 = BM_edge_other_vert(e, v)->co;
+ add_v3_v3v3(co, co, co2);
+ j += 1;
+ }
+
+ if (!j) {
+ copy_v3_v3(co, v->co);
+ i++;
+ continue;
+ }
+
+ mul_v3_fl(co, 1.0f / (float)j);
+ mid_v3_v3v3(co, co, v->co);
+
+ if (clipx && fabsf(v->co[0]) <= clipdist)
+ co[0] = 0.0f;
+ if (clipy && fabsf(v->co[1]) <= clipdist)
+ co[1] = 0.0f;
+ if (clipz && fabsf(v->co[2]) <= clipdist)
+ co[2] = 0.0f;
+
+ i++;
+ }
+
+ i = 0;
+ BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) {
+ copy_v3_v3(v->co, cos[i]);
+ i++;
+ }
+
+ BLI_array_free(cos);
+}
+
+/*
+ * compute the perimeter of an ngon
+ *
+ * NOTE: This should probably go to bmesh_polygon.c
+ */
+static float ngon_perimeter(BMesh *bm, BMFace *f)
+{
+ BMIter liter;
+ BMLoop *l;
+ int num_verts = 0;
+ float v[3], sv[3];
+ float perimeter = 0.0f;
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ if (num_verts == 0) {
+ copy_v3_v3(v, l->v->co);
+ copy_v3_v3(sv, l->v->co);
+ }
+ else {
+ perimeter += len_v3v3(v, l->v->co);
+ copy_v3_v3(v, l->v->co);
+ }
+ num_verts++;
+ }
+
+ perimeter += len_v3v3(v, sv);
+
+ return perimeter;
+}
+
+/*
+ * compute the fake surface of an ngon
+ * This is done by decomposing the ngon into triangles who share the centroid of the ngon
+ * while this method is far from being exact, it should garantee an invariance.
+ *
+ * NOTE: This should probably go to bmesh_polygon.c
+ */
+static float ngon_fake_area(BMesh *bm, BMFace *f)
+{
+ BMIter liter;
+ BMLoop *l;
+ int num_verts = 0;
+ float v[3], sv[3], c[3];
+ float area = 0.0f;
+
+ BM_face_center_mean_calc(bm, f, c);
+
+ BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) {
+ if (num_verts == 0) {
+ copy_v3_v3(v, l->v->co);
+ copy_v3_v3(sv, l->v->co);
+ num_verts++;
+ }
+ else {
+ area += area_tri_v3(v, c, l->v->co);
+ copy_v3_v3(v, l->v->co);
+ num_verts++;
+ }
+ }
+
+ area += area_tri_v3(v, c, sv);
+
+ return area;
+}
+
+/*
+ * extra face data (computed data)
+ */
+typedef struct tmp_face_ext {
+ BMFace *f; /* the face */
+ float c[3]; /* center */
+ union {
+ float area; /* area */
+ float perim; /* perimeter */
+ float d; /* 4th component of plane (the first three being the normal) */
+ struct Image *t; /* image pointer */
+ };
+} tmp_face_ext;
+
+/*
+ * Select similar faces, the choices are in the enum in source/blender/bmesh/bmesh_operators.h
+ * We select either similar faces based on material, image, area, perimeter, normal, or the coplanar faces
+ */
+void bmesh_similarfaces_exec(BMesh *bm, BMOperator *op)
+{
+ BMIter fm_iter;
+ BMFace *fs, *fm;
+ BMOIter fs_iter;
+ int num_sels = 0, num_total = 0, i = 0, idx = 0;
+ float angle = 0.0f;
+ tmp_face_ext *f_ext = NULL;
+ int *indices = NULL;
+ float t_no[3]; /* temporary normal */
+ int type = BMO_slot_int_get(op, "type");
+ float thresh = BMO_slot_float_get(op, "thresh");
+
+ num_total = BM_mesh_elem_count(bm, BM_FACE);
+
+ /*
+ ** The first thing to do is to iterate through all the the selected items and mark them since
+ ** they will be in the selection anyway.
+ ** This will increase performance, (especially when the number of originaly selected faces is high)
+ ** so the overall complexity will be less than $O(mn)$ where is the total number of selected faces,
+ ** and n is the total number of faces
+ */
+ BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) {
+ if (!BMO_elem_flag_test(bm, fs, FACE_MARK)) { /* is this really needed ? */
+ BMO_elem_flag_enable(bm, fs, FACE_MARK);
+ num_sels++;
+ }
+ }
+
+ /* allocate memory for the selected faces indices and for all temporary faces */
+ indices = (int *)MEM_callocN(sizeof(int) * num_sels, "face indices util.c");
+ f_ext = (tmp_face_ext *)MEM_callocN(sizeof(tmp_face_ext) * num_total, "f_ext util.c");
+
+ /* loop through all the faces and fill the faces/indices structure */
+ BM_ITER(fm, &fm_iter, bm, BM_FACES_OF_MESH, NULL) {
+ f_ext[i].f = fm;
+ if (BMO_elem_flag_test(bm, fm, FACE_MARK)) {
+ indices[idx] = i;
+ idx++;
+ }
+ i++;
+ }
+
+ /*
+ ** Save us some computation burden: In case of perimeter/area/coplanar selection we compute
+ ** only once.
+ */
+ if (type == SIMFACE_PERIMETER || type == SIMFACE_AREA || type == SIMFACE_COPLANAR || type == SIMFACE_IMAGE) {
+ for (i = 0; i < num_total; i++) {
+ switch (type) {
+ case SIMFACE_PERIMETER:
+ /* set the perimeter */
+ f_ext[i].perim = ngon_perimeter(bm, f_ext[i].f);
+ break;
+
+ case SIMFACE_COPLANAR:
+ /* compute the center of the polygon */
+ BM_face_center_mean_calc(bm, f_ext[i].f, f_ext[i].c);
+
+ /* normalize the polygon normal */
+ copy_v3_v3(t_no, f_ext[i].f->no);
+ normalize_v3(t_no);
+
+ /* compute the plane distance */
+ f_ext[i].d = dot_v3v3(t_no, f_ext[i].c);
+ break;
+
+ case SIMFACE_AREA:
+ f_ext[i].area = ngon_fake_area(bm, f_ext[i].f);
+ break;
+
+ case SIMFACE_IMAGE:
+ f_ext[i].t = NULL;
+ if (CustomData_has_layer(&(bm->pdata), CD_MTEXPOLY)) {
+ MTexPoly *mtpoly = CustomData_bmesh_get(&bm->pdata, f_ext[i].f->head.data, CD_MTEXPOLY);
+ f_ext[i].t = mtpoly->tpage;
+ }
+ break;
+ }
+ }
+ }
+
+ /* now select the rest (if any) */
+ for (i = 0; i < num_total; i++) {
+ fm = f_ext[i].f;
+ if (!BMO_elem_flag_test(bm, fm, FACE_MARK) && !BM_elem_flag_test(fm, BM_ELEM_HIDDEN)) {
+ int cont = 1;
+ for (idx = 0; idx < num_sels && cont == 1; idx++) {
+ fs = f_ext[indices[idx]].f;
+ switch (type) {
+ case SIMFACE_MATERIAL:
+ if (fm->mat_nr == fs->mat_nr) {
+ BMO_elem_flag_enable(bm, fm, FACE_MARK);
+ cont = 0;
+ }
+ break;
+
+ case SIMFACE_IMAGE:
+ if (f_ext[i].t == f_ext[indices[idx]].t) {
+ BMO_elem_flag_enable(bm, fm, FACE_MARK);
+ cont = 0;
+ }
+ break;
+
+ case SIMFACE_NORMAL:
+ angle = RAD2DEGF(angle_v3v3(fs->no, fm->no)); /* if the angle between the normals -> 0 */
+ if (angle / 180.0f <= thresh) {
+ BMO_elem_flag_enable(bm, fm, FACE_MARK);
+ cont = 0;
+ }
+ break;
+
+ case SIMFACE_COPLANAR:
+ angle = RAD2DEGF(angle_v3v3(fs->no, fm->no)); /* angle -> 0 */
+ if (angle / 180.0f <= thresh) { /* and dot product difference -> 0 */
+ if (fabsf(f_ext[i].d - f_ext[indices[idx]].d) <= thresh) {
+ BMO_elem_flag_enable(bm, fm, FACE_MARK);
+ cont = 0;
+ }
+ }
+ break;
+
+ case SIMFACE_AREA:
+ if (fabsf(f_ext[i].area - f_ext[indices[idx]].area) <= thresh) {
+ BMO_elem_flag_enable(bm, fm, FACE_MARK);
+ cont = 0;
+ }
+ break;
+
+ case SIMFACE_PERIMETER:
+ if (fabsf(f_ext[i].perim - f_ext[indices[idx]].perim) <= thresh) {
+ BMO_elem_flag_enable(bm, fm, FACE_MARK);
+ cont = 0;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ MEM_freeN(f_ext);
+ MEM_freeN(indices);
+
+ /* transfer all marked faces to the output slot */
+ BMO_slot_from_flag(bm, op, "faceout", FACE_MARK, BM_FACE);
+}
+
+/******************************************************************************
+** Similar Edges
+**************************************************************************** */
+#define EDGE_MARK 1
+
+/*
+ * compute the angle of an edge (i.e. the angle between two faces)
+ */
+static float edge_angle(BMesh *bm, BMEdge *e)
+{
+ BMIter fiter;
+ BMFace *f, *f_prev = NULL;
+
+ /* first edge faces, dont account for 3+ */
+
+ BM_ITER(f, &fiter, bm, BM_FACES_OF_EDGE, e) {
+ if (f_prev == NULL) {
+ f_prev = f;
+ }
+ else {
+ return angle_v3v3(f_prev->no, f->no);
+ }
+ }
+
+ return 0.0f;
+}
+/*
+ * extra edge information
+ */
+typedef struct tmp_edge_ext {
+ BMEdge *e;
+ union {
+ float dir[3];
+ float angle; /* angle between the face */
+ };
+
+ union {
+ float length; /* edge length */
+ int faces; /* faces count */
+ };
+} tmp_edge_ext;
+
+/*
+ * select similar edges: the choices are in the enum in source/blender/bmesh/bmesh_operators.h
+ * choices are length, direction, face, ...
+ */
+void bmesh_similaredges_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter es_iter; /* selected edges iterator */
+ BMIter e_iter; /* mesh edges iterator */
+ BMEdge *es; /* selected edge */
+ BMEdge *e; /* mesh edge */
+ int idx = 0, i = 0 /* , f = 0 */;
+ int *indices = NULL;
+ tmp_edge_ext *e_ext = NULL;
+ // float *angles = NULL;
+ float angle;
+
+ int num_sels = 0, num_total = 0;
+ int type = BMO_slot_int_get(op, "type");
+ float thresh = BMO_slot_float_get(op, "thresh");
+
+ num_total = BM_mesh_elem_count(bm, BM_EDGE);
+
+ /* iterate through all selected edges and mark them */
+ BMO_ITER(es, &es_iter, bm, op, "edges", BM_EDGE) {
+ BMO_elem_flag_enable(bm, es, EDGE_MARK);
+ num_sels++;
+ }
+
+ /* allocate memory for the selected edges indices and for all temporary edges */
+ indices = (int *)MEM_callocN(sizeof(int) * num_sels, "indices util.c");
+ e_ext = (tmp_edge_ext *)MEM_callocN(sizeof(tmp_edge_ext) * num_total, "e_ext util.c");
+
+ /* loop through all the edges and fill the edges/indices structure */
+ BM_ITER(e, &e_iter, bm, BM_EDGES_OF_MESH, NULL) {
+ e_ext[i].e = e;
+ if (BMO_elem_flag_test(bm, e, EDGE_MARK)) {
+ indices[idx] = i;
+ idx++;
+ }
+ i++;
+ }
+
+ /* save us some computation time by doing heavy computation once */
+ if (type == SIMEDGE_LENGTH || type == SIMEDGE_FACE || type == SIMEDGE_DIR || type == SIMEDGE_FACE_ANGLE) {
+ for (i = 0; i < num_total; i++) {
+ switch (type) {
+ case SIMEDGE_LENGTH: /* compute the length of the edge */
+ e_ext[i].length = len_v3v3(e_ext[i].e->v1->co, e_ext[i].e->v2->co);
+ break;
+
+ case SIMEDGE_DIR: /* compute the direction */
+ sub_v3_v3v3(e_ext[i].dir, e_ext[i].e->v1->co, e_ext[i].e->v2->co);
+ break;
+
+ case SIMEDGE_FACE: /* count the faces around the edge */
+ e_ext[i].faces = BM_edge_face_count(e_ext[i].e);
+ break;
+
+ case SIMEDGE_FACE_ANGLE:
+ e_ext[i].faces = BM_edge_face_count(e_ext[i].e);
+ if (e_ext[i].faces == 2)
+ e_ext[i].angle = edge_angle(bm, e_ext[i].e);
+ break;
+ }
+ }
+ }
+
+ /* select the edges if any */
+ for (i = 0; i < num_total; i++) {
+ e = e_ext[i].e;
+ if (!BMO_elem_flag_test(bm, e, EDGE_MARK) && !BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
+ int cont = 1;
+ for (idx = 0; idx < num_sels && cont == 1; idx++) {
+ es = e_ext[indices[idx]].e;
+ switch (type) {
+ case SIMEDGE_LENGTH:
+ if (fabsf(e_ext[i].length - e_ext[indices[idx]].length) <= thresh) {
+ BMO_elem_flag_enable(bm, e, EDGE_MARK);
+ cont = 0;
+ }
+ break;
+
+ case SIMEDGE_DIR:
+ /* compute the angle between the two edges */
+ angle = RAD2DEGF(angle_v3v3(e_ext[i].dir, e_ext[indices[idx]].dir));
+
+ if (angle > 90.0f) /* use the smallest angle between the edges */
+ angle = fabsf(angle - 180.0f);
+
+ if (angle / 90.0f <= thresh) {
+ BMO_elem_flag_enable(bm, e, EDGE_MARK);
+ cont = 0;
+ }
+ break;
+
+ case SIMEDGE_FACE:
+ if (e_ext[i].faces == e_ext[indices[idx]].faces) {
+ BMO_elem_flag_enable(bm, e, EDGE_MARK);
+ cont = 0;
+ }
+ break;
+
+ case SIMEDGE_FACE_ANGLE:
+ if (e_ext[i].faces == 2) {
+ if (e_ext[indices[idx]].faces == 2) {
+ if (fabsf(e_ext[i].angle - e_ext[indices[idx]].angle) <= thresh) {
+ BMO_elem_flag_enable(bm, e, EDGE_MARK);
+ cont = 0;
+ }
+ }
+ }
+ else {
+ cont = 0;
+ }
+ break;
+
+ case SIMEDGE_CREASE:
+ if (CustomData_has_layer(&bm->edata, CD_CREASE)) {
+ float *c1, *c2;
+
+ c1 = CustomData_bmesh_get(&bm->edata, e->head.data, CD_CREASE);
+ c2 = CustomData_bmesh_get(&bm->edata, es->head.data, CD_CREASE);
+
+ if (c1 && c2 && fabsf(*c1 - *c2) <= thresh) {
+ BMO_elem_flag_enable(bm, e, EDGE_MARK);
+ cont = 0;
+ }
+ }
+ break;
+
+ case SIMEDGE_SEAM:
+ if (BM_elem_flag_test(e, BM_ELEM_SEAM) == BM_elem_flag_test(es, BM_ELEM_SEAM)) {
+ BMO_elem_flag_enable(bm, e, EDGE_MARK);
+ cont = 0;
+ }
+ break;
+
+ case SIMEDGE_SHARP:
+ if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) == BM_elem_flag_test(es, BM_ELEM_SMOOTH)) {
+ BMO_elem_flag_enable(bm, e, EDGE_MARK);
+ cont = 0;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ MEM_freeN(e_ext);
+ MEM_freeN(indices);
+
+ /* transfer all marked edges to the output slot */
+ BMO_slot_from_flag(bm, op, "edgeout", EDGE_MARK, BM_EDGE);
+}
+
+/******************************************************************************
+** Similar Vertices
+**************************************************************************** */
+#define VERT_MARK 1
+
+typedef struct tmp_vert_ext {
+ BMVert *v;
+ union {
+ int num_faces; /* adjacent faces */
+ MDeformVert *dvert; /* deform vertex */
+ };
+} tmp_vert_ext;
+
+/*
+ * select similar vertices: the choices are in the enum in source/blender/bmesh/bmesh_operators.h
+ * choices are normal, face, vertex group...
+ */
+void bmesh_similarverts_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter vs_iter; /* selected verts iterator */
+ BMIter v_iter; /* mesh verts iterator */
+ BMVert *vs; /* selected vertex */
+ BMVert *v; /* mesh vertex */
+ tmp_vert_ext *v_ext = NULL;
+ int *indices = NULL;
+ int num_total = 0, num_sels = 0, i = 0, idx = 0;
+ int type = BMO_slot_int_get(op, "type");
+ float thresh = BMO_slot_float_get(op, "thresh");
+
+ num_total = BM_mesh_elem_count(bm, BM_VERT);
+
+ /* iterate through all selected edges and mark them */
+ BMO_ITER(vs, &vs_iter, bm, op, "verts", BM_VERT) {
+ BMO_elem_flag_enable(bm, vs, VERT_MARK);
+ num_sels++;
+ }
+
+ /* allocate memory for the selected vertices indices and for all temporary vertices */
+ indices = (int *)MEM_mallocN(sizeof(int) * num_sels, "vertex indices");
+ v_ext = (tmp_vert_ext *)MEM_mallocN(sizeof(tmp_vert_ext) * num_total, "vertex extra");
+
+ /* loop through all the vertices and fill the vertices/indices structure */
+ BM_ITER(v, &v_iter, bm, BM_VERTS_OF_MESH, NULL) {
+ v_ext[i].v = v;
+ if (BMO_elem_flag_test(bm, v, VERT_MARK)) {
+ indices[idx] = i;
+ idx++;
+ }
+
+ switch (type) {
+ case SIMVERT_FACE:
+ /* calling BM_vert_face_count every time is time consumming, so call it only once per vertex */
+ v_ext[i].num_faces = BM_vert_face_count(v);
+ break;
+
+ case SIMVERT_VGROUP:
+ if (CustomData_has_layer(&(bm->vdata), CD_MDEFORMVERT)) {
+ v_ext[i].dvert = CustomData_bmesh_get(&bm->vdata, v_ext[i].v->head.data, CD_MDEFORMVERT);
+ }
+ else {
+ v_ext[i].dvert = NULL;
+ }
+ break;
+ }
+
+ i++;
+ }
+
+ /* select the vertices if any */
+ for (i = 0; i < num_total; i++) {
+ v = v_ext[i].v;
+ if (!BMO_elem_flag_test(bm, v, VERT_MARK) && !BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
+ int cont = 1;
+ for (idx = 0; idx < num_sels && cont == 1; idx++) {
+ vs = v_ext[indices[idx]].v;
+ switch (type) {
+ case SIMVERT_NORMAL:
+ /* compare the angle between the normals */
+ if (RAD2DEGF(angle_v3v3(v->no, vs->no)) / 180.0f <= thresh) {
+ BMO_elem_flag_enable(bm, v, VERT_MARK);
+ cont = 0;
+ }
+ break;
+ case SIMVERT_FACE:
+ /* number of adjacent faces */
+ if (v_ext[i].num_faces == v_ext[indices[idx]].num_faces) {
+ BMO_elem_flag_enable(bm, v, VERT_MARK);
+ cont = 0;
+ }
+ break;
+
+ case SIMVERT_VGROUP:
+ if (v_ext[i].dvert != NULL && v_ext[indices[idx]].dvert != NULL) {
+ int v1, v2;
+ for (v1 = 0; v1 < v_ext[i].dvert->totweight && cont == 1; v1++) {
+ for (v2 = 0; v2 < v_ext[indices[idx]].dvert->totweight; v2++) {
+ if (v_ext[i].dvert->dw[v1].def_nr == v_ext[indices[idx]].dvert->dw[v2].def_nr) {
+ BMO_elem_flag_enable(bm, v, VERT_MARK);
+ cont = 0;
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ MEM_freeN(indices);
+ MEM_freeN(v_ext);
+
+ BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
+}
+
+/******************************************************************************
+** Cycle UVs for a face
+**************************************************************************** */
+
+void bmesh_rotateuvs_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter fs_iter; /* selected faces iterator */
+ BMFace *fs; /* current face */
+ BMIter l_iter; /* iteration loop */
+ // int n;
+
+ int dir = BMO_slot_int_get(op, "dir");
+
+ BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) {
+ if (CustomData_has_layer(&(bm->ldata), CD_MLOOPUV)) {
+ if (dir == DIRECTION_CW) { /* same loops direction */
+ BMLoop *lf; /* current face loops */
+ MLoopUV *f_luv; /* first face loop uv */
+ float p_uv[2]; /* previous uvs */
+ float t_uv[2]; /* tmp uvs */
+
+ int n = 0;
+ BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
+ /* current loop uv is the previous loop uv */
+ MLoopUV *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV);
+ if (n == 0) {
+ f_luv = luv;
+ copy_v2_v2(p_uv, luv->uv);
+ }
+ else {
+ copy_v2_v2(t_uv, luv->uv);
+ copy_v2_v2(luv->uv, p_uv);
+ copy_v2_v2(p_uv, t_uv);
+ }
+ n++;
+ }
+
+ copy_v2_v2(f_luv->uv, p_uv);
+ }
+ else if (dir == DIRECTION_CCW) { /* counter loop direction */
+ BMLoop *lf; /* current face loops */
+ MLoopUV *p_luv; /* previous loop uv */
+ MLoopUV *luv;
+ float t_uv[2]; /* current uvs */
+
+ int n = 0;
+ BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
+ /* previous loop uv is the current loop uv */
+ luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV);
+ if (n == 0) {
+ p_luv = luv;
+ copy_v2_v2(t_uv, luv->uv);
+ }
+ else {
+ copy_v2_v2(p_luv->uv, luv->uv);
+ p_luv = luv;
+ }
+ n++;
+ }
+
+ copy_v2_v2(luv->uv, t_uv);
+ }
+ }
+ }
+
+}
+
+/******************************************************************************
+** Reverse UVs for a face
+**************************************************************************** */
+
+void bmesh_reverseuvs_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter fs_iter; /* selected faces iterator */
+ BMFace *fs; /* current face */
+ BMIter l_iter; /* iteration loop */
+ BLI_array_declare(uvs);
+ float (*uvs)[2] = NULL;
+
+ BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) {
+ if (CustomData_has_layer(&(bm->ldata), CD_MLOOPUV)) {
+ BMLoop *lf; /* current face loops */
+ int i = 0;
+
+ BLI_array_empty(uvs);
+ BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
+ MLoopUV *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV);
+
+ /* current loop uv is the previous loop uv */
+ BLI_array_growone(uvs);
+ uvs[i][0] = luv->uv[0];
+ uvs[i][1] = luv->uv[1];
+ i++;
+ }
+
+ /* now that we have the uvs in the array, reverse! */
+ i = 0;
+ BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
+ /* current loop uv is the previous loop uv */
+ MLoopUV *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV);
+ luv->uv[0] = uvs[(fs->len - i - 1)][0];
+ luv->uv[1] = uvs[(fs->len - i - 1)][1];
+ i++;
+ }
+ }
+ }
+
+ BLI_array_free(uvs);
+}
+
+/******************************************************************************
+** Cycle colors for a face
+**************************************************************************** */
+
+void bmesh_rotatecolors_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter fs_iter; /* selected faces iterator */
+ BMFace *fs; /* current face */
+ BMIter l_iter; /* iteration loop */
+ // int n;
+
+ int dir = BMO_slot_int_get(op, "dir");
+
+ BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) {
+ if (CustomData_has_layer(&(bm->ldata), CD_MLOOPCOL)) {
+ if (dir == DIRECTION_CW) { /* same loops direction */
+ BMLoop *lf; /* current face loops */
+ MLoopCol *f_lcol; /* first face loop color */
+ MLoopCol p_col; /* previous color */
+ MLoopCol t_col; /* tmp color */
+
+ int n = 0;
+ BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
+ /* current loop color is the previous loop color */
+ MLoopCol *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL);
+ if (n == 0) {
+ f_lcol = luv;
+ p_col = *luv;
+ }
+ else {
+ t_col = *luv;
+ *luv = p_col;
+ p_col = t_col;
+ }
+ n++;
+ }
+
+ *f_lcol = p_col;
+ }
+ else if (dir == DIRECTION_CCW) { /* counter loop direction */
+ BMLoop *lf; /* current face loops */
+ MLoopCol *p_lcol; /* previous loop color */
+ MLoopCol *lcol;
+ MLoopCol t_col; /* current color */
+
+ int n = 0;
+ BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
+ /* previous loop color is the current loop color */
+ lcol = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL);
+ if (n == 0) {
+ p_lcol = lcol;
+ t_col = *lcol;
+ }
+ else {
+ *p_lcol = *lcol;
+ p_lcol = lcol;
+ }
+ n++;
+ }
+
+ *lcol = t_col;
+ }
+ }
+ }
+}
+
+/******************************************************************************
+** Reverse colors for a face
+**************************************************************************** */
+
+void bmesh_reversecolors_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter fs_iter; /* selected faces iterator */
+ BMFace *fs; /* current face */
+ BMIter l_iter; /* iteration loop */
+ BLI_array_declare(cols);
+ MLoopCol *cols = NULL;
+
+ BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) {
+ if (CustomData_has_layer(&(bm->ldata), CD_MLOOPCOL)) {
+ BMLoop *lf; /* current face loops */
+ int i = 0;
+
+ BLI_array_empty(cols);
+ BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
+ MLoopCol *lcol = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL);
+
+ /* current loop uv is the previous loop color */
+ BLI_array_growone(cols);
+ cols[i] = *lcol;
+ i++;
+ }
+
+ /* now that we have the uvs in the array, reverse! */
+ i = 0;
+ BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) {
+ /* current loop uv is the previous loop color */
+ MLoopCol *lcol = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL);
+ *lcol = cols[(fs->len - i - 1)];
+ i++;
+ }
+ }
+ }
+
+ BLI_array_free(cols);
+}
+
+
+/******************************************************************************
+** shortest vertex path select
+**************************************************************************** */
+
+typedef struct element_node {
+ BMVert *v; /* vertex */
+ BMVert *parent; /* node parent id */
+ float weight; /* node weight */
+ HeapNode *hn; /* heap node */
+} element_node;
+
+void bmesh_vertexshortestpath_exec(BMesh *bm, BMOperator *op)
+{
+ BMOIter vs_iter /* , vs2_iter */; /* selected verts iterator */
+ BMIter v_iter; /* mesh verts iterator */
+ BMVert *vs, *sv, *ev; /* starting vertex, ending vertex */
+ BMVert *v; /* mesh vertex */
+ Heap *h = NULL;
+
+ element_node *vert_list = NULL;
+
+ int num_total = 0 /*, num_sels = 0 */, i = 0;
+ int type = BMO_slot_int_get(op, "type");
+
+ BMO_ITER(vs, &vs_iter, bm, op, "startv", BM_VERT) {
+ sv = vs;
+ }
+ BMO_ITER(vs, &vs_iter, bm, op, "endv", BM_VERT) {
+ ev = vs;
+ }
+
+ num_total = BM_mesh_elem_count(bm, BM_VERT);
+
+ /* allocate memory for the nodes */
+ vert_list = (element_node *)MEM_mallocN(sizeof(element_node) * num_total, "vertex nodes");
+
+ /* iterate through all the mesh vertices */
+ /* loop through all the vertices and fill the vertices/indices structure */
+ i = 0;
+ BM_ITER(v, &v_iter, bm, BM_VERTS_OF_MESH, NULL) {
+ vert_list[i].v = v;
+ vert_list[i].parent = NULL;
+ vert_list[i].weight = FLT_MAX;
+ BM_elem_index_set(v, i); /* set_inline */
+ i++;
+ }
+ bm->elem_index_dirty &= ~BM_VERT;
+
+ /*
+ ** we now have everything we need, start Dijkstra path finding algorithm
+ */
+
+ /* set the distance/weight of the start vertex to 0 */
+ vert_list[BM_elem_index_get(sv)].weight = 0.0f;
+
+ h = BLI_heap_new();
+
+ for (i = 0; i < num_total; i++) {
+ vert_list[i].hn = BLI_heap_insert(h, vert_list[i].weight, vert_list[i].v);
+ }
+
+ while (!BLI_heap_empty(h)) {
+ BMEdge *e;
+ BMIter e_i;
+ float v_weight;
+
+ /* take the vertex with the lowest weight out of the heap */
+ BMVert *v = (BMVert *)BLI_heap_popmin(h);
+
+ if (vert_list[BM_elem_index_get(v)].weight == FLT_MAX) /* this means that there is no path */
+ break;
+
+ v_weight = vert_list[BM_elem_index_get(v)].weight;
+
+ BM_ITER(e, &e_i, bm, BM_EDGES_OF_VERT, v) {
+ BMVert *u;
+ float e_weight = v_weight;
+
+ if (type == VPATH_SELECT_EDGE_LENGTH)
+ e_weight += len_v3v3(e->v1->co, e->v2->co);
+ else e_weight += 1.0f;
+
+ u = (e->v1 == v) ? e->v2 : e->v1;
+
+ if (e_weight < vert_list[BM_elem_index_get(u)].weight) { /* is this path shorter ? */
+ /* add it if so */
+ vert_list[BM_elem_index_get(u)].parent = v;
+ vert_list[BM_elem_index_get(u)].weight = e_weight;
+
+ /* we should do a heap update node function!!! :-/ */
+ BLI_heap_remove(h, vert_list[BM_elem_index_get(u)].hn);
+ BLI_heap_insert(h, e_weight, u);
+ }
+ }
+ }
+
+ /* now we trace the path (if it exists) */
+ v = ev;
+
+ while (vert_list[BM_elem_index_get(v)].parent != NULL) {
+ BMO_elem_flag_enable(bm, v, VERT_MARK);
+ v = vert_list[BM_elem_index_get(v)].parent;
+ }
+
+ BLI_heap_free(h, NULL);
+ MEM_freeN(vert_list);
+
+ BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT);
+}