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/tools
parentd6deca4e9d6bc7faff98644286571c7934324a9d (diff)
copying bmesh dir on its own from bmesh branch
Diffstat (limited to 'source/blender/bmesh/tools')
-rw-r--r--source/blender/bmesh/tools/BME_bevel.c1011
-rw-r--r--source/blender/bmesh/tools/BME_dupe_ops.c322
-rw-r--r--source/blender/bmesh/tools/BME_duplicate.c310
-rw-r--r--source/blender/bmesh/tools/BME_weld.c341
4 files changed, 1984 insertions, 0 deletions
diff --git a/source/blender/bmesh/tools/BME_bevel.c b/source/blender/bmesh/tools/BME_bevel.c
new file mode 100644
index 00000000000..b20b7316eb4
--- /dev/null
+++ b/source/blender/bmesh/tools/BME_bevel.c
@@ -0,0 +1,1011 @@
+/*
+ * ***** 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.
+ *
+ * The Original Code is Copyright (C) 2004 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Geoffrey Bantle and Levi Schooley.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <math.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_listBase.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_mesh_types.h"
+
+#include "BKE_utildefines.h"
+#include "BKE_tessmesh.h"
+#include "BKE_bmesh.h"
+#include "BLI_math.h"
+#include "BLI_blenlib.h"
+#include "BLI_ghash.h"
+
+#include "bmesh.h"
+#include "bmesh_private.h"
+
+/* ------- Bevel code starts here -------- */
+
+BME_TransData_Head *BME_init_transdata(int bufsize)
+{
+ BME_TransData_Head *td;
+
+ td = MEM_callocN(sizeof(BME_TransData_Head), "BM transdata header");
+ td->gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "BME_init_transdata gh");
+ td->ma = BLI_memarena_new(bufsize, "BME_TransData arena");
+ BLI_memarena_use_calloc(td->ma);
+
+ return td;
+}
+
+void BME_free_transdata(BME_TransData_Head *td)
+{
+ BLI_ghash_free(td->gh, NULL, NULL);
+ BLI_memarena_free(td->ma);
+ MEM_freeN(td);
+}
+
+BME_TransData *BME_assign_transdata(
+ BME_TransData_Head *td, BMesh *bm, BMVert *v,
+ float *co, float *org, float *vec, float *loc,
+ float factor, float weight, float maxfactor, float *max)
+{
+ BME_TransData *vtd;
+ int is_new = 0;
+
+ if (v == NULL) {
+ return NULL;
+ }
+
+ if ((vtd = BLI_ghash_lookup(td->gh, v)) == NULL && bm != NULL) {
+ vtd = BLI_memarena_alloc(td->ma, sizeof(*vtd));
+ BLI_ghash_insert(td->gh, v, vtd);
+ td->len++;
+ is_new = 1;
+ }
+
+ vtd->bm = bm;
+ vtd->v = v;
+
+ if (co != NULL) {
+ copy_v3_v3(vtd->co, co);
+ }
+
+ if (org == NULL && is_new) {
+ copy_v3_v3(vtd->org, v->co); /* default */
+ }
+ else if (org != NULL) {
+ copy_v3_v3(vtd->org, org);
+ }
+
+ if (vec != NULL) {
+ copy_v3_v3(vtd->vec, vec);
+ normalize_v3(vtd->vec);
+ }
+
+ vtd->loc = loc;
+
+ vtd->factor = factor;
+ vtd->weight = weight;
+ vtd->maxfactor = maxfactor;
+ vtd->max = max;
+
+ return vtd;
+}
+
+BME_TransData *BME_get_transdata(BME_TransData_Head *td, BMVert *v)
+{
+ BME_TransData *vtd;
+ vtd = BLI_ghash_lookup(td->gh, v);
+ return vtd;
+}
+
+/* a hack (?) to use the transdata memarena to allocate floats for use with the max limits */
+float *BME_new_transdata_float(BME_TransData_Head *td)
+{
+ return BLI_memarena_alloc(td->ma, sizeof(float));
+}
+
+/* BM_disk_dissolve is a real mess, and crashes bevel if called instead of this.
+ * The drawback, though, is that this code doesn't merge customdata. */
+static int BME_Bevel_Dissolve_Disk(BMesh *bm, BMVert *v)
+{
+ BMIter iter;
+ BMEdge *e, *elast;
+ BMLoop *l1, *l2;
+
+ if (!BM_vert_is_manifold(bm, v)) {
+ return 0;
+ }
+
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) {
+ if (BM_edge_face_count(e) != 2) {
+ return 0;
+ }
+ }
+
+ if (BM_vert_edge_count(v) > 2) {
+ while (BM_vert_edge_count(v) > 2) {
+ e = v->e;
+ l1 = e->l;
+ l2 = l1->radial_next;
+ bmesh_jfke(bm, l1->f, l2->f, e);
+ }
+
+ e = v->e;
+ elast = bmesh_disk_nextedge(e, v);
+ bmesh_jekv(bm, e, v);
+
+ l1 = elast->l;
+ l2 = l1->radial_next;
+ bmesh_jfke(bm, l1->f, l2->f, elast);
+ }
+
+ return 1;
+}
+
+static int BME_bevel_is_split_vert(BMesh *bm, BMLoop *l)
+{
+ /* look for verts that have already been added to the edge when
+ * beveling other polys; this can be determined by testing the
+ * vert and the edges around it for originality
+ */
+ if ( !BMO_elem_flag_test(bm, l->v, BME_BEVEL_ORIG) &&
+ BMO_elem_flag_test(bm, l->e, BME_BEVEL_ORIG) &&
+ BMO_elem_flag_test(bm, l->prev->e, BME_BEVEL_ORIG))
+ {
+ return 1;
+ }
+ return 0;
+}
+
+/* get a vector, vec, that points from v1->co to wherever makes sense to
+ * the bevel operation as a whole based on the relationship between v1 and v2
+ * (won't necessarily be a vec from v1->co to v2->co, though it probably will be);
+ * the return value is -1 for failure, 0 if we used vert co's, and 1 if we used transform origins */
+static int BME_bevel_get_vec(float *vec, BMVert *v1, BMVert *v2, BME_TransData_Head *td)
+{
+ BME_TransData *vtd1, *vtd2;
+
+ vtd1 = BME_get_transdata(td, v1);
+ vtd2 = BME_get_transdata(td, v2);
+ if (!vtd1 || !vtd2) {
+ //printf("BME_bevel_get_vec() got called without proper BME_TransData\n");
+ return -1;
+ }
+
+ /* compare the transform origins to see if we can use the vert co's;
+ * if they belong to different origins, then we will use the origins to determine
+ * the vector */
+ if (compare_v3v3(vtd1->org, vtd2->org, 0.000001f)) {
+ sub_v3_v3v3(vec, v2->co, v1->co);
+ if (len_v3(vec) < 0.000001f) {
+ zero_v3(vec);
+ }
+ return 0;
+ }
+ else {
+ sub_v3_v3v3(vec, vtd2->org, vtd1->org);
+ if (len_v3(vec) < 0.000001f) {
+ zero_v3(vec);
+ }
+ return 1;
+ }
+}
+
+/* "Projects" a vector perpendicular to vec2 against vec1, such that
+ * the projected vec1 + vec2 has a min distance of 1 from the "edge" defined by vec2.
+ * note: the direction, is_forward, is used in conjunction with up_vec to determine
+ * whether this is a convex or concave corner. If it is a concave corner, it will
+ * be projected "backwards." If vec1 is before vec2, is_forward should be 0 (we are projecting backwards).
+ * vec1 is the vector to project onto (expected to be normalized)
+ * vec2 is the direction of projection (pointing away from vec1)
+ * up_vec is used for orientation (expected to be normalized)
+ * returns the length of the projected vector that lies along vec1 */
+static float BME_bevel_project_vec(float *vec1, float *vec2, float *up_vec, int is_forward, BME_TransData_Head *UNUSED(td))
+{
+ float factor, vec3[3], tmp[3], c1, c2;
+
+ cross_v3_v3v3(tmp, vec1, vec2);
+ normalize_v3(tmp);
+ factor = dot_v3v3(up_vec, tmp);
+ if ((factor > 0 && is_forward) || (factor < 0 && !is_forward)) {
+ cross_v3_v3v3(vec3, vec2, tmp); /* hmm, maybe up_vec should be used instead of tmp */
+ }
+ else {
+ cross_v3_v3v3(vec3, tmp, vec2); /* hmm, maybe up_vec should be used instead of tmp */
+ }
+ normalize_v3(vec3);
+ c1 = dot_v3v3(vec3, vec1);
+ c2 = dot_v3v3(vec1, vec1);
+ if (fabsf(c1) < 0.000001f || fabsf(c2) < 0.000001f) {
+ factor = 0.0f;
+ }
+ else {
+ factor = c2 / c1;
+ }
+
+ return factor;
+}
+
+/* BME_bevel_split_edge() is the main math work-house; its responsibilities are:
+ * using the vert and the loop passed, get or make the split vert, set its coordinates
+ * and transform properties, and set the max limits.
+ * Finally, return the split vert. */
+static BMVert *BME_bevel_split_edge(BMesh *bm, BMVert *v, BMVert *v1, BMLoop *l, float *up_vec, float value, BME_TransData_Head *td)
+{
+ BME_TransData *vtd, *vtd1, *vtd2;
+ BMVert *sv, *v2, *v3, *ov;
+ BMLoop *lv1, *lv2;
+ BMEdge *ne, *e1, *e2;
+ float maxfactor, scale, len, dis, vec1[3], vec2[3], t_up_vec[3];
+ int is_edge, forward, is_split_vert;
+
+ if (l == NULL) {
+ /* what you call operator overloading in C :)
+ * I wanted to use the same function for both wire edges and poly loops
+ * so... here we walk around edges to find the needed verts */
+ forward = 1;
+ is_split_vert = 0;
+ if (v->e == NULL) {
+ //printf("We can't split a loose vert's edge!\n");
+ return NULL;
+ }
+ e1 = v->e; /* we just use the first two edges */
+ e2 = bmesh_disk_nextedge(v->e, v);
+ if (e1 == e2) {
+ //printf("You need at least two edges to use BME_bevel_split_edge()\n");
+ return NULL;
+ }
+ v2 = BM_edge_other_vert(e1, v);
+ v3 = BM_edge_other_vert(e2, v);
+ if (v1 != v2 && v1 != v3) {
+ //printf("Error: more than 2 edges in v's disk cycle, or v1 does not share an edge with v\n");
+ return NULL;
+ }
+ if (v1 == v2) {
+ v2 = v3;
+ }
+ else {
+ e1 = e2;
+ }
+ ov = BM_edge_other_vert(e1, v);
+ sv = BM_edge_split(bm, v, e1, &ne, 0);
+ //BME_data_interp_from_verts(bm, v, ov, sv, 0.25); /* this is technically wrong.. */
+ //BME_data_interp_from_faceverts(bm, v, ov, sv, 0.25);
+ //BME_data_interp_from_faceverts(bm, ov, v, sv, 0.25);
+ BME_assign_transdata(td, bm, sv, sv->co, sv->co, NULL, sv->co, 0, -1, -1, NULL); /* quick default */
+ BMO_elem_flag_enable(bm, sv, BME_BEVEL_BEVEL);
+ BMO_elem_flag_enable(bm, ne, BME_BEVEL_ORIG); /* mark edge as original, even though it isn't */
+ BME_bevel_get_vec(vec1, v1, v, td);
+ BME_bevel_get_vec(vec2, v2, v, td);
+ cross_v3_v3v3(t_up_vec, vec1, vec2);
+ normalize_v3(t_up_vec);
+ up_vec = t_up_vec;
+ }
+ else {
+ /* establish loop direction */
+ if (l->v == v) {
+ forward = 1;
+ lv1 = l->next;
+ lv2 = l->prev;
+ v1 = l->next->v;
+ v2 = l->prev->v;
+ }
+ else if (l->next->v == v) {
+ forward = 0;
+ lv1 = l;
+ lv2 = l->next->next;
+ v1 = l->v;
+ v2 = l->next->next->v;
+ }
+ else {
+ //printf("ERROR: BME_bevel_split_edge() - v must be adjacent to l\n");
+ return NULL;
+ }
+
+ if (BME_bevel_is_split_vert(bm, lv1)) {
+ is_split_vert = 1;
+ sv = v1;
+ v1 = forward ? l->next->next->v : l->prev->v;
+ }
+ else {
+ is_split_vert = 0;
+ ov = BM_edge_other_vert(l->e, v);
+ sv = BM_edge_split(bm, v, l->e, &ne, 0);
+ //BME_data_interp_from_verts(bm, v, ov, sv, 0.25); /* this is technically wrong.. */
+ //BME_data_interp_from_faceverts(bm, v, ov, sv, 0.25);
+ //BME_data_interp_from_faceverts(bm, ov, v, sv, 0.25);
+ BME_assign_transdata(td, bm, sv, sv->co, sv->co, NULL, sv->co, 0, -1, -1, NULL); /* quick default */
+ BMO_elem_flag_enable(bm, sv, BME_BEVEL_BEVEL);
+ BMO_elem_flag_enable(bm, ne, BME_BEVEL_ORIG); /* mark edge as original, even though it isn't */
+ }
+
+ if (BME_bevel_is_split_vert(bm, lv2)) {
+ v2 = forward ? lv2->prev->v : lv2->next->v;
+ }
+ }
+
+ is_edge = BME_bevel_get_vec(vec1, v, v1, td); /* get the vector we will be projecting onto */
+ BME_bevel_get_vec(vec2, v, v2, td); /* get the vector we will be projecting parallel to */
+ len = len_v3(vec1);
+ normalize_v3(vec1);
+
+ vtd = BME_get_transdata(td, sv);
+ vtd1 = BME_get_transdata(td, v);
+ vtd2 = BME_get_transdata(td, v1);
+
+ if (vtd1->loc == NULL) {
+ /* this is a vert with data only for calculating initial weights */
+ if (vtd1->weight < 0) {
+ vtd1->weight = 0;
+ }
+ scale = vtd1->weight / vtd1->factor;
+ if (!vtd1->max) {
+ vtd1->max = BME_new_transdata_float(td);
+ *vtd1->max = -1;
+ }
+ }
+ else {
+ scale = vtd1->weight;
+ }
+ vtd->max = vtd1->max;
+
+ if (is_edge && vtd1->loc != NULL) {
+ maxfactor = vtd1->maxfactor;
+ }
+ else {
+ maxfactor = scale * BME_bevel_project_vec(vec1, vec2, up_vec, forward, td);
+ if (vtd->maxfactor > 0 && vtd->maxfactor < maxfactor) {
+ maxfactor = vtd->maxfactor;
+ }
+ }
+
+ dis = BMO_elem_flag_test(bm, v1, BME_BEVEL_ORIG) ? len / 3 : len / 2;
+ if (is_edge || dis > maxfactor * value) {
+ dis = maxfactor * value;
+ }
+ madd_v3_v3v3fl(sv->co, v->co, vec1, dis);
+ sub_v3_v3v3(vec1, sv->co, vtd1->org);
+ dis = len_v3(vec1);
+ normalize_v3(vec1);
+ BME_assign_transdata(td, bm, sv, vtd1->org, vtd1->org, vec1, sv->co, dis, scale, maxfactor, vtd->max);
+
+ return sv;
+}
+
+#if 0 /* UNUSED */
+static float BME_bevel_set_max(BMVert *v1, BMVert *v2, float value, BME_TransData_Head *td)
+{
+ BME_TransData *vtd1, *vtd2;
+ float max, fac1, fac2, vec1[3], vec2[3], vec3[3];
+
+ BME_bevel_get_vec(vec1, v1, v2, td);
+ vtd1 = BME_get_transdata(td, v1);
+ vtd2 = BME_get_transdata(td, v2);
+
+ if (vtd1->loc == NULL) {
+ fac1 = 0;
+ }
+ else {
+ copy_v3_v3(vec2, vtd1->vec);
+ mul_v3_fl(vec2, vtd1->factor);
+ if (dot_v3v3(vec1, vec1)) {
+ project_v3_v3v3(vec2, vec2, vec1);
+ fac1 = len_v3(vec2) / value;
+ }
+ else {
+ fac1 = 0;
+ }
+ }
+
+ if (vtd2->loc == NULL) {
+ fac2 = 0;
+ }
+ else {
+ copy_v3_v3(vec3, vtd2->vec);
+ mul_v3_fl(vec3, vtd2->factor);
+ if (dot_v3v3(vec1, vec1)) {
+ project_v3_v3v3(vec2, vec3, vec1);
+ fac2 = len_v3(vec2) / value;
+ }
+ else {
+ fac2 = 0;
+ }
+ }
+
+ if (fac1 || fac2) {
+ max = len_v3(vec1) / (fac1 + fac2);
+ if (vtd1->max && (*vtd1->max < 0 || max < *vtd1->max)) {
+ *vtd1->max = max;
+ }
+ if (vtd2->max && (*vtd2->max < 0 || max < *vtd2->max)) {
+ *vtd2->max = max;
+ }
+ }
+ else {
+ max = -1;
+ }
+
+ return max;
+}
+#endif
+
+#if 0 /* UNUSED */
+static BMVert *BME_bevel_wire(BMesh *bm, BMVert *v, float value, int res, int UNUSED(options), BME_TransData_Head *td)
+{
+ BMVert *ov1, *ov2, *v1, *v2;
+
+ ov1 = BM_edge_other_vert(v->e, v);
+ ov2 = BM_edge_other_vert(bmesh_disk_nextedge(v->e, v), v);
+
+ /* split the edges */
+ v1 = BME_bevel_split_edge(bm, v, ov1, NULL, NULL, value, td);
+ BMO_elem_flag_enable(bm, v1, BME_BEVEL_NONMAN);
+ v2 = BME_bevel_split_edge(bm, v, ov2, NULL, NULL, value, td);
+ BMO_elem_flag_enable(bm, v2, BME_BEVEL_NONMAN);
+
+ if (value > 0.5) {
+ BME_bevel_set_max(v1, ov1, value, td);
+ BME_bevel_set_max(v2, ov2, value, td);
+ }
+
+ /* remove the original vert */
+ if (res) {
+ /* bmesh_jekv; */
+
+ //void BM_vert_collapse_faces(BMesh *bm, BMEdge *ke, BMVert *kv, float fac, int calcnorm){
+ //hrm, why is there a fac here? it just removes a vert
+ BM_vert_collapse_edges(bm, v->e, v);
+ }
+
+ return v1;
+}
+#endif
+
+static BMLoop *BME_bevel_edge(BMesh *bm, BMLoop *l, float value, int UNUSED(options), float *up_vec, BME_TransData_Head *td)
+{
+ BMVert *v1, *v2, *kv;
+ BMLoop *kl = NULL, *nl;
+ BMEdge *e, *ke, *se;
+ BMFace *f, *jf;
+
+ f = l->f;
+ e = l->e;
+
+ /* sanity check */
+ if ( !BMO_elem_flag_test(bm, l->e, BME_BEVEL_BEVEL) &&
+ (BMO_elem_flag_test(bm, l->v, BME_BEVEL_BEVEL) || BMO_elem_flag_test(bm, l->next->v, BME_BEVEL_BEVEL)))
+ {
+ return l;
+ }
+
+ /* checks and operations for prev edge */
+ /* first, check to see if this edge was inset previously */
+ if ( !BMO_elem_flag_test(bm, l->prev->e, BME_BEVEL_ORIG) &&
+ !BMO_elem_flag_test(bm, l->v, BME_BEVEL_NONMAN))
+ {
+ kl = l->prev->radial_next;
+ kl = (kl->v == l->v) ? kl->prev : kl->next;
+ kv = l->v;
+ }
+ else {
+ kv = NULL;
+ }
+ /* get/make the first vert to be used in SFME */
+ if (BMO_elem_flag_test(bm, l->v, BME_BEVEL_NONMAN)) {
+ v1 = l->v;
+ }
+ else { /* we'll need to split the previous edge */
+ v1 = BME_bevel_split_edge(bm, l->v, NULL, l->prev, up_vec, value, td);
+ }
+ /* if we need to clean up geometry... */
+ if (kv) {
+ se = l->next->e;
+ jf = NULL;
+ if (kl->v == kv) {
+ BM_face_split(bm, kl->f, kl->prev->v, kl->next->v, &nl, kl->prev->e);
+ ke = kl->e;
+ /* BMESH-TODO: jfke doesn't handle customdata */
+ jf = bmesh_jfke(bm, kl->prev->radial_next->f, kl->f, kl->prev->e);
+ BM_vert_collapse_edges(bm, ke, kv);
+ }
+ else {
+ BM_face_split(bm, kl->f, kl->next->next->v, kl->v, &nl, kl->next->e);
+ ke = kl->e;
+ /* BMESH-TODO: jfke doesn't handle customdata */
+ jf = bmesh_jfke(bm, kl->next->radial_next->f, kl->f, kl->next->e);
+ BM_vert_collapse_edges(bm, ke, kv);
+ }
+ /* find saved loop pointer */
+ l = se->l;
+ while (l->f != jf) {
+ l = bmesh_radial_nextloop(l);
+ BLI_assert(l != se->l);
+ }
+ l = l->prev;
+ }
+
+ /* checks and operations for the next edge */
+ /* first, check to see if this edge was inset previously */
+ if ( !BMO_elem_flag_test(bm, l->next->e, BME_BEVEL_ORIG) &&
+ !BMO_elem_flag_test(bm, l->next->v, BME_BEVEL_NONMAN))
+ {
+ kl = l->next->radial_next;
+ kl = (kl->v == l->next->v) ? kl->prev : kl->next;
+ kv = l->next->v;
+ }
+ else {
+ kv = NULL;
+ }
+ /* get/make the second vert to be used in SFME */
+ if (BMO_elem_flag_test(bm, l->next->v, BME_BEVEL_NONMAN)) {
+ v2 = l->next->v;
+ }
+ else { /* we'll need to split the next edge */
+ v2 = BME_bevel_split_edge(bm, l->next->v, NULL, l->next, up_vec, value, td);
+ }
+ /* if we need to clean up geometry... */
+ if (kv) {
+ se = l->e;
+ jf = NULL;
+ if (kl->v == kv) {
+ BM_face_split(bm, kl->f, kl->prev->v, kl->next->v, &nl, kl->prev->e);
+ ke = kl->e;
+ /* BMESH-TODO: jfke doesn't handle customdata */
+ jf = bmesh_jfke(bm, kl->prev->radial_next->f, kl->f, kl->prev->e);
+ BM_vert_collapse_edges(bm, ke, kv);
+ }
+ else {
+ BM_face_split(bm, kl->f, kl->next->next->v, kl->v, &nl, kl->next->e);
+ ke = kl->e;
+ /* BMESH-TODO: jfke doesn't handle customdata */
+ jf = bmesh_jfke(bm, kl->next->radial_next->f, kl->f, kl->next->e);
+ BM_vert_collapse_edges(bm, ke, kv);
+ }
+ /* find saved loop pointer */
+ l = se->l;
+ while (l->f != jf) {
+ l = bmesh_radial_nextloop(l);
+ BLI_assert(l != se->l);
+ }
+ }
+
+ if (!BMO_elem_flag_test(bm, v1, BME_BEVEL_NONMAN) || !BMO_elem_flag_test(bm, v2, BME_BEVEL_NONMAN)) {
+ BM_face_split(bm, f, v2, v1, &l, e);
+ BMO_elem_flag_enable(bm, l->e, BME_BEVEL_BEVEL);
+ l = l->radial_next;
+ }
+
+ if (l->f != f){
+ //printf("Whoops! You got something out of order in BME_bevel_edge()!\n");
+ }
+
+ return l;
+}
+
+static BMLoop *BME_bevel_vert(BMesh *bm, BMLoop *l, float value, int UNUSED(options), float *up_vec, BME_TransData_Head *td)
+{
+ BMVert *v1, *v2;
+ BMFace *f;
+
+ /* get/make the first vert to be used in SFME */
+ /* may need to split the previous edge */
+ v1 = BME_bevel_split_edge(bm, l->v, NULL, l->prev, up_vec, value, td);
+
+ /* get/make the second vert to be used in SFME */
+ /* may need to split this edge (so move l) */
+ l = l->prev;
+ v2 = BME_bevel_split_edge(bm, l->next->v, NULL, l->next, up_vec, value, td);
+ l = l->next->next;
+
+ /* "cut off" this corner */
+ f = BM_face_split(bm, l->f, v2, v1, NULL, l->e);
+
+ return l;
+}
+
+/*
+ * BME_bevel_poly
+ *
+ * Polygon inset tool:
+ *
+ * Insets a polygon/face based on the flagss of its vertices
+ * and edges. Used by the bevel tool only, for now.
+ * The parameter "value" is the distance to inset (should be negative).
+ * The parameter "options" is not currently used.
+ *
+ * Returns -
+ * A BMFace pointer to the resulting inner face.
+ */
+static BMFace *BME_bevel_poly(BMesh *bm, BMFace *f, float value, int options, BME_TransData_Head *td)
+{
+ BMLoop *l/*, *o */;
+ BME_TransData *vtd1, *vtd2;
+ float up_vec[3], vec1[3], vec2[3], vec3[3], fac1, fac2, max = -1;
+ int len, i;
+ BMIter iter;
+
+ zero_v3(up_vec);
+
+ /* find a good normal for this face (there's better ways, I'm sure) */
+ BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) {
+ BME_bevel_get_vec(vec1, l->v, l->next->v, td);
+ BME_bevel_get_vec(vec2, l->prev->v, l->v, td);
+ cross_v3_v3v3(vec3, vec2, vec1);
+ add_v3_v3(up_vec, vec3);
+ }
+ normalize_v3(up_vec);
+
+ /* Can't use a BM_LOOPS_OF_FACE iterator here, because the loops are being modified
+ * and so the end condition will never hi */
+ for (l = BM_FACE_FIRST_LOOP(f)->prev, i = 0, len = f->len; i < len; i++, l = l->next) {
+ if (BMO_elem_flag_test(bm, l->e, BME_BEVEL_BEVEL) && BMO_elem_flag_test(bm, l->e, BME_BEVEL_ORIG)) {
+ max = 1.0f;
+ l = BME_bevel_edge(bm, l, value, options, up_vec, td);
+ }
+ else if ( BMO_elem_flag_test(bm, l->v, BME_BEVEL_BEVEL) &&
+ BMO_elem_flag_test(bm, l->v, BME_BEVEL_ORIG) &&
+ !BMO_elem_flag_test(bm, l->prev->e, BME_BEVEL_BEVEL))
+ {
+ max = 1.0f;
+ l = BME_bevel_vert(bm, l, value, options, up_vec, td);
+ }
+ }
+
+ f = l->f;
+
+ /* max pass */
+ if (value > 0.5f && max > 0) {
+ max = -1;
+ BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) {
+ if (BMO_elem_flag_test(bm, l->e, BME_BEVEL_BEVEL) || BMO_elem_flag_test(bm, l->e, BME_BEVEL_ORIG)) {
+ BME_bevel_get_vec(vec1, l->v, l->next->v, td);
+ vtd1 = BME_get_transdata(td, l->v);
+ vtd2 = BME_get_transdata(td, l->next->v);
+ if (vtd1->loc == NULL) {
+ fac1 = 0;
+ }
+ else {
+ copy_v3_v3(vec2, vtd1->vec);
+ mul_v3_fl(vec2, vtd1->factor);
+ if (dot_v3v3(vec1, vec1)) {
+ project_v3_v3v3(vec2, vec2, vec1);
+ fac1 = len_v3(vec2) / value;
+ }
+ else {
+ fac1 = 0;
+ }
+ }
+ if (vtd2->loc == NULL) {
+ fac2 = 0;
+ }
+ else {
+ copy_v3_v3(vec3, vtd2->vec);
+ mul_v3_fl(vec3, vtd2->factor);
+ if (dot_v3v3(vec1, vec1)) {
+ project_v3_v3v3(vec2, vec3, vec1);
+ fac2 = len_v3(vec2) / value;
+ }
+ else {
+ fac2 = 0;
+ }
+ }
+ if (fac1 || fac2) {
+ max = len_v3(vec1)/(fac1 + fac2);
+ if (vtd1->max && (*vtd1->max < 0 || max < *vtd1->max)) {
+ *vtd1->max = max;
+ }
+ if (vtd2->max && (*vtd2->max < 0 || max < *vtd2->max)) {
+ *vtd2->max = max;
+ }
+ }
+ }
+ }
+ }
+
+ /* return l->f; */
+ return NULL;
+}
+
+static void BME_bevel_add_vweight(BME_TransData_Head *td, BMesh *bm, BMVert *v, float weight, float factor, int options)
+{
+ BME_TransData *vtd;
+
+ if (BMO_elem_flag_test(bm, v, BME_BEVEL_NONMAN)) return;
+ BMO_elem_flag_enable(bm, v, BME_BEVEL_BEVEL);
+ if ((vtd = BME_get_transdata(td, v))) {
+ if (options & BME_BEVEL_EMIN) {
+ vtd->factor = 1.0;
+ if (vtd->weight < 0 || weight < vtd->weight) {
+ vtd->weight = weight;
+ }
+ }
+ else if (options & BME_BEVEL_EMAX) {
+ vtd->factor = 1.0;
+ if (weight > vtd->weight) {
+ vtd->weight = weight;
+ }
+ }
+ else if (vtd->weight < 0) {
+ vtd->factor = factor;
+ vtd->weight = weight;
+ }
+ else {
+ vtd->factor += factor; /* increment number of edges with weights (will be averaged) */
+ vtd->weight += weight; /* accumulate all the weights */
+ }
+ }
+ else {
+ /* we'll use vtd->loc == NULL to mark that this vert is not moving */
+ vtd = BME_assign_transdata(td, bm, v, v->co, NULL, NULL, NULL, factor, weight, -1, NULL);
+ }
+}
+
+static void bevel_init_verts(BMesh *bm, int options, BME_TransData_Head *td)
+{
+ BMVert *v;
+ BMIter iter;
+ float weight;
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ weight = 0.0;
+ if (!BMO_elem_flag_test(bm, v, BME_BEVEL_NONMAN)) {
+ /* modifiers should not use selection */
+ if(options & BME_BEVEL_SELECT){
+ if(BM_elem_flag_test(v, BM_ELEM_SELECT)) weight = 1.0;
+ }
+ /* bevel weight NYI */
+ else if(options & BME_BEVEL_WEIGHT)
+ weight = BM_elem_float_data_get(&bm->vdata, v, CD_BWEIGHT);
+ else
+ weight = 1.0;
+ if(weight > 0.0){
+ BMO_elem_flag_enable(bm, v, BME_BEVEL_BEVEL);
+ BME_assign_transdata(td, bm, v, v->co, v->co, NULL, NULL, 1.0, weight, -1, NULL);
+ }
+ }
+ }
+}
+
+static void bevel_init_edges(BMesh *bm, int options, BME_TransData_Head *td)
+{
+ BMEdge *e;
+ int count;
+ float weight;
+ BMIter iter;
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ weight = 0.0;
+ if (!BMO_elem_flag_test(bm, e, BME_BEVEL_NONMAN)) {
+ if(options & BME_BEVEL_SELECT){
+ if(BM_elem_flag_test(e, BM_ELEM_SELECT)) weight = 1.0;
+ }
+ else if(options & BME_BEVEL_WEIGHT) {
+ weight = BM_elem_float_data_get(&bm->edata, e, CD_BWEIGHT);
+ }
+ else {
+ weight = 1.0;
+ }
+ if(weight > 0.0){
+ BMO_elem_flag_enable(bm, e, BME_BEVEL_BEVEL);
+ BMO_elem_flag_enable(bm, e->v1, BME_BEVEL_BEVEL);
+ BMO_elem_flag_enable(bm, e->v2, BME_BEVEL_BEVEL);
+ BME_bevel_add_vweight(td, bm, e->v1, weight, 1.0, options);
+ BME_bevel_add_vweight(td, bm, e->v2, weight, 1.0, options);
+ }
+ }
+ }
+
+ /* clean up edges with 2 faces that share more than one edg */
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if(BMO_elem_flag_test(bm, e, BME_BEVEL_BEVEL)) {
+ count = BM_face_share_edges(e->l->f, e->l->radial_next->f);
+ if(count > 1) BMO_elem_flag_disable(bm, e, BME_BEVEL_BEVEL);
+ }
+ }
+}
+
+static BMesh *BME_bevel_initialize(BMesh *bm, int options, int UNUSED(defgrp_index), float UNUSED(angle), BME_TransData_Head *td)
+{
+ BMVert *v/*, *v2 */;
+ BMEdge *e/*, *curedg */;
+ BMFace *f;
+ BMIter iter;
+ int /* wire, */ len;
+
+ /* tag non-manifold geometr */
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ BMO_elem_flag_enable(bm, v, BME_BEVEL_ORIG);
+ if(v->e){
+ BME_assign_transdata(td, bm, v, v->co, v->co, NULL, NULL, 0, -1, -1, NULL);
+ if (!BM_vert_is_manifold(bm, v))
+ BMO_elem_flag_enable(bm, v, BME_BEVEL_NONMAN);
+ /* test wire ver */
+ len = BM_vert_edge_count(v);
+ if (len == 2 && BM_vert_is_wire(bm, v))
+ BMO_elem_flag_disable(bm, v, BME_BEVEL_NONMAN);
+ }
+ else
+ BMO_elem_flag_enable(bm, v, BME_BEVEL_NONMAN);
+ }
+
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ BMO_elem_flag_enable(bm, e, BME_BEVEL_ORIG);
+ if (!BM_edge_is_manifold(bm, e)) {
+ BMO_elem_flag_enable(bm, e->v1, BME_BEVEL_NONMAN);
+ BMO_elem_flag_enable(bm, e->v2, BME_BEVEL_NONMAN);
+ BMO_elem_flag_enable(bm, e, BME_BEVEL_NONMAN);
+ }
+ if(BMO_elem_flag_test(bm, e->v1, BME_BEVEL_NONMAN) || BMO_elem_flag_test(bm, e->v2, BME_BEVEL_NONMAN)) BMO_elem_flag_enable(bm, e, BME_BEVEL_NONMAN);
+ }
+
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL)
+ BMO_elem_flag_enable(bm, f, BME_BEVEL_ORIG);
+ if(options & BME_BEVEL_VERT) bevel_init_verts(bm, options, td);
+ else bevel_init_edges(bm, options, td);
+ return bm;
+
+}
+
+#if 0
+
+static BMesh *BME_bevel_reinitialize(BMesh *bm)
+{
+ BMVert *v;
+ BMEdge *e;
+ BMFace *f;
+ BMIter iter;
+
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ BMO_elem_flag_enable(bm, v, BME_BEVEL_ORIG);
+ }
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ BMO_elem_flag_enable(bm, e, BME_BEVEL_ORIG);
+ }
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ BMO_elem_flag_enable(bm, f, BME_BEVEL_ORIG);
+ }
+ return bm;
+
+}
+
+#endif
+
+/**
+ * BME_bevel_mesh
+ *
+ * Mesh beveling tool:
+ *
+ * Bevels an entire mesh. It currently uses the flags of
+ * its vertices and edges to track topological changes.
+ * The parameter "value" is the distance to inset (should be negative).
+ * The parameter "options" is not currently used.
+ *
+ * Returns -
+ * A BMesh pointer to the BM passed as a parameter.
+ */
+
+static BMesh *BME_bevel_mesh(BMesh *bm, float value, int UNUSED(res), int options, int UNUSED(defgrp_index), BME_TransData_Head *td)
+{
+ BMVert *v;
+ BMEdge *e, *curedge;
+ BMLoop *l, *l2;
+ BMFace *f;
+ BMIter iter;
+
+ /* unsigned int i, len; */
+
+ /* bevel poly */
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ if(BMO_elem_flag_test(bm, f, BME_BEVEL_ORIG)) {
+ BME_bevel_poly(bm, f, value, options, td);
+ }
+ }
+
+ /* get rid of beveled edge */
+ BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
+ if(BMO_elem_flag_test(bm, e, BME_BEVEL_BEVEL) && BMO_elem_flag_test(bm, e, BME_BEVEL_ORIG)) {
+ BM_faces_join_pair(bm, e->l->f, e->l->radial_next->f, e);
+ }
+ }
+
+ /* link up corners and cli */
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ if(BMO_elem_flag_test(bm, v, BME_BEVEL_ORIG) && BMO_elem_flag_test(bm, v, BME_BEVEL_BEVEL)) {
+ curedge = v->e;
+ do{
+ l = curedge->l;
+ l2 = l->radial_next;
+ if(l->v != v) l = l->next;
+ if(l2->v != v) l2 = l2->next;
+ if(l->f->len > 3)
+ BM_face_split(bm, l->f, l->next->v, l->prev->v, &l, l->e); /* clip this corner off */
+ if(l2->f->len > 3)
+ BM_face_split(bm, l2->f, l2->next->v, l2->prev->v, &l, l2->e); /* clip this corner off */
+ curedge = bmesh_disk_nextedge(curedge, v);
+ } while(curedge != v->e);
+ BME_Bevel_Dissolve_Disk(bm, v);
+ }
+ }
+
+ /* Debug print, remov */
+ BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) {
+ if(f->len == 2){
+ printf("warning");
+ }
+ }
+
+ return bm;
+}
+
+BMesh *BME_bevel(BMEditMesh *em, float value, int res, int options, int defgrp_index, float angle, BME_TransData_Head **rtd)
+{
+ BMesh *bm = em->bm;
+ BMVert *v;
+ BMIter iter;
+
+ BME_TransData_Head *td;
+ BME_TransData *vtd;
+ int i;
+ double fac = 1, d;
+
+ td = BME_init_transdata(BLI_MEMARENA_STD_BUFSIZE);
+ /* recursion math courtesy of Martin Poirier (theeth) */
+ for (i = 0; i < res - 1; i++) {
+ if (i == 0) fac += 1.0f / 3.0f; else fac += 1.0f / (3 * i * 2.0f);
+ }
+ d = 1.0f / fac;
+
+ for (i = 0; i < res || (res == 0 && i == 0); i++) {
+ BMO_push(bm, NULL);
+ BME_bevel_initialize(bm, options, defgrp_index, angle, td);
+ //if (i != 0) BME_bevel_reinitialize(bm);
+ bmesh_begin_edit(bm, 0);
+ BME_bevel_mesh(bm, (float)d, res, options, defgrp_index, td);
+ bmesh_end_edit(bm, 0);
+ d /= (i == 0) ? 3.0 : 2.0;
+ BMO_pop(bm);
+ }
+
+ BMEdit_RecalcTesselation(em);
+
+ /* interactive preview? */
+ if (rtd) {
+ *rtd = td;
+ return bm;
+ }
+
+ /* otherwise apply transforms */
+ BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
+ if ( (vtd = BME_get_transdata(td, v)) ) {
+ if (vtd->max && (*vtd->max > 0 && value > *vtd->max)) {
+ d = *vtd->max;
+ }
+ else {
+ d = value;
+ }
+ madd_v3_v3v3fl(v->co, vtd->org, vtd->vec, vtd->factor * d);
+ }
+ }
+
+ BME_free_transdata(td);
+ return bm;
+}
diff --git a/source/blender/bmesh/tools/BME_dupe_ops.c b/source/blender/bmesh/tools/BME_dupe_ops.c
new file mode 100644
index 00000000000..fb357390ecc
--- /dev/null
+++ b/source/blender/bmesh/tools/BME_dupe_ops.c
@@ -0,0 +1,322 @@
+#if 0
+
+/*
+ * BME_DUPLICATE.C
+ *
+ * This file contains functions for duplicating, copying, and splitting
+ * elements from a bmesh.
+ *
+ */
+
+/*
+ * COPY VERTEX
+ *
+ * Copy an existing vertex from one bmesh to another.
+ *
+*/
+
+static BMVert *copy_vertex(BMMesh *source_mesh, BMVert *source_vertex, BMMesh *target_mesh, GHash *vhash)
+{
+ BMVert *target_vertex = NULL;
+
+ /*create a new vertex*/
+ target_vertex = BM_vert_create(target, source_vertex->co, NULL);
+
+ /*insert new vertex into the vert hash*/
+ BLI_ghash_insert(vhash, source_vertex, target_vertex);
+
+ /*copy custom data in this function since we cannot be assured that byte layout is same between meshes*/
+ CustomData_bmesh_copy_data(&source_mesh->vdata, &target_mesh->vdata, source_vertex->data, &target_vertex->data);
+
+ /*Copy Markings*/
+ if(BM_Is_Selected((BMHeader*)source_vertex)) BM_vert_select_set(target_mesh, target_vertex, TRUE);
+ if(BM_Is_Hidden((BMHeader*)source_vertex)) BM_Mark_Hidden((BMHeader*)target_vertex, 1);
+
+ BMO_elem_flag_enable(target_mesh, (BMHeader*)target_vertex, DUPE_NEW);
+
+ return target_vertex;
+}
+
+/*
+ * COPY EDGE
+ *
+ * Copy an existing edge from one bmesh to another.
+ *
+*/
+
+static BMEdge *copy_edge(BMMesh *source_mesh, BMEdge *source_edge, BMMesh *target_mesh, GHash *vhash, GHash *ehash)
+{
+ BMEdge *target_edge = NULL;
+ BMVert *target_vert1, *target_vert2;
+
+ /*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 edge*/
+ target_edge = BM_edge_create(target_mesh, target_vert1, target_vert2, NULL, FALSE);
+
+ /*insert new edge into the edge hash*/
+ BLI_ghash_insert(ehash, source_edge, target_edge);
+
+ /*copy custom data in this function since we cannot be assured that byte layout is same between meshes*/
+ CustomData_bmesh_copy_data(&source_mesh->edata, &target_mesh->edata, source_edge->data, &target_edge->data);
+
+ /*copy flags*/
+ if(BM_Is_Selected((BMHeader*) source_edge)) BM_edge_select_set(target_mesh, target_edge, TRUE);
+ if(BM_Is_Hidden((BMHeader*) source_edge)) BM_Mark_Hidden(target_mesh, target_edge, 1);
+ if(BM_Is_Sharp((BMHeader*) source_edge)) BM_Mark_Sharp(target_edge, 1);
+ if(BM_Is_Seam((BMHeader*) source_edge)) BM_Mark_Seam(target_edge, 1);
+ if(BM_Is_Fgon((BMHeader*) source_edge)) BM_Mark_Fgon(target_edge, 1);
+
+ BMO_elem_flag_enable(target_mesh, (BMHeader*)target_edge, DUPE_NEW);
+
+ return target_edge;
+}
+
+/*
+ * COPY FACE
+ *
+ * Copy an existing face from one bmesh to another.
+ *
+*/
+
+static BMFace *copy_face(BMMesh *source_mesh, BMFace *source_face, BMMesh *target_mesh, BMEdge **edar, GHash *verthash, GHash *ehash)
+{
+ BMEdge *target_edge;
+ BMVert *target_vert1, *target_vert2;
+ BMLoop *source_loop, *target_loop;
+ BMFace *target_face = NULL;
+ int i;
+
+ /*lookup the first and second verts*/
+ target_vert1 = BLI_ghash_lookup(vhash, source_face->lbase->v);
+ target_vert2 = BLI_ghash_lookup(vhash, source_face->lbase->next->v);
+
+ /*lookup edges*/
+ i = 0;
+ source_loop = source_face->lbase;
+ do{
+ edar[i] = BLI_ghash_lookup(ehash, source_loop->e);
+ i++;
+ source_loop = source_loop->next;
+ }while(source_loop != source_face->lbase);
+
+ /*create new face*/
+ target_face = BM_face_create_ngon(target_mesh, target_vert1, target_vert2, edar, source_face->len, 0);
+
+ /*we copy custom data by hand, we cannot assume that customdata byte layout will be exactly the same....*/
+ CustomData_bmesh_copy_data(&source_mesh->pdata, &target_mesh->pdata, source_face->data, &target_face->data);
+
+ /*copy flags*/
+ if(BM_Is_Selected((BMHeader*)source_face)) BM_Select_face(target, target_face, TRUE);
+ if(BM_Is_Hidden((BMHeader*)source_face)) BM_Mark_Hidden((BMHeader*)target_face, 1);
+
+ /*mark the face for output*/
+ BMO_elem_flag_enable(target_mesh, (BMHeader*)target_face, DUPE_NEW);
+
+ /*copy per-loop custom data*/
+ source_loop = source_face->lbase;
+ target_loop = target_face->lbase;
+ do{
+ CustomData_bmesh_copy_data(&source_mesh->ldata, &target_mesh->ldata, source_loop->data, &target_loop->data);
+ source_loop = source_loop->next;
+ target_loop = target_loop->next;
+ }while(source_loop != source_face->lbase);
+
+ return target_face;
+}
+
+/*
+ * COPY MESH
+ *
+ * Internal Copy function.
+*/
+
+/*local flag defines*/
+
+#define DUPE_INPUT 1 /*input from operator*/
+#define DUPE_NEW 2
+#define DUPE_DONE 3
+
+static void copy_mesh(BMMesh *source, BMMesh *target)
+{
+
+ BMVert *v = NULL;
+ BMEdge *e = NULL, **edar = NULL;
+ BMLoop *l = NULL;
+ BMFace *f = NULL;
+
+ BMIter verts;
+ BMIter edges;
+ BMIter faces;
+ BMIter loops;
+
+ GHash *vhash;
+ GHash *ehash;
+
+ int maxlength = 0, flag;
+
+ /*initialize pointer hashes*/
+ vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
+ ehash = BLI_ghash_new(BLI_ghashutil_ptrrhash, BLI_ghashutil_ptrcmp);
+
+ /*initialize edge pointer array*/
+ for(f = BM_iter_new(&faces, source, BM_FACES, source, 0, NULL); f; f = BM_iter_step(&faces)){
+ if(f->len > maxlength) maxlength = f->len;
+ }
+ edar = MEM_callocN(sizeof(BMEdge*) * maxlength, "BM copy mesh edge pointer array");
+
+
+ /*first we dupe all flagged faces and their elements from source*/
+ for(f = BM_iter_new(&faces, source, BM_FACES, source, 0, NULL); f; f= BM_iter_step(&faces)){
+ if(BMO_elem_flag_test(source, (BMHeader*)f, DUPE_INPUT)){
+ /*vertex pass*/
+ for(v = BM_iter_new(&verts, source, BM_VERT_OF_FACE, f, 0, NULL); v; v = BM_iter_step(&verts)){
+ if(!BMO_elem_flag_test(source, (BMHeader*)v, DUPE_DONE)){
+ copy_vertex(source,v, target, vhash);
+ BMO_elem_flag_enable(source, (BMHeader*)v, DUPE_DONE);
+ }
+ }
+
+ /*edge pass*/
+ for(e = BM_iter_new(&edges, source, BM_EDGE_OF_FACE, f, 0, NULL); e; e = BMeshIter_step(&edges)){
+ if(!BMO_elem_flag_test(source, (BMHeader*)e, DUPE_DONE)){
+ copy_edge(source, e, target, vhash, ehash);
+ BMO_elem_flag_enable(source, (BMHeader*)e, DUPE_DONE);
+ }
+ }
+ copy_face(source, f, target, edar, vhash, ehash);
+ BMO_elem_flag_enable(source, (BMHeader*)f, DUPE_DONE);
+ }
+ }
+
+ /*now we dupe all the edges*/
+ for(e = BM_iter_new(&edges, source, BM_EDGES, source, 0, NULL); e; e = BM_iter_step(&edges)){
+ if(BMO_elem_flag_test(source, (BMHeader*)e, DUPE_INPUT) && (!BMO_elem_flag_test(source, (BMHeader*)e, DUPE_DONE))){
+ /*make sure that verts are copied*/
+ if(!BMO_elem_flag_test(source, (BMHeader*)e->v1, DUPE_DONE){
+ copy_vertex(source, e->v1, target, vhash);
+ BMO_elem_flag_enable(source, (BMHeader*)e->v1, DUPE_DONE);
+ }
+ if(!BMO_elem_flag_test(source, (BMHeader*)e->v2, DUPE_DONE){
+ copy_vertex(source, e->v2, target, vhash);
+ BMO_elem_flag_enable(source, (BMHeader*)e->v2, DUPE_DONE);
+ }
+ /*now copy the actual edge*/
+ copy_edge(source, e, target, vhash, ehash);
+ BMO_elem_flag_enable(source, (BMHeader*)e, DUPE_DONE);
+ }
+ }
+
+ /*finally dupe all loose vertices*/
+ for(v = BM_iter_new(&verts, source, BM_VERTS, source, 0, NULL); v; v = BM_iter_step(&verts)){
+ if(BMO_elem_flag_test(source, (BMHeader*)v, DUPE_INPUT) && (!BMO_elem_flag_test(source, (BMHeader*)v, DUPE_DONE))){
+ copy_vertex(source, v, target, vhash);
+ BMO_elem_flag_enable(source, (BMHeader*)v, DUPE_DONE);
+ }
+ }
+
+ /*free pointer hashes*/
+ BLI_ghash_free(vhash, NULL, NULL);
+ BLI_ghash_free(ehash, NULL, NULL);
+
+ /*free edge pointer array*/
+ if(edar)
+ MEM_freeN(edar);
+}
+/*
+BMMesh *bmesh_make_mesh_from_mesh(BMMesh *bm, int allocsize[4])
+{
+ BMMesh *target = NULL;
+ target = bmesh_make_mesh(allocsize);
+
+
+ CustomData_copy(&bm->vdata, &target->vdata, CD_MASK_BMESH, CD_CALLOC, 0);
+ CustomData_copy(&bm->edata, &target->edata, CD_MASK_BMESH, CD_CALLOC, 0);
+ CustomData_copy(&bm->ldata, &target->ldata, CD_MASK_BMESH, CD_CALLOC, 0);
+ CustomData_copy(&bm->pdata, &target->pdata, CD_MASK_BMESH, CD_CALLOC, 0);
+
+
+ CustomData_bmesh_init_pool(&target->vdata, allocsize[0]);
+ CustomData_bmesh_init_pool(&target->edata, allocsize[1]);
+ CustomData_bmesh_init_pool(&target->ldata, allocsize[2]);
+ CustomData_bmesh_init_pool(&target->pdata, allocsize[3]);
+
+ bmesh_begin_edit(bm);
+ bmesh_begin_edit(target);
+
+ bmesh_copy_mesh(bm, target, 0);
+
+ bmesh_end_edit(bm);
+ bmesh_end_edit(target);
+
+ return target;
+
+}
+*/
+
+void dupeop_exec(BMMesh *bm, BMOperator *op)
+{
+ BMOperator *dupeop = op;
+ BMOpSlot *vinput, *einput, *finput, *vnew, *enew, *fnew;
+ int i;
+
+ vinput = BMO_Get_Slot(dupeop, BMOP_DUPE_VINPUT);
+ einput = BMO_Get_Slot(dupeop, BMOP_DUPE_EINPUT);
+ finput = BMO_Get_Slot(dupeop, BMOP_DUPE_FINPUT);
+
+ /*go through vinput, einput, and finput and flag elements with private flags*/
+ BMO_slot_buffer_flag_enable(bm, dupeop, BMOP_DUPE_VINPUT, DUPE_INPUT);
+ BMO_slot_buffer_flag_enable(bm, dupeop, BMOP_DUPE_EINPUT, DUPE_INPUT);
+ BMO_slot_buffer_flag_enable(bm, dupeop, BMOP_DUPE_FINPUT, DUPE_INPUT);
+
+ /*use the internal copy function*/
+ copy_mesh(bm, bm);
+
+ /*Output*/
+ /*First copy the input buffers to output buffers - original data*/
+ BMO_Copy_Opslot_Buffer_Alloc(dupeop, vinput, BMO_Get_Slot(dupeop, BMOP_DUPE_VORIGINAL));
+ BMO_Copy_Opslot_Buffer_Alloc(dupeop, einput, BMO_Get_Slot(dupeop, BMOP_DUPE_EORIGINAL));
+ BMO_Copy_Opslot_Buffer_Alloc(dupeop, finput, BMO_Get_Slot(dupeop, BMOP_DUPE_FORIGINAL));
+
+ /*Now alloc the new output buffers*/
+ BMO_slot_from_flag(bm, dupeop, BMOP_DUPE_VNEW, DUPE_NEW, BMESH_VERT);
+ BMO_slot_from_flag(bm, dupeop, BMOP_DUPE_ENEW, DUPE_NEW, BMESH_EDGE);
+ BMO_slot_from_flag(bm, dupeop, BMOP_DUPE_FNEW, DUPE_NEW, BMESH_FACE);
+}
+
+void splitop_exec(BMMesh *bm, BMOperator *op)
+{
+ BMOperator *splitop = op;
+ BMOperator dupeop;
+ BMOperator delop;
+
+ /*initialize our sub-operators*/
+ BMO_op_init(&dupeop, BMOP_DUPE);
+ BMO_op_init(&delop, BMOP_DEL);
+
+ BMO_Connect(BMO_Get_Slot(splitop, BMOP_SPLIT_VINPUT),BMO_Get_Slot(&dupeop, BMOP_DUPE_VINPUT));
+ BMO_Connect(BMO_Get_Slot(splitop, BMOP_SPLIT_EINPUT),BMO_Get_Slot(&dupeop, BMOP_DUPE_EINPUT));
+ BMO_Connect(BMO_Get_Slot(splitop, BMOP_SPLIT_FINPUT),BMO_Get_Slot(&dupeop, BMOP_DUPE_FINPUT));
+
+ BMO_op_exec(&dupeop);
+
+ /*connect outputs of dupe to delete*/
+ BMO_Connect(BMO_Get_Slot(&dupeop, BMOP_DUPE_VORIGINAL), BMO_Get_Slot(&delop, BMOP_DEL_VINPUT));
+ BMO_Connect(BMO_Get_Slot(&dupeop, BMOP_DUPE_EORIGINAL), BMO_Get_Slot(&delop, BMOP_DEL_EINPUT));
+ BMO_Connect(BMO_Get_Slot(&dupeop, BMOP_DUPE_FORIGINAL), BMO_Get_Slot(&delop, BMOP_DEL_FINPUT));
+
+ BMO_op_exec(&delop);
+
+ /*now we make our outputs by copying the dupe outputs*/
+ BMO_Copy_Buffer_Alloc(BMO_Get_Slot(&dupeop, BMOP_DUPE_VNEW), BMO_Get_Slot(splitop, BMOP_SPLIT_VOUTPUT));
+ BMO_Copy_Buffer_Alloc(BMO_Get_Slot(&dupeop, BMOP_DUPE_ENEW), BMO_Get_Slot(splitop, BMOP_SPLIT_EOUTPUT));
+ BMO_Copy_Buffer_Alloc(BMO_Get_Slot(&dupeop, BMOP_DUPE_FNEW), BMO_Get_Slot(splitop, BMOP_SPLIT_FOUTPUT));
+
+ /*cleanup*/
+ BMO_op_finish(&dupeop);
+ BMO_op_finish(&delop);
+}
+
+#endif
diff --git a/source/blender/bmesh/tools/BME_duplicate.c b/source/blender/bmesh/tools/BME_duplicate.c
new file mode 100644
index 00000000000..11151de9ed7
--- /dev/null
+++ b/source/blender/bmesh/tools/BME_duplicate.c
@@ -0,0 +1,310 @@
+#if 0
+
+/*
+ * BME_DUPLICATE.C
+ *
+ * This file contains functions for duplicating, copying, and splitting
+ * elements from a bmesh.
+ *
+ */
+
+/*
+ * BMESH COPY VERTEX
+ *
+ * Copy an existing vertex from one bmesh to another.
+ *
+*/
+
+static BMVert *bmesh_copy_vertex(BMMesh *source_mesh, BMVert *source_vertex, BMMesh *target_mesh, GHash *vhash)
+{
+ BMVert *target_vertex = NULL;
+
+ /*create a new vertex*/
+ target_vertex = bmesh_make_vert(target, source_vertex->co, NULL);
+
+ /*insert new vertex into the vert hash*/
+ BLI_ghash_insert(vhash, source_vertex, target_vertex);
+
+ /*copy custom data in this function since we cannot be assured that byte layout is same between meshes*/
+ CustomData_bmesh_copy_data(&source_mesh->vdata, &target_mesh->vdata, source_vertex->data, &target_vertex->data);
+
+ /*copy flags*/
+ if(bmesh_test_flag(source_vertex, BMESH_SELECT)) bmesh_set_flag(target_vertex, BMESH_SELECT);
+ if(bmesh_test_flag(source_vertex, BMESH_HIDDEN)) bmesh_set_flag(target_vertex, BMESH_HIDDEN);
+
+ return target_vertex;
+}
+
+/*
+ * BMESH COPY EDGE
+ *
+ * Copy an existing edge from one bmesh to another.
+ *
+*/
+
+static BMEdge *bmesh_copy_edge(BMMesh *source_mesh, BMEdge *source_edge, BMMesh *target_mesh, GHash *vhash, GHash *ehash)
+{
+ BMEdge *target_edge = NULL;
+ BMVert *target_vert1, *target_vert2;
+
+ /*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 edge*/
+ target_edge = bmesh_make_edge(target_mesh, target_vert1, target_vert2, NULL, 0);
+
+ /*insert new edge into the edge hash*/
+ BLI_ghash_insert(ehash, source_edge, target_edge);
+
+ /*copy custom data in this function since we cannot be assured that byte layout is same between meshes*/
+ CustomData_bmesh_copy_data(&source_mesh->edata, &target_mesh->edata, source_edge->data, &target_edge->data);
+
+ /*copy flags*/
+ if(bmesh_test_flag(source_edge, BMESH_SELECT)) bmesh_set_flag(target_edge, BMESH_SELECT);
+ if(bmesh_test_flag(source_edge, BMESH_HIDDEN)) bmesh_set_flag(target_edge, BMESH_SELECT);
+ if(bmesh_test_flag(source_edge, BMESH_SHARP)) bmesh_set_flag(target_edge, BMESH_SHARP);
+ if(bmesh_test_flag(source_edge, BMESH_SEAM)) bmesh_set_flag(target_edge, BMESH_SEAM);
+ if(bmesh_test_flag(source_edge, BMESH_FGON)) bmesh_set_flag(target_edge, BMESH_FGON);
+
+ return target_edge;
+}
+
+/*
+ * BMESH COPY FACE
+ *
+ * Copy an existing face from one bmesh to another.
+ *
+*/
+
+static BMFace *bmesh_copy_face(BMMesh *source_mesh, BMFace *source_face, BMMesh *target_mesh, BMEdge **edar, GHash *verthash, GHash *ehash)
+{
+ BMEdge *target_edge;
+ BMVert *target_vert1, *target_vert2;
+ BMLoop *source_loop, *target_loop;
+ BMFace *target_face = NULL;
+ int i;
+
+
+ /*lookup the first and second verts*/
+ target_vert1 = BLI_ghash_lookup(vhash, source_face->lbase->v);
+ target_vert2 = BLI_ghash_lookup(vhash, source_face->lbase->next->v);
+
+ /*lookup edges*/
+ i = 0;
+ source_loop = source_face->lbase;
+ do{
+ edar[i] = BLI_ghash_lookup(ehash, source_loop->e);
+ i++;
+ source_loop = source_loop->next;
+ }while(source_loop != source_face->lbase);
+
+ /*create new face*/
+ target_face = bmesh_make_ngon(target_mesh, target_vert1, target_vert2, edar, source_face->len, 0);
+
+ /*we copy custom data by hand, we cannot assume that customdata byte layout will be exactly the same....*/
+ CustomData_bmesh_copy_data(&source_mesh->pdata, &target_mesh->pdata, source_face->data, &target_face->data);
+
+ /*copy flags*/
+ if(bmesh_test_flag(source_face, BMESH_SELECT)) bmesh_set_flag(target_face, BMESH_SELECT);
+ if(bmesh_test_flag(source_face, BMESH_HIDDEN)) bmesh_set_flag(target_face, BMESH_HIDDEN);
+
+ /*mark the face as dirty for normal and tesselation calcs*/
+ bmesh_set_flag(target_face, BMESH_DIRTY);
+
+ /*copy per-loop custom data*/
+ source_loop = source_face->lbase;
+ target_loop = target_face->lbase;
+ do{
+ CustomData_bmesh_copy_data(&source_mesh->ldata, &target_mesh->ldata, source_loop->data, &target_loop->data);
+ source_loop = source_loop->next;
+ target_loop = target_loop->next;
+ }while(source_loop != source_face->lbase);
+
+ return target_face;
+}
+
+/*
+ * BMESH COPY MESH
+ *
+ * Internal Copy function. copies flagged elements from
+ * source to target, which may in fact be the same mesh.
+ * Note that if __flag is 0, all elements will be copied.
+ *
+*/
+
+static void bmesh_copy_mesh(BMMesh *source, BMMesh *target, int __flag)
+{
+
+ BMVert *v;
+ BMEdge *e, **edar;
+ BMLoop *l;
+ BMFace *f;
+
+ BMIter verts;
+ BMIter edges;
+ BMIter faces;
+ BMIter loops;
+
+ GHash *vhash;
+ GHash *ehash;
+
+ int maxlength = 0, flag;
+
+ /*initialize pointer hashes*/
+ vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
+ ehash = BLI_ghash_new(BLI_ghashutil_ptrrhash, BLI_ghashutil_ptrcmp);
+
+ /*initialize edge pointer array*/
+ for(f = BMeshIter_init(faces, BM_FACES, source, 0); f; f = BMeshIter_step(faces)){
+ if(f->len > maxlength) maxlength = f->len;
+ }
+ edar = MEM_callocN(sizeof(BMEdge*) * maxlength, "BM copy mesh edge pointer array");
+
+ /*begin modelling loop for target*/
+ bmesh_begin_edit(target);
+
+ /*we make special exception for __flag == 0... we copy all*/
+ if(!__flag){
+ flag = BMESH_DUPE;
+ for(v = BMeshIter_init(verts, BM_VERTS, source, 0); v; v = BMeshIter_step(verts)) bmesh_set_flag(v, BMESH_DUPE);
+ for(e = BMeshIter_init(verts, BM_EDGES, source, 0); e; e = BMeshIter_step(edges)) bmesh_set_flag(e, BMESH_DUPE);
+ for(f = BMeshIter_init(faces, BM_FACES, source, 0); f; f = BMeshIter_step(faces)) bmesh_set_flag(f, BMESH_DUPE);
+ } else{
+ flag = __flag;
+ }
+
+ /*first we dupe all flagged faces and their elements from source*/
+ for(f = BMeshIter_init(faces, BM_FACES, source, 0); f; f= BMeshIter_step(faces)){
+ if(bmesh_test_flag(f, flag)){
+ /*vertex pass*/
+ for(l = BMeshIter_init(loops, BMESH_LOOP_OF_MESH, f, 0); l; l = BMeshIter_step(loops)){
+ if(!bmesh_test_flag(l->v, BMESH_DUPED)){
+ bmesh_copy_vertex(source,l->v, target, vhash);
+ bmesh_set_flag(l->v, BMESH_DUPED);
+ }
+ }
+
+ /*edge pass*/
+ for(l = BMeshIter_init(loops, BMESH_LOOP_OF_MESH, f, 0); l; l = BMeshIter_step(loops)){
+ if(!bmesh_test_flag(l->e, BMESH_DUPED)){
+ bmesh_copy_edge(source, l->e, target, vhash, ehash);
+ bmesh_set_flag(l->e, BMESH_DUPED);
+ }
+ }
+ bmesh_copy_face(source, f, target, edar, vhash, ehash);
+ bmesh_set_flag(f, BMESH_DUPED);
+ }
+ }
+
+ /*now we dupe all the edges*/
+ for(e = BMeshIter_init(edges, BM_EDGES, source, 0); e; e = BMeshIter_step(edges)){
+ if(bmesh_test_flag(e, flag) && (!bmesh_test_flag(e, BMESH_DUPED))){
+ /*make sure that verts are copied*/
+ if(!bmesh_test_flag(e->v1, BMESH_DUPED)){
+ bmesh_copy_vertex(source, e->v1, target, vhash);
+ bmesh_set_flag(e->v1, BMESH_DUPED);
+ }
+ if(!bmesh_test_flag(e->v2, BMESH_DUPED)){
+ bmesh_copy_vertex(source, e->v2, target, vhash);
+ bmesh_set_flag(e->v2, BMESH_DUPED);
+ }
+ /*now copy the actual edge*/
+ bmesh_copy_edge(source, e, target, vhash, ehash);
+ bmesh_set_flag(e, BMESH_DUPED);
+ }
+ }
+
+ /*finally dupe all loose vertices*/
+ for(v = BMeshIter_init(verts, BM_VERTS, bm, 0); v; v = BMeshIter_step(verts)){
+ if(bmesh_test_flag(v, flag) && (!bmesh_test_flag(v, BMESH_DUPED))){
+ bmesh_copy_vertex(source, v, target, vhash);
+ bmesh_set_flag(v, BMESH_DUPED);
+ }
+ }
+
+ /*finish*/
+ bmesh_end_edit(target, BMESH_CALC_NORM | BMESH_CALC_TESS);
+
+ /*free pointer hashes*/
+ BLI_ghash_free(vhash, NULL, NULL);
+ BLI_ghash_free(ehash, NULL, NULL);
+
+ /*free edge pointer array*/
+ MEM_freeN(edar);
+}
+
+/*
+ * BMESH MAKE MESH FROM MESH
+ *
+ * Creates a new mesh by duplicating an existing one.
+ *
+*/
+
+BMMesh *bmesh_make_mesh_from_mesh(BMMesh *bm, int allocsize[4])
+{
+ BMMesh *target = NULL;
+ target = bmesh_make_mesh(allocsize);
+
+ /*copy custom data layout*/
+ CustomData_copy(&bm->vdata, &target->vdata, CD_MASK_BMESH, CD_CALLOC, 0);
+ CustomData_copy(&bm->edata, &target->edata, CD_MASK_BMESH, CD_CALLOC, 0);
+ CustomData_copy(&bm->ldata, &target->ldata, CD_MASK_BMESH, CD_CALLOC, 0);
+ CustomData_copy(&bm->pdata, &target->pdata, CD_MASK_BMESH, CD_CALLOC, 0);
+
+ /*initialize memory pools*/
+ CustomData_bmesh_init_pool(&target->vdata, allocsize[0]);
+ CustomData_bmesh_init_pool(&target->edata, allocsize[1]);
+ CustomData_bmesh_init_pool(&target->ldata, allocsize[2]);
+ CustomData_bmesh_init_pool(&target->pdata, allocsize[3]);
+
+ bmesh_begin_edit(bm);
+ bmesh_begin_edit(target);
+
+ bmesh_copy_mesh(bm, target, 0); /* copy all elements */
+
+ bmesh_end_edit(bm);
+ bmesh_end_edit(target);
+
+ return target;
+
+}
+
+/*
+ * BMESH SPLIT MESH
+ *
+ * Copies flagged elements then deletes them.
+ *
+*/
+
+void bmesh_split_mesh(BMMesh *bm, int flag)
+{
+ BMVert *v;
+ BMEdge *e;
+ BMFace *f;
+
+ BMIter verts;
+ BMIter edges;
+ BMIter faces;
+
+ bmesh_begin_edit(bm);
+ bmesh_copy_mesh(bm, bm, flag);
+
+ /*mark verts for deletion*/
+ for(v = BMeshIter_init(verts, BM_VERTS, bm, 0); v; v = BMeshIter_step(verts)){
+ if(bmesh_test_flag(v, flag)) bmesh_delete_vert(bm, v);
+ }
+ /*mark edges for deletion*/
+ for(e = BMeshIter_init(edges, BM_EDGES, bm, 0); e; e = BMeshIter_step(edges)){
+ if(bmesh_test_flag(e, flag)) bmesh_delete_edge(bm, e);
+
+ }
+ /*mark faces for deletion*/
+ for(f = BMeshIter_init(faces, BM_FACES, bm, 0); f; f= BMeshIter_step(faces)){
+ if(bmesh_tes t_flag(f, flag)) bmesh_delete_face(bm, f);
+
+ }
+ bmesh_end_edit(bm);
+}
+
+#endif
diff --git a/source/blender/bmesh/tools/BME_weld.c b/source/blender/bmesh/tools/BME_weld.c
new file mode 100644
index 00000000000..fe1a340d94a
--- /dev/null
+++ b/source/blender/bmesh/tools/BME_weld.c
@@ -0,0 +1,341 @@
+#if
+
+/*
+ * BME_WELD.C
+ *
+ * This file contains functions for welding
+ * elements in a mesh togather (remove doubles,
+ * collapse, ect).
+ *
+ * TODO:
+ * -Rewrite this to fit into the new API
+ * -Seperate out find doubles code and put it in
+ * BME_queries.c
+ *
+*/
+
+
+/********* qsort routines *********/
+
+
+typedef struct xvertsort {
+ float x;
+ BMVert *v1;
+} xvertsort;
+
+static int vergxco(const void *v1, const void *v2)
+{
+ const xvertsort *x1=v1, *x2=v2;
+
+ if( x1->x > x2->x ) return 1;
+ else if( x1->x < x2->x) return -1;
+ return 0;
+}
+
+struct facesort {
+ unsigned long x;
+ struct BMFace *f;
+};
+
+
+static int vergface(const void *v1, const void *v2)
+{
+ const struct facesort *x1=v1, *x2=v2;
+
+ if( x1->x > x2->x ) return 1;
+ else if( x1->x < x2->x) return -1;
+ return 0;
+}
+
+
+
+/*break this into two functions.... 'find doubles' and 'remove doubles'?*/
+
+static void BME_remove_doubles__splitface(BME_Mesh *bm,BMFace *f,GHash *vhash)
+{
+ BMVert *doub=NULL, *target=NULL;
+ BME_Loop *l;
+ BMFace *f2=NULL;
+ int split=0;
+
+ l=f->loopbase;
+ do{
+ if(l->v->tflag1 == 2){
+ target = BLI_ghash_lookup(vhash,l->v);
+ if((BME_vert_in_face(target,f)) && (target != l->next->v) && (target != l->prev->v)){
+ doub = l->v;
+ split = 1;
+ break;
+ }
+ }
+
+ l= l->next;
+ }while(l!= f->loopbase);
+
+ if(split){
+ f2 = BME_SFME(bm,f,doub,target,NULL);
+ BME_remove_doubles__splitface(bm,f,vhash);
+ BME_remove_doubles__splitface(bm,f2,vhash);
+ }
+}
+
+int BME_remove_doubles(BME_Mesh *bm, float limit)
+{
+
+ /* all verts with (flag & 'flag') are being evaluated */
+ BMVert *v, *v2, *target;
+ BMEdge *e, **edar, *ne;
+ BME_Loop *l;
+ BMFace *f, *nf;
+ xvertsort *sortblock, *sb, *sb1;
+ struct GHash *vhash;
+ struct facesort *fsortblock, *vsb, *vsb1;
+ int a, b, test, amount=0, found;
+ float dist;
+
+ /*Build a hash table of doubles to thier target vert/edge.*/
+ vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
+
+ /*count amount of selected vertices*/
+ for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){
+ if(BME_SELECTED(v))amount++;
+ }
+
+ /*qsort vertices based upon average of coordinate. We test this way first.*/
+ sb= sortblock= MEM_mallocN(sizeof(xvertsort)*amount,"sortremovedoub");
+
+ for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){
+ if(BME_SELECTED(v)){
+ sb->x = v->co[0]+v->co[1]+v->co[2];
+ sb->v1 = v;
+ sb++;
+ }
+ }
+ qsort(sortblock, amount, sizeof(xvertsort), vergxco);
+
+ /* test for doubles */
+ sb= sortblock;
+ for(a=0; a<amount; a++) {
+ v= sb->v1;
+ if(!(v->tflag1)) { //have we tested yet?
+ sb1= sb+1;
+ for(b=a+1; b<amount; b++) {
+ /* first test: simple distance. Simple way to discard*/
+ dist= sb1->x - sb->x;
+ if(dist > limit) break;
+
+ /* second test: have we already done this vertex?
+ (eh this should be swapped, simple equality test should be cheaper than math above... small savings
+ though) */
+ v2= sb1->v1;
+ if(!(v2->tflag1)) {
+ dist= fabsf(v2->co[0]-v->co[0]);
+ if(dist<=limit) {
+ dist= fabsf(v2->co[1]-v->co[1]);
+ if(dist<=limit) {
+ dist= fabsf(v2->co[2]-v->co[2]);
+ if(dist<=limit) {
+ /*v2 is a double of v. We want to throw out v1 and relink everything to v*/
+ BLI_ghash_insert(vhash,v2, v);
+ v->tflag1 = 1; //mark this vertex as a target
+ v->tflag2++; //increase user count for this vert.
+ v2->tflag1 = 2; //mark this vertex as a double.
+ BME_VISIT(v2); //mark for delete
+ }
+ }
+ }
+ }
+ sb1++;
+ }
+ }
+ sb++;
+ }
+ MEM_freeN(sortblock);
+
+
+ /*todo... figure out what this is for...
+ for(eve = em->verts.first; eve; eve=eve->next)
+ if((eve->f & flag) && (eve->f & 128))
+ EM_data_interp_from_verts(eve, eve->tmp.v, eve->tmp.v, 0.5f);
+ */
+
+ /*We cannot collapse a vertex onto another vertex if they share a face and are not connected via a collapsable edge.
+ so to deal with this we simply find these offending vertices and split the faces. Note that this is not optimal, but works.
+ */
+
+
+ for(f=BME_first(bm,BME_POLY);f;f=BME_next(bm,BME_POLY,f)){
+ if(!(BME_NEWELEM(f))){
+ BME_remove_doubles__splitface(bm,f,vhash);
+ }
+ }
+
+ for(e=BME_first(bm,BME_EDGE);e;e=BME_next(bm,BME_EDGE,e)){
+ /*If either vertices of this edge are a double, we must mark it for removal and we create a new one.*/
+ if(e->v1->tflag1 == 2 || e->v2->tflag1 == 2){
+ v = v2 = NULL;
+ /*For each vertex in the edge, test to find out what it should equal now.*/
+ if(e->v1->tflag1 == 2) v= BLI_ghash_lookup(vhash,e->v1);
+ else v = e->v1;
+ if(e->v2->tflag1 == 2) v2 = BLI_ghash_lookup(vhash,e->v2);
+ else v2 = e->v2;
+
+ /*small optimization, test to see if the edge needs to be rebuilt at all*/
+ if((e->v1 != v) || (e->v2 != v2)){ /*will this always be true of collapsed edges?*/
+ if(v == v2) e->tflag1 = 2; /*mark as a collapsed edge*/
+ else if(!BME_disk_existedge(v,v2)) ne = BME_ME(bm,v,v2);
+ BME_VISIT(e); /*mark for delete*/
+ }
+ }
+ }
+
+
+ /* need to remove double edges as well. To do this we decide on one edge to keep,
+ * and if its inserted into hash then we need to remove all other
+ * edges incident upon and relink.*/
+ /*
+ * REBUILD FACES
+ *
+ * Loop through double faces and if they have vertices that have been flagged, they need to be rebuilt.
+ * We do this by looking up the face rebuild faces.
+ * loop through original face, for each loop, if the edge it is attached to is marked for delete and has no
+ * other edge in the hash edge, then we know to skip that loop on face recreation. Simple.
+ */
+
+ /*1st loop through, just marking elements*/
+ for(f=BME_first(bm,BME_POLY);f;f=BME_next(bm,BME_POLY,f)){ //insert bit here about double edges, mark with a flag (e->tflag2) so that we can nuke it later.
+ l = f->loopbase;
+ do{
+ if(l->v->tflag1 == 2) f->tflag1 = 1; //double, mark for rebuild
+ if(l->e->tflag1 != 2) f->tflag2++; //count number of edges in the new face.
+ l=l->next;
+ }while(l!=f->loopbase);
+ }
+
+ /*now go through and create new faces*/
+ for(f=BME_first(bm,BME_POLY);f;f=BME_next(bm,BME_POLY,f)){
+ if(f->tflag1 && f->tflag2 < 3) BME_VISIT(f); //mark for delete
+ else if (f->tflag1 == 1){ /*is the face marked for rebuild*/
+ edar = MEM_callocN(sizeof(BMEdge *)*f->tflag2,"Remove doubles face creation array.");
+ a=0;
+ l = f->loopbase;
+ do{
+ v = l->v;
+ v2 = l->next->v;
+ if(l->v->tflag1 == 2) v = BLI_ghash_lookup(vhash,l->v);
+ if(l->next->v->tflag1 == 2) v2 = BLI_ghash_lookup(vhash,l->next->v);
+ ne = BME_disk_existedge(v,v2); //use BME_disk_next_edgeflag here or something to find the edge that is marked as 'target'.
+ //add in call here to edge doubles hash array... then bobs your uncle.
+ if(ne){
+ edar[a] = ne;
+ a++;
+ }
+ l=l->next;
+ }while(l!=f->loopbase);
+
+ if(BME_vert_in_edge(edar[1],edar[0]->v2)){
+ v = edar[0]->v1;
+ v2 = edar[0]->v2;
+ }
+ else{
+ v = edar[0]->v2;
+ v2 = edar[0]->v1;
+ }
+
+ nf = BME_MF(bm,v,v2,edar,f->tflag2);
+
+ /*copy per loop data here*/
+ if(nf){
+ BME_VISIT(f); //mark for delete
+ }
+ MEM_freeN(edar);
+ }
+ }
+
+ /*count amount of removed vert doubles*/
+ a = 0;
+ for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){
+ if(v->tflag1 == 2) a++;
+ }
+
+ /*free memory and return amount removed*/
+ remove_tagged_polys(bm);
+ remove_tagged_edges(bm);
+ remove_tagged_verts(bm);
+ BLI_ghash_free(vhash,NULL, NULL);
+ BME_selectmode_flush(bm);
+ return a;
+}
+
+static void BME_MeshWalk__collapsefunc(void *userData, BMEdge *applyedge)
+{
+ int index;
+ GHash *collected = userData;
+ index = BLI_ghash_size(collected);
+ if(!BLI_ghash_lookup(collected,applyedge->v1)){
+ BLI_ghash_insert(collected,index,applyedge->v1);
+ index++;
+ }
+ if(!BLI_ghash_lookup(collected,applyedge->v2)){
+ BLI_ghash_insert(collected,index,applyedge->v2);
+ }
+}
+
+void BME_collapse_edges(BME_Mesh *bm)
+{
+
+ BMVert *v, *cvert;
+ GHash *collected;
+ float min[3], max[3], cent[3];
+ int size, i=0, j, num=0;
+
+ for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){
+ if(!(BME_ISVISITED(v)) && v->edge){
+ /*initiate hash table*/
+ collected = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
+ /*do the walking.*/
+ BME_MeshWalk(bm,v,BME_MeshWalk__collapsefunc,collected,BME_RESTRICTSELECT);
+ /*now loop through the hash table twice, once to calculate bounding box, second time to do the actual collapse*/
+ size = BLI_ghash_size(collected);
+ /*initial values*/
+ copy_v3_v3(min,v->co);
+ copy_v3_v3(max,v->co);
+ cent[0] = cent[1] = cent[2]=0;
+ for(i=0; i<size; i++){
+ cvert = BLI_ghash_lookup(collected,i);
+ cent[0] = cent[0] + cvert->co[0];
+ cent[1] = cent[1] + cvert->co[1];
+ cent[2] = cent[2] + cvert->co[2];
+ }
+
+ cent[0] = cent[0] / size;
+ cent[1] = cent[1] / size;
+ cent[2] = cent[2] / size;
+
+ for(i=0; i<size; i++){
+ cvert = BLI_ghash_lookup(collected,i);
+ copy_v3_v3(cvert->co,cent);
+ num++;
+ }
+ /*free the hash table*/
+ BLI_ghash_free(collected,NULL, NULL);
+ }
+ }
+ /*if any collapsed, call remove doubles*/
+ if(num){
+ //need to change selection mode here, OR do something else? Or does tool change selection mode?
+ //selectgrep
+ //first clear flags
+ BMEdge *e;
+ BMFace *f;
+ BME_clear_flag_all(bm,BME_VISITED);
+ for(v=BME_first(bm,BME_VERT); v; v=BME_next(bm,BME_VERT,v)) v->tflag1 = v->tflag2 = 0;
+ for(e=BME_first(bm,BME_EDGE); e; e=BME_next(bm,BME_EDGE,e)) e->tflag1 = e->tflag2 = 0;
+ for(f=BME_first(bm,BME_POLY); f; f=BME_next(bm,BME_POLY,f)) f->tflag1 = f->tflag2 = 0;
+ /*now call remove doubles*/
+ BME_remove_doubles(bm,0.0000001);
+ }
+ BME_selectmode_flush(bm);
+}
+
+#endif