This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/bmesh/operators/subdivideop.c

778 lines
16 KiB
C
Raw Normal View History

2009-01-07 15:17:58 +00:00
#include "MEM_guardedalloc.h"
#include "BKE_utildefines.h"
#include "BLI_arithb.h"
#include "BLI_rand.h"
2009-02-08 14:25:04 +00:00
#include "BLI_ghash.h"
2009-01-07 15:17:58 +00:00
#include "DNA_object_types.h"
2009-01-07 15:17:58 +00:00
#include "ED_mesh.h"
2009-01-07 15:17:58 +00:00
#include "bmesh.h"
#include "mesh_intern.h"
2009-02-08 14:25:04 +00:00
#include "subdivideop.h"
2009-01-07 15:17:58 +00:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
2009-01-07 15:17:58 +00:00
#define SUBD_SPLIT 1
2009-02-08 14:25:04 +00:00
/*I don't think new faces are flagged, currently, but
better safe than sorry.*/
#define FACE_NEW 2
#define FACE_CUSTOMFILL 4
2009-01-07 15:17:58 +00:00
/*stuff for the flag paramter. note that
what used to live in "beauty" and
in "seltype" live here. still have to
convert the beauty flags over, which
is why it starts at 128 (to avoid
collision).*/
#define SELTYPE_INNER 128
/*
NOTE: beauty has been renamed to flag!
*/
2009-01-07 15:17:58 +00:00
/*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?
*/
/* calculates offset for co, based on fractal, sphere or smooth settings */
static void alter_co(float *co, BMEdge *edge, float rad, int flag, float perc,
BMVert *vsta, BMVert *vend)
{
float vec1[3], fac;
if(flag & B_SMOOTH) {
/* we calculate an offset vector vec1[], to be added to *co */
float len, fac, nor[3], nor1[3], nor2[3];
VecSubf(nor, vsta->co, vend->co);
len= 0.5f*Normalize(nor);
VECCOPY(nor1, vsta->no);
VECCOPY(nor2, vend->no);
/* cosine angle */
fac= nor[0]*nor1[0] + nor[1]*nor1[1] + nor[2]*nor1[2] ;
vec1[0]= fac*nor1[0];
vec1[1]= fac*nor1[1];
vec1[2]= fac*nor1[2];
/* cosine angle */
fac= -nor[0]*nor2[0] - nor[1]*nor2[1] - nor[2]*nor2[2] ;
vec1[0]+= fac*nor2[0];
vec1[1]+= fac*nor2[1];
vec1[2]+= fac*nor2[2];
vec1[0]*= rad*len;
vec1[1]*= rad*len;
vec1[2]*= rad*len;
co[0] += vec1[0];
co[1] += vec1[1];
co[2] += vec1[2];
}
else {
if(rad > 0.0) { /* subdivide sphere */
Normalize(co);
co[0]*= rad;
co[1]*= rad;
co[2]*= rad;
}
else if(rad< 0.0) { /* fractal subdivide */
fac= rad* VecLenf(vsta->co, vend->co);
vec1[0]= fac*(float)(0.5-BLI_drand());
vec1[1]= fac*(float)(0.5-BLI_drand());
vec1[2]= fac*(float)(0.5-BLI_drand());
VecAddf(co, co, vec1);
}
2009-01-07 15:17:58 +00:00
}
}
2009-01-07 15:17:58 +00:00
/* 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, float rad,
int flag, float percent, BMEdge **out,
BMVert *vsta, BMVert *vend)
{
BMVert *ev;
// float co[3];
ev = BM_Split_Edge(bm, edge->v1, edge, out, percent, 1);
if (flag & SELTYPE_INNER) BM_Select_Vert(bm, ev, 1);
2009-01-07 15:17:58 +00:00
/* offset for smooth or sphere or fractal */
alter_co(ev->co, edge, rad, flag, percent, vsta, vend);
2009-01-07 15:17:58 +00:00
#if 0 //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;
}
2009-01-07 15:17:58 +00:00
static BMVert *subdivideedgenum(BMesh *bm, BMEdge *edge,
int curpoint, int totpoint, float rad,
int flag, BMEdge **newe,
BMVert *vsta, BMVert *vend)
{
BMVert *ev;
float percent;
if (flag & (B_PERCENTSUBD) && totpoint == 1)
/*I guess the idea is vertices store what
percent to use?*/
//percent=(float)(edge->tmp.l)/32768.0f;
percent= 1.0; //edge->tmp.fp;
else {
percent= 1.0f/(float)(totpoint+1-curpoint);
2009-01-07 15:17:58 +00:00
}
/*{
float co[3], co2[3];
VecSubf(co, edge->v2->co, edge->v1->co);
VecMulf(co, 1.0f/(float)(totpoint+1-curpoint));
VecAddf(co2, edge->v1->co, co);
2009-01-07 15:17:58 +00:00
*/
ev= bm_subdivide_edge_addvert(bm, edge, rad, flag, percent,
newe, vsta, vend);
/* VECCOPY(ev->co, co2);
}
*/
return ev;
}
static void bm_subdivide_multicut(BMesh *bm, BMEdge *edge, float rad,
int flag, int numcuts,
BMVert *vsta, BMVert *vend) {
BMEdge *eed = edge, *newe;
BMVert *v;
int i;
for(i=0;i<numcuts;i++) {
v = subdivideedgenum(bm, eed, i, numcuts, rad,
flag, &newe, vsta, vend);
BMO_SetFlag(bm, v, SUBD_SPLIT);
BMO_SetFlag(bm, eed, SUBD_SPLIT);
}
}
/*note: the patterns are rotated as necassary to
match the input geometry. they're based on the
pre-split state of the face*/
2009-01-07 15:17:58 +00:00
/*
v3---------v2
2009-01-07 15:17:58 +00:00
| |
| |
| |
| |
v4---v0---v1
2009-01-07 15:17:58 +00:00
*/
static void q_1edge_split(BMesh *bm, BMFace *face, BMVert **vlist, int numcuts,
int flag, float rad) {
BMFace *nf;
int i, add;
/*if it's odd, the middle face is a quad, otherwise it's a triangle*/
if (numcuts % 2==0) {
add = 2;
for (i=0; i<numcuts; i++) {
if (i == numcuts/2) add -= 1;
BM_Connect_Verts(bm, vlist[i], vlist[numcuts+add],
&nf);
}
} else {
add = 2;
for (i=0; i<numcuts; i++) {
BM_Connect_Verts(bm, vlist[i], vlist[numcuts+add],
&nf);
if (i == numcuts/2) {
add -= 1;
BM_Connect_Verts(bm, vlist[i],
vlist[numcuts+add],
&nf);
}
}
}
}
2009-01-07 15:17:58 +00:00
subdpattern q_1edge = {
{1, 0, 0, 0},
q_1edge_split,
4,
2009-01-07 15:17:58 +00:00
};
2009-01-07 15:17:58 +00:00
/*
v4---v3---v2
| s |
2009-01-07 15:17:58 +00:00
| |
| |
| s |
v5---v0---v1
2009-01-07 15:17:58 +00:00
*/
2009-02-08 14:25:04 +00:00
static void q_2edge_op_split(BMesh *bm, BMFace *face, BMVert **vlist,
int numcuts, int flag, float rad) {
BMFace *nf;
int i;
for (i=0; i<numcuts; i++) {
BM_Connect_Verts(bm, vlist[i],vlist[(numcuts-i-1)+numcuts+2],
&nf);
}
}
2009-01-07 15:17:58 +00:00
subdpattern q_2edge_op = {
{1, 0, 1, 0},
q_2edge_op_split,
4,
};
2009-01-07 15:17:58 +00:00
/*
v6--------v5
2009-01-07 15:17:58 +00:00
| |
| |v4s
| |v3s
| s s |
v7-v0--v1-v2
2009-01-07 15:17:58 +00:00
*/
static void q_2edge_split(BMesh *bm, BMFace *face, BMVert **vlist,
int numcuts, int flag, float rad) {
BMFace *nf;
int i;
for (i=0; i<numcuts; i++) {
BM_Connect_Verts(bm, vlist[i], vlist[numcuts+(numcuts-i)],
&nf);
}
BM_Connect_Verts(bm, vlist[numcuts*2+3], vlist[numcuts*2+1], &nf);
}
2009-01-07 15:17:58 +00:00
subdpattern q_2edge = {
{1, 1, 0, 0},
q_2edge_split,
4,
};
2009-01-07 15:17:58 +00:00
/* s s
v8--v7--v6-v5
| |
| v4 s
| |
| v3 s
| s s |
v9-v0--v1-v2
2009-01-07 15:17:58 +00:00
*/
static void q_3edge_split(BMesh *bm, BMFace *face, BMVert **vlist,
int numcuts, int flag, float rad) {
BMFace *nf;
int i, add=0;
for (i=0; i<numcuts; i++) {
if (i == numcuts/2) {
if (numcuts % 2 != 0) {
BM_Connect_Verts(bm, vlist[numcuts-i-1+add],
vlist[i+numcuts+1], &nf);
}
add = numcuts*2+2;
}
BM_Connect_Verts(bm, vlist[numcuts-i-1+add],
vlist[i+numcuts+1], &nf);
}
for (i=0; i<numcuts/2+1; i++) {
BM_Connect_Verts(bm, vlist[i],vlist[(numcuts-i)+numcuts*2+1],
&nf);
}
}
2009-01-07 15:17:58 +00:00
subdpattern q_3edge = {
{1, 1, 1, 0},
q_3edge_split,
4,
2009-01-07 15:17:58 +00:00
};
/*
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 q_4edge_split(BMesh *bm, BMFace *face, BMVert **vlist, int numcuts,
int flag, float rad) {
BMFace *nf;
BMVert *v, *v1, *v2;
BMEdge *e, *ne;
BMVert **lines = MEM_callocN(sizeof(BMVert*)*(numcuts+2)*(numcuts+2),
"q_4edge_split");
int i, j, a, b, s=numcuts+2, totv=numcuts*4+4;
/*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] = vlist[numcuts*3+2+(numcuts-i+1)];
}
2009-01-07 15:17:58 +00:00
/*last line*/
for (i=0; i<numcuts+2; i++) {
lines[(s-1)*s+i] = vlist[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 = BM_Connect_Verts(bm, vlist[a], vlist[b], &nf);
if (flag & SELTYPE_INNER) {
BM_Select_Edge(bm, e, 1);
BM_Select_Face(bm, nf, 1);
}
v1 = lines[(i+1)*s] = vlist[a];
v2 = lines[(i+1)*s + s-1] = vlist[b];
for (a=0; a<numcuts; a++) {
v = subdivideedgenum(bm, e, a, numcuts, rad, flag, &ne,
v1, v2);
if (flag & SELTYPE_INNER) {
BM_Select_Edge(bm, ne, 1);
}
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 = BM_Connect_Verts(bm, lines[a], lines[b], &nf);
if (flag & SELTYPE_INNER) {
BM_Select_Edge(bm, e, 1);
BM_Select_Face(bm, nf, 1);
}
}
}
MEM_freeN(lines);
}
2009-01-07 15:17:58 +00:00
/* v3
/ \
/ \
/ \
/ \
/ \
v4--v0--v1--v2
s s
*/
static void t_1edge_split(BMesh *bm, BMFace *face, BMVert **vlist,
int numcuts, int flag, float rad) {
BMFace *nf;
int i;
for (i=0; i<numcuts; i++) {
BM_Connect_Verts(bm, vlist[i], vlist[numcuts+1], &nf);
}
}
subdpattern t_1edge = {
{1, 0, 0},
t_1edge_split,
3,
};
/* v5
/ \
/ \ v4 s
/ \
/ \ v3 s
/ \
v6--v0--v1--v2
s s
*/
static void t_2edge_split(BMesh *bm, BMFace *face, BMVert **vlist,
int numcuts, int flag, float rad) {
BMFace *nf;
int i;
for (i=0; i<numcuts; i++) {
BM_Connect_Verts(bm, vlist[i], vlist[numcuts+numcuts-i], &nf);
}
}
subdpattern t_2edge = {
{1, 1, 0},
t_2edge_split,
3,
};
/* v5
/ \
s v6/---\ v4 s
/ \ / \
sv7/---v---\ v3 s
/ \/ \/ \
v8--v0--v1--v2
s s
*/
static void t_3edge_split(BMesh *bm, BMFace *face, BMVert **vlist,
int numcuts, int flag, float rad) {
BMFace *nf;
BMEdge *e, *ne;
BMVert ***lines, *v;
void *stackarr[1];
int i, j, a, b;
/*number of verts in each line*/
lines = MEM_callocN(sizeof(void*)*(numcuts+2), "triangle vert table");
lines[0] = (BMVert**) stackarr;
lines[0][0] = vlist[numcuts*2+1];
lines[1+numcuts] = MEM_callocN(sizeof(void*)*(numcuts+2),
"triangle vert table 2");
for (i=0; i<numcuts; i++) {
lines[1+numcuts][1+i] = vlist[i];
}
lines[1+numcuts][0] = vlist[numcuts*3+2];
lines[1+numcuts][1+numcuts] = vlist[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 = BM_Connect_Verts(bm, vlist[a], vlist[b], &nf);
if (flag & SELTYPE_INNER) {
BM_Select_Edge(bm, e, 1);
BM_Select_Face(bm, nf, 1);
}
lines[i+1][0] = vlist[a];
lines[i+1][1+i] = vlist[b];
for (j=0; j<i; j++) {
v = subdivideedgenum(bm, e, j, i, rad, flag, &ne,
vlist[a], vlist[b]);
lines[i+1][j+1] = v;
if (flag & SELTYPE_INNER) {
BM_Select_Edge(bm, ne, 1);
}
}
}
/* 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= BM_Connect_Verts(bm, lines[i][j], lines[i+1][j+1],
&nf);
if (flag & SELTYPE_INNER) {
BM_Select_Edge(bm, e, 1);
BM_Select_Face(bm, nf, 1);
}
e= BM_Connect_Verts(bm,lines[i][j+1],lines[i+1][j+1],
&nf);
if (flag & SELTYPE_INNER) {
BM_Select_Edge(bm, e, 1);
BM_Select_Face(bm, nf, 1);
}
}
}
for (i=1; i<numcuts+2; i++) {
MEM_freeN(lines[i]);
}
MEM_freeN(lines);
}
subdpattern t_3edge = {
{1, 1, 1},
t_3edge_split,
3,
};
2009-01-07 15:17:58 +00:00
subdpattern q_4edge = {
{1, 1, 1, 1},
q_4edge_split,
4,
2009-01-07 15:17:58 +00:00
};
subdpattern *patterns[] = {
&q_1edge,
&q_2edge_op,
2009-01-07 15:17:58 +00:00
&q_4edge,
&q_3edge,
&q_2edge,
&t_1edge,
&t_2edge,
&t_3edge,
2009-01-07 15:17:58 +00:00
};
#define PLEN (sizeof(patterns) / sizeof(void*))
typedef struct subd_facedata {
BMVert *start; subdpattern *pat;
} subd_facedata;
2009-01-07 15:17:58 +00:00
void esubdivide_exec(BMesh *bmesh, BMOperator *op)
{
2009-02-08 14:25:04 +00:00
V_DECLARE(facedata);
V_DECLARE(verts);
V_DECLARE(edges);
2009-02-08 14:25:04 +00:00
BMOpSlot *einput, *finput, *pinput;
BMEdge *edge, **edges = NULL;
BMFace *face;
BMLoop *nl;
BMVert **verts = NULL;
BMIter fiter, liter;
2009-02-08 14:25:04 +00:00
GHash *customfill_hash;
2009-01-07 15:17:58 +00:00
subdpattern *pat;
subd_facedata *facedata = NULL;
2009-02-08 14:25:04 +00:00
float rad;
int i, j, matched, a, b, numcuts, flag, selaction, fillindex;
2009-01-07 15:17:58 +00:00
BMO_Flag_Buffer(bmesh, op, BMOP_ESUBDIVIDE_EDGES, SUBD_SPLIT);
numcuts = BMO_GetSlot(op, BMOP_ESUBDIVIDE_NUMCUTS)->data.i;
flag = BMO_GetSlot(op, BMOP_ESUBDIVIDE_FLAG)->data.i;
rad = BMO_GetSlot(op, BMOP_ESUBDIVIDE_RADIUS)->data.f;
selaction = BMO_GetSlot(op, BMOP_ESUBDIVIDE_SELACTION)->data.i;
if (selaction == SUBDIV_SELECT_INNER) flag |= SELTYPE_INNER;
2009-01-07 15:17:58 +00:00
einput = BMO_GetSlot(op, BMOP_ESUBDIVIDE_EDGES);
2009-01-07 15:17:58 +00:00
/*first go through and split edges*/
for (i=0; i<einput->len; i++) {
edge = ((BMEdge**)einput->data.p)[i];
BMO_SetFlag(bmesh, edge, SUBD_SPLIT);
2009-01-07 15:17:58 +00:00
}
2009-02-08 14:25:04 +00:00
customfill_hash = BLI_ghash_new(BLI_ghashutil_ptrhash,
BLI_ghashutil_ptrcmp);
/*process custom fill patterns*/
finput = BMO_GetSlot(op, BMOP_ESUBDIVIDE_CUSTOMFILL_FACES);
pinput = BMO_GetSlot(op, BMOP_ESUBDIVIDE_CUSTOMFILL_PATTERNS);
for (i=0; i<finput->len; i++) {
face = ((BMFace**)finput->data.p)[i];
BMO_SetFlag(bmesh, face, FACE_CUSTOMFILL);
BLI_ghash_insert(customfill_hash, face,
((void**)pinput->data.p)[i]);
}
for (face=BMIter_New(&fiter, bmesh, BM_FACES, NULL);
face; face=BMIter_Step(&fiter)) {
/*figure out which pattern to use*/
2009-02-08 14:25:04 +00:00
V_RESET(edges);
V_RESET(verts);
2009-02-08 14:25:04 +00:00
i = 0;
for (nl=BMIter_New(&liter, bmesh, BM_LOOPS_OF_FACE, face);
nl; nl=BMIter_Step(&liter)) {
V_GROW(edges);
V_GROW(verts);
edges[i] = nl->e;
verts[i] = nl->v;
i++;
}
2009-01-07 15:17:58 +00:00
2009-02-08 14:25:04 +00:00
if (BMO_TestFlag(bmesh, face, FACE_CUSTOMFILL)) {
pat = BLI_ghash_lookup(customfill_hash, face);
for (i=0; i<pat->len; i++) {
matched = 1;
for (j=0; j<pat->len; j++) {
a = (j + i) % pat->len;
if ((!!BMO_TestFlag(bmesh, edges[a], SUBD_SPLIT))
!= (!!pat->seledges[j])) {
matched = 0;
break;
}
}
if (matched) {
V_GROW(facedata);
b = V_COUNT(facedata)-1;
facedata[b].pat = pat;
facedata[b].start = verts[i];
break;
}
}
if (!matched) {
/*if no match, append null element to array.*/
V_GROW(facedata);
}
/*obvously don't test for other patterns matching*/
continue;
}
for (i=0; i<PLEN; i++) {
pat = patterns[i];
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_TestFlag(bmesh, edges[j], SUBD_SPLIT))
!= (!!pat->seledges[b])) {
matched = 0;
break;
2009-01-07 15:17:58 +00:00
}
}
if (matched) break;
}
if (matched) {
V_GROW(facedata);
BMO_SetFlag(bmesh, face, SUBD_SPLIT);
j = V_COUNT(facedata) - 1;
facedata[j].pat = pat;
facedata[j].start = verts[a];
break;
}
2009-01-07 15:17:58 +00:00
}
}
}
2009-01-07 15:17:58 +00:00
/*go through and split edges*/
for (i=0; i<einput->len; i++) {
edge = ((BMEdge**)einput->data.p)[i];
bm_subdivide_multicut(bmesh, edge, rad, flag, numcuts,
edge->v1, edge->v2);
//BM_Split_Edge_Multi(bmesh, edge, numcuts);
}
2009-01-07 15:17:58 +00:00
//if (facedata) V_FREE(facedata);
//return;
i = 0;
for (face=BMIter_New(&fiter, bmesh, BM_FACES, NULL);
face; face=BMIter_Step(&fiter)) {
/*figure out which pattern to use*/
V_RESET(verts);
if (BMO_TestFlag(bmesh, face, SUBD_SPLIT) == 0) continue;
2009-02-08 14:25:04 +00:00
pat = facedata[i].pat;
if (!pat) continue;
j = a = 0;
for (nl=BMIter_New(&liter, bmesh, BM_LOOPS_OF_FACE, face);
nl; nl=BMIter_Step(&liter)) {
if (nl->v == facedata[i].start) {
a = j+1;
break;
2009-01-07 15:17:58 +00:00
}
j++;
}
2009-01-07 15:17:58 +00:00
for (j=0; j<face->len; j++) {
V_GROW(verts);
}
j = 0;
for (nl=BMIter_New(&liter, bmesh, BM_LOOPS_OF_FACE, face);
nl; nl=BMIter_Step(&liter)) {
b = (j-a+face->len) % face->len;
verts[b] = nl->v;
j += 1;
2009-01-07 15:17:58 +00:00
}
pat->connectexec(bmesh, face, verts, numcuts, flag, rad);
i++;
2009-01-07 15:17:58 +00:00
}
if (facedata) V_FREE(facedata);
if (edges) V_FREE(edges);
if (verts) V_FREE(verts);
}
/*editmesh-emulating function*/
void BM_esubdivideflag(Object *obedit, BMesh *bm, int selflag, float rad,
int flag, int numcuts, int seltype) {
BMOperator op;
BMO_Init_Op(&op, BMOP_ESUBDIVIDE);
BMO_Set_Int(&op, BMOP_ESUBDIVIDE_NUMCUTS, numcuts);
BMO_Set_Int(&op, BMOP_ESUBDIVIDE_FLAG, flag);
BMO_Set_Float(&op, BMOP_ESUBDIVIDE_RADIUS, rad);
BMO_Set_Int(&op, BMOP_ESUBDIVIDE_SELACTION, seltype);
BMO_HeaderFlag_To_Slot(bm, &op, BMOP_ESUBDIVIDE_EDGES, selflag, BM_EDGE);
BMO_Exec_Op(bm, &op);
BMO_Finish_Op(bm, &op);
}
void BM_esubdivideflag_conv(Object *obedit,EditMesh *em,int selflag, float rad,
int flag, int numcuts, int seltype) {
BMesh *bm = editmesh_to_bmesh(em);
EditMesh *em2;
BM_esubdivideflag(obedit, bm, selflag, rad, flag, numcuts, seltype);
em2 = bmesh_to_editmesh(bm);
free_editMesh(em);
*em = *em2;
MEM_freeN(em2);
BM_Free_Mesh(bm);
}