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:
authorTon Roosendaal <ton@blender.org>2008-12-30 16:16:14 +0300
committerTon Roosendaal <ton@blender.org>2008-12-30 16:16:14 +0300
commit25fac7b001aaa03f6341a0c04e08798a01379ca3 (patch)
tree1606ead91f45c805dd5035990ea04e75cbe87682 /source/blender/editors/mesh/editmesh_lib.c
parent446492c2669773e34dc2d827648021f9115dc821 (diff)
2.5
Editmesh code cleaned and compiling/linking. A whopping 20k lines back! :) Not that it does stuff... editmode in/out has to be done, and loads of operators. Also linking/exporting editmesh calls has to be reviewed. Also: added a blender_test_break() mechanism in BKE.
Diffstat (limited to 'source/blender/editors/mesh/editmesh_lib.c')
-rw-r--r--source/blender/editors/mesh/editmesh_lib.c2266
1 files changed, 2266 insertions, 0 deletions
diff --git a/source/blender/editors/mesh/editmesh_lib.c b/source/blender/editors/mesh/editmesh_lib.c
new file mode 100644
index 00000000000..667c8d5ac45
--- /dev/null
+++ b/source/blender/editors/mesh/editmesh_lib.c
@@ -0,0 +1,2266 @@
+/**
+ * $Id:
+ *
+ * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2004 by Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/*
+
+editmesh_lib: generic (no UI, no menus) operations/evaluators for editmesh data
+
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_arithb.h"
+#include "BLI_editVert.h"
+
+#include "BKE_customdata.h"
+#include "BKE_global.h"
+#include "BKE_mesh.h"
+#include "BKE_utildefines.h"
+
+#include "ED_mesh.h"
+
+#include "editmesh.h"
+
+/* this replaces the active flag used in uv/face mode */
+void EM_set_actFace(EditMesh *em, EditFace *efa)
+{
+ em->act_face = efa;
+}
+
+EditFace *EM_get_actFace(EditMesh *em, int sloppy)
+{
+ if (em->act_face) {
+ return em->act_face;
+ } else if (sloppy) {
+ EditFace *efa= NULL;
+ EditSelection *ese;
+
+ ese = em->selected.last;
+ for (; ese; ese=ese->prev){
+ if(ese->type == EDITFACE) {
+ efa = (EditFace *)ese->data;
+
+ if (efa->h) efa= NULL;
+ else break;
+ }
+ }
+ if (efa==NULL) {
+ for (efa= em->faces.first; efa; efa= efa->next) {
+ if (efa->f & SELECT)
+ break;
+ }
+ }
+ return efa; /* can still be null */
+ }
+ return NULL;
+}
+
+int EM_get_actSelection(EditMesh *em, EditSelection *ese)
+{
+ EditSelection *ese_last = em->selected.last;
+ EditFace *efa = EM_get_actFace(em, 0);
+
+ ese->next = ese->prev = NULL;
+
+ if (ese_last) {
+ if (ese_last->type == EDITFACE) { /* if there is an active face, use it over the last selected face */
+ if (efa) {
+ ese->data = (void *)efa;
+ } else {
+ ese->data = ese_last->data;
+ }
+ ese->type = EDITFACE;
+ } else {
+ ese->data = ese_last->data;
+ ese->type = ese_last->type;
+ }
+ } else if (efa) { /* no */
+ ese->data = (void *)efa;
+ ese->type = EDITFACE;
+ } else {
+ ese->data = NULL;
+ return 0;
+ }
+ return 1;
+}
+
+/* ********* Selection History ************ */
+static int EM_check_selection(EditMesh *em, void *data)
+{
+ EditSelection *ese;
+
+ for(ese = em->selected.first; ese; ese = ese->next){
+ if(ese->data == data) return 1;
+ }
+
+ return 0;
+}
+
+void EM_remove_selection(EditMesh *em, void *data, int type)
+{
+ EditSelection *ese;
+ for(ese=em->selected.first; ese; ese = ese->next){
+ if(ese->data == data){
+ BLI_freelinkN(&(em->selected),ese);
+ break;
+ }
+ }
+}
+
+void EM_store_selection(EditMesh *em, void *data, int type)
+{
+ EditSelection *ese;
+ if(!EM_check_selection(em, data)){
+ ese = (EditSelection*) MEM_callocN( sizeof(EditSelection), "Edit Selection");
+ ese->type = type;
+ ese->data = data;
+ BLI_addtail(&(em->selected),ese);
+ }
+}
+
+void EM_validate_selections(EditMesh *em)
+{
+ EditSelection *ese, *nextese;
+
+ ese = em->selected.first;
+ while(ese){
+ nextese = ese->next;
+ if(ese->type == EDITVERT && !(((EditVert*)ese->data)->f & SELECT)) BLI_freelinkN(&(em->selected), ese);
+ else if(ese->type == EDITEDGE && !(((EditEdge*)ese->data)->f & SELECT)) BLI_freelinkN(&(em->selected), ese);
+ else if(ese->type == EDITFACE && !(((EditFace*)ese->data)->f & SELECT)) BLI_freelinkN(&(em->selected), ese);
+ ese = nextese;
+ }
+}
+
+static void EM_strip_selections(EditMesh *em)
+{
+ EditSelection *ese, *nextese;
+ if(!(em->selectmode & SCE_SELECT_VERTEX)){
+ ese = em->selected.first;
+ while(ese){
+ nextese = ese->next;
+ if(ese->type == EDITVERT) BLI_freelinkN(&(em->selected),ese);
+ ese = nextese;
+ }
+ }
+ if(!(em->selectmode & SCE_SELECT_EDGE)){
+ ese=em->selected.first;
+ while(ese){
+ nextese = ese->next;
+ if(ese->type == EDITEDGE) BLI_freelinkN(&(em->selected), ese);
+ ese = nextese;
+ }
+ }
+ if(!(em->selectmode & SCE_SELECT_FACE)){
+ ese=em->selected.first;
+ while(ese){
+ nextese = ese->next;
+ if(ese->type == EDITFACE) BLI_freelinkN(&(em->selected), ese);
+ ese = nextese;
+ }
+ }
+}
+
+/* generic way to get data from an EditSelection type
+These functions were written to be used by the Modifier widget when in Rotate about active mode,
+but can be used anywhere.
+EM_editselection_center
+EM_editselection_normal
+EM_editselection_plane
+*/
+void EM_editselection_center(float *center, EditSelection *ese)
+{
+ if (ese->type==EDITVERT) {
+ EditVert *eve= ese->data;
+ VecCopyf(center, eve->co);
+ } else if (ese->type==EDITEDGE) {
+ EditEdge *eed= ese->data;
+ VecAddf(center, eed->v1->co, eed->v2->co);
+ VecMulf(center, 0.5);
+ } else if (ese->type==EDITFACE) {
+ EditFace *efa= ese->data;
+ VecCopyf(center, efa->cent);
+ }
+}
+
+void EM_editselection_normal(float *normal, EditSelection *ese)
+{
+ if (ese->type==EDITVERT) {
+ EditVert *eve= ese->data;
+ VecCopyf(normal, eve->no);
+ } else if (ese->type==EDITEDGE) {
+ EditEdge *eed= ese->data;
+ float plane[3]; /* need a plane to correct the normal */
+ float vec[3]; /* temp vec storage */
+
+ VecAddf(normal, eed->v1->no, eed->v2->no);
+ VecSubf(plane, eed->v2->co, eed->v1->co);
+
+ /* the 2 vertex normals will be close but not at rightangles to the edge
+ for rotate about edge we want them to be at right angles, so we need to
+ do some extra colculation to correct the vert normals,
+ we need the plane for this */
+ Crossf(vec, normal, plane);
+ Crossf(normal, plane, vec);
+ Normalize(normal);
+
+ } else if (ese->type==EDITFACE) {
+ EditFace *efa= ese->data;
+ VecCopyf(normal, efa->n);
+ }
+}
+
+/* Calculate a plane that is rightangles to the edge/vert/faces normal
+also make the plane run allong an axis that is related to the geometry,
+because this is used for the manipulators Y axis.*/
+void EM_editselection_plane(float *plane, EditSelection *ese)
+{
+ if (ese->type==EDITVERT) {
+ EditVert *eve= ese->data;
+ float vec[3]={0,0,0};
+
+ if (ese->prev) { /*use previously selected data to make a usefull vertex plane */
+ EM_editselection_center(vec, ese->prev);
+ VecSubf(plane, vec, eve->co);
+ } else {
+ /* make a fake plane thats at rightangles to the normal
+ we cant make a crossvec from a vec thats the same as the vec
+ unlikely but possible, so make sure if the normal is (0,0,1)
+ that vec isnt the same or in the same direction even.*/
+ if (eve->no[0]<0.5) vec[0]=1;
+ else if (eve->no[1]<0.5) vec[1]=1;
+ else vec[2]=1;
+ Crossf(plane, eve->no, vec);
+ }
+ } else if (ese->type==EDITEDGE) {
+ EditEdge *eed= ese->data;
+
+ /*the plane is simple, it runs allong the edge
+ however selecting different edges can swap the direction of the y axis.
+ this makes it less likely for the y axis of the manipulator
+ (running along the edge).. to flip less often.
+ at least its more pradictable */
+ if (eed->v2->co[1] > eed->v1->co[1]) /*check which to do first */
+ VecSubf(plane, eed->v2->co, eed->v1->co);
+ else
+ VecSubf(plane, eed->v1->co, eed->v2->co);
+
+ } else if (ese->type==EDITFACE) {
+ EditFace *efa= ese->data;
+ float vec[3];
+ if (efa->v4) { /*if its a quad- set the plane along the 2 longest edges.*/
+ float vecA[3], vecB[3];
+ VecSubf(vecA, efa->v4->co, efa->v3->co);
+ VecSubf(vecB, efa->v1->co, efa->v2->co);
+ VecAddf(plane, vecA, vecB);
+
+ VecSubf(vecA, efa->v1->co, efa->v4->co);
+ VecSubf(vecB, efa->v2->co, efa->v3->co);
+ VecAddf(vec, vecA, vecB);
+ /*use the biggest edge length*/
+ if (plane[0]*plane[0]+plane[1]*plane[1]+plane[2]*plane[2] < vec[0]*vec[0]+vec[1]*vec[1]+vec[2]*vec[2])
+ VecCopyf(plane, vec);
+ } else {
+ /*start with v1-2 */
+ VecSubf(plane, efa->v1->co, efa->v2->co);
+
+ /*test the edge between v2-3, use if longer */
+ VecSubf(vec, efa->v2->co, efa->v3->co);
+ if (plane[0]*plane[0]+plane[1]*plane[1]+plane[2]*plane[2] < vec[0]*vec[0]+vec[1]*vec[1]+vec[2]*vec[2])
+ VecCopyf(plane, vec);
+
+ /*test the edge between v1-3, use if longer */
+ VecSubf(vec, efa->v3->co, efa->v1->co);
+ if (plane[0]*plane[0]+plane[1]*plane[1]+plane[2]*plane[2] < vec[0]*vec[0]+vec[1]*vec[1]+vec[2]*vec[2])
+ VecCopyf(plane, vec);
+ }
+ }
+ Normalize(plane);
+}
+
+
+
+void EM_select_face(EditFace *efa, int sel)
+{
+ if(sel) {
+ efa->f |= SELECT;
+ efa->e1->f |= SELECT;
+ efa->e2->f |= SELECT;
+ efa->e3->f |= SELECT;
+ if(efa->e4) efa->e4->f |= SELECT;
+ efa->v1->f |= SELECT;
+ efa->v2->f |= SELECT;
+ efa->v3->f |= SELECT;
+ if(efa->v4) efa->v4->f |= SELECT;
+ }
+ else {
+ efa->f &= ~SELECT;
+ efa->e1->f &= ~SELECT;
+ efa->e2->f &= ~SELECT;
+ efa->e3->f &= ~SELECT;
+ if(efa->e4) efa->e4->f &= ~SELECT;
+ efa->v1->f &= ~SELECT;
+ efa->v2->f &= ~SELECT;
+ efa->v3->f &= ~SELECT;
+ if(efa->v4) efa->v4->f &= ~SELECT;
+ }
+}
+
+void EM_select_edge(EditEdge *eed, int sel)
+{
+ if(sel) {
+ eed->f |= SELECT;
+ eed->v1->f |= SELECT;
+ eed->v2->f |= SELECT;
+ }
+ else {
+ eed->f &= ~SELECT;
+ eed->v1->f &= ~SELECT;
+ eed->v2->f &= ~SELECT;
+ }
+}
+
+void EM_select_face_fgon(EditMesh *em, EditFace *efa, int val)
+{
+ short index=0;
+
+ if(efa->fgonf==0) EM_select_face(efa, val);
+ else {
+ if(efa->e1->fgoni) index= efa->e1->fgoni;
+ if(efa->e2->fgoni) index= efa->e2->fgoni;
+ if(efa->e3->fgoni) index= efa->e3->fgoni;
+ if(efa->v4 && efa->e4->fgoni) index= efa->e4->fgoni;
+
+ if(index==0) printf("wrong fgon select\n");
+
+ // select all ngon faces with index
+ for(efa= em->faces.first; efa; efa= efa->next) {
+ if(efa->fgonf) {
+ if(efa->e1->fgoni==index || efa->e2->fgoni==index ||
+ efa->e3->fgoni==index || (efa->e4 && efa->e4->fgoni==index) ) {
+ EM_select_face(efa, val);
+ }
+ }
+ }
+ }
+}
+
+
+/* only vertices */
+int faceselectedOR(EditFace *efa, int flag)
+{
+ if ((efa->v1->f | efa->v2->f | efa->v3->f | (efa->v4?efa->v4->f:0))&flag) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+// replace with (efa->f & SELECT)
+int faceselectedAND(EditFace *efa, int flag)
+{
+ if ((efa->v1->f & efa->v2->f & efa->v3->f & (efa->v4?efa->v4->f:flag))&flag) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+int EM_nfaces_selected(EditMesh *em)
+{
+ EditFace *efa;
+ int count= 0;
+
+ for (efa= em->faces.first; efa; efa= efa->next)
+ if (efa->f & SELECT)
+ count++;
+
+ return count;
+}
+
+#if 0
+static int EM_nedges(EditMesh *em)
+{
+ EditEdge *eed;
+ int count= 0;
+
+ for (eed= em->edges.first; eed; eed= eed->next) count++;
+ return count;
+}
+#endif
+
+int EM_nvertices_selected(EditMesh *em)
+{
+ EditVert *eve;
+ int count= 0;
+
+ for (eve= em->verts.first; eve; eve= eve->next)
+ if (eve->f & SELECT)
+ count++;
+
+ return count;
+}
+
+void EM_clear_flag_all(EditMesh *em, int flag)
+{
+ EditVert *eve;
+ EditEdge *eed;
+ EditFace *efa;
+
+ for (eve= em->verts.first; eve; eve= eve->next) eve->f &= ~flag;
+ for (eed= em->edges.first; eed; eed= eed->next) eed->f &= ~flag;
+ for (efa= em->faces.first; efa; efa= efa->next) efa->f &= ~flag;
+
+ if(flag & SELECT) BLI_freelistN(&(em->selected));
+}
+
+void EM_set_flag_all(EditMesh *em, int flag)
+{
+ EditVert *eve;
+ EditEdge *eed;
+ EditFace *efa;
+
+ for (eve= em->verts.first; eve; eve= eve->next) if(eve->h==0) eve->f |= flag;
+ for (eed= em->edges.first; eed; eed= eed->next) if(eed->h==0) eed->f |= flag;
+ for (efa= em->faces.first; efa; efa= efa->next) if(efa->h==0) efa->f |= flag;
+
+}
+
+/* flush for changes in vertices only */
+void EM_deselect_flush(EditMesh *em)
+{
+ EditEdge *eed;
+ EditFace *efa;
+
+ for(eed= em->edges.first; eed; eed= eed->next) {
+ if(eed->v1->f & eed->v2->f & SELECT);
+ else eed->f &= ~SELECT;
+ }
+ for(efa= em->faces.first; efa; efa= efa->next) {
+ if(efa->v4) {
+ if(efa->v1->f & efa->v2->f & efa->v3->f & efa->v4->f & SELECT );
+ else efa->f &= ~SELECT;
+ }
+ else {
+ if(efa->v1->f & efa->v2->f & efa->v3->f & SELECT );
+ else efa->f &= ~SELECT;
+ }
+ }
+}
+
+
+/* flush selection to edges & faces */
+
+/* this only based on coherent selected vertices, for example when adding new
+ objects. call clear_flag_all() before you select vertices to be sure it ends OK!
+
+*/
+
+void EM_select_flush(EditMesh *em)
+{
+ EditEdge *eed;
+ EditFace *efa;
+
+ for(eed= em->edges.first; eed; eed= eed->next) {
+ if(eed->v1->f & eed->v2->f & SELECT) eed->f |= SELECT;
+ }
+ for(efa= em->faces.first; efa; efa= efa->next) {
+ if(efa->v4) {
+ if(efa->v1->f & efa->v2->f & efa->v3->f & efa->v4->f & SELECT ) efa->f |= SELECT;
+ }
+ else {
+ if(efa->v1->f & efa->v2->f & efa->v3->f & SELECT ) efa->f |= SELECT;
+ }
+ }
+}
+
+/* when vertices or edges can be selected, also make fgon consistant */
+static void check_fgons_selection(EditMesh *em)
+{
+ EditFace *efa, *efan;
+ EditEdge *eed;
+ ListBase *lbar;
+ int sel, desel, index, totfgon= 0;
+
+ /* count amount of fgons */
+ for(eed= em->edges.first; eed; eed= eed->next)
+ if(eed->fgoni>totfgon) totfgon= eed->fgoni;
+
+ if(totfgon==0) return;
+
+ lbar= MEM_callocN((totfgon+1)*sizeof(ListBase), "listbase array");
+
+ /* put all fgons in lbar */
+ for(efa= em->faces.first; efa; efa= efan) {
+ efan= efa->next;
+ index= efa->e1->fgoni;
+ if(index==0) index= efa->e2->fgoni;
+ if(index==0) index= efa->e3->fgoni;
+ if(index==0 && efa->e4) index= efa->e4->fgoni;
+ if(index) {
+ BLI_remlink(&em->faces, efa);
+ BLI_addtail(&lbar[index], efa);
+ }
+ }
+
+ /* now check the fgons */
+ for(index=1; index<=totfgon; index++) {
+ /* we count on vertices/faces/edges being set OK, so we only have to set ngon itself */
+ sel= desel= 0;
+ for(efa= lbar[index].first; efa; efa= efa->next) {
+ if(efa->e1->fgoni==0) {
+ if(efa->e1->f & SELECT) sel++;
+ else desel++;
+ }
+ if(efa->e2->fgoni==0) {
+ if(efa->e2->f & SELECT) sel++;
+ else desel++;
+ }
+ if(efa->e3->fgoni==0) {
+ if(efa->e3->f & SELECT) sel++;
+ else desel++;
+ }
+ if(efa->e4 && efa->e4->fgoni==0) {
+ if(efa->e4->f & SELECT) sel++;
+ else desel++;
+ }
+
+ if(sel && desel) break;
+ }
+
+ if(sel && desel) sel= 0;
+ else if(sel) sel= 1;
+ else sel= 0;
+
+ /* select/deselect and put back */
+ for(efa= lbar[index].first; efa; efa= efa->next) {
+ if(sel) efa->f |= SELECT;
+ else efa->f &= ~SELECT;
+ }
+ addlisttolist(&em->faces, &lbar[index]);
+ }
+
+ MEM_freeN(lbar);
+}
+
+
+/* flush to edges & faces */
+
+/* based on select mode it selects edges/faces
+ assumed is that verts/edges/faces were properly selected themselves
+ with the calls above
+*/
+
+void EM_selectmode_flush(EditMesh *em)
+{
+ EditEdge *eed;
+ EditFace *efa;
+
+ // flush to edges & faces
+ if(em->selectmode & SCE_SELECT_VERTEX) {
+ for(eed= em->edges.first; eed; eed= eed->next) {
+ if(eed->v1->f & eed->v2->f & SELECT) eed->f |= SELECT;
+ else eed->f &= ~SELECT;
+ }
+ for(efa= em->faces.first; efa; efa= efa->next) {
+ if(efa->v4) {
+ if(efa->v1->f & efa->v2->f & efa->v3->f & efa->v4->f & SELECT) efa->f |= SELECT;
+ else efa->f &= ~SELECT;
+ }
+ else {
+ if(efa->v1->f & efa->v2->f & efa->v3->f & SELECT) efa->f |= SELECT;
+ else efa->f &= ~SELECT;
+ }
+ }
+ }
+ // flush to faces
+ else if(em->selectmode & SCE_SELECT_EDGE) {
+ for(efa= em->faces.first; efa; efa= efa->next) {
+ if(efa->e4) {
+ if(efa->e1->f & efa->e2->f & efa->e3->f & efa->e4->f & SELECT) efa->f |= SELECT;
+ else efa->f &= ~SELECT;
+ }
+ else {
+ if(efa->e1->f & efa->e2->f & efa->e3->f & SELECT) efa->f |= SELECT;
+ else efa->f &= ~SELECT;
+ }
+ }
+ }
+ // make sure selected faces have selected edges too, for extrude (hack?)
+ else if(em->selectmode & SCE_SELECT_FACE) {
+ for(efa= em->faces.first; efa; efa= efa->next) {
+ if(efa->f & SELECT) EM_select_face(efa, 1);
+ }
+ }
+
+ if(!(em->selectmode & SCE_SELECT_FACE))
+ check_fgons_selection(em);
+
+}
+
+void EM_convertsel(EditMesh *em, short oldmode, short selectmode)
+{
+ EditVert *eve;
+ EditEdge *eed;
+ EditFace *efa;
+ /*clear flags*/
+ for(eve= em->verts.first; eve; eve= eve->next) eve->f1 = 0;
+ for(eed= em->edges.first; eed; eed= eed->next) eed->f1 = 0;
+ for(efa= em->faces.first; efa; efa= efa->next) efa->f1 = 0;
+
+ /*have to find out what the selectionmode was previously*/
+ if(oldmode == SCE_SELECT_VERTEX) {
+ if(selectmode == SCE_SELECT_EDGE){
+ /*select all edges associated with every selected vertex*/
+ for(eed= em->edges.first; eed; eed= eed->next){
+ if(eed->v1->f&SELECT) eed->f1 = 1;
+ else if(eed->v2->f&SELECT) eed->f1 = 1;
+ }
+
+ for(eed= em->edges.first; eed; eed= eed->next){
+ if(eed->f1 == 1) EM_select_edge(eed,1);
+ }
+ }
+ else if(selectmode == SCE_SELECT_FACE){
+ /*select all faces associated with every selected vertex*/
+ for(efa= em->faces.first; efa; efa= efa->next){
+ if(efa->v1->f&SELECT) efa->f1 = 1;
+ else if(efa->v2->f&SELECT) efa->f1 = 1;
+ else if(efa->v3->f&SELECT) efa->f1 = 1;
+ else{
+ if(efa->v4){
+ if(efa->v4->f&SELECT) efa->f1 =1;
+ }
+ }
+ }
+ for(efa= em->faces.first; efa; efa= efa->next){
+ if(efa->f1 == 1) EM_select_face(efa,1);
+ }
+ }
+ }
+
+ if(oldmode == SCE_SELECT_EDGE){
+ if(selectmode == SCE_SELECT_FACE){
+ for(efa= em->faces.first; efa; efa= efa->next){
+ if(efa->e1->f&SELECT) efa->f1 = 1;
+ else if(efa->e2->f&SELECT) efa->f1 = 1;
+ else if(efa->e3->f&SELECT) efa->f1 = 1;
+ else if(efa->e4){
+ if(efa->e4->f&SELECT) efa->f1 = 1;
+ }
+ }
+ for(efa= em->faces.first; efa; efa= efa->next){
+ if(efa->f1 == 1) EM_select_face(efa,1);
+ }
+ }
+ }
+
+ check_fgons_selection(em);
+}
+
+/* when switching select mode, makes sure selection is consistant for editing */
+/* also for paranoia checks to make sure edge or face mode works */
+void EM_selectmode_set(EditMesh *em)
+{
+ EditVert *eve;
+ EditEdge *eed;
+ EditFace *efa;
+
+ EM_strip_selections(em); /*strip EditSelections from em->selected that are not relevant to new mode*/
+
+ if(em->selectmode & SCE_SELECT_VERTEX) {
+ /* vertices -> edges -> faces */
+ for (eed= em->edges.first; eed; eed= eed->next) eed->f &= ~SELECT;
+ for (efa= em->faces.first; efa; efa= efa->next) efa->f &= ~SELECT;
+
+ EM_select_flush(em);
+ }
+ else if(em->selectmode & SCE_SELECT_EDGE) {
+ /* deselect vertices, and select again based on edge select */
+ for(eve= em->verts.first; eve; eve= eve->next) eve->f &= ~SELECT;
+ for(eed= em->edges.first; eed; eed= eed->next)
+ if(eed->f & SELECT) EM_select_edge(eed, 1);
+ /* selects faces based on edge status */
+ EM_selectmode_flush(em);
+ }
+ else if(em->selectmode & SCE_SELECT_FACE) {
+ /* deselect eges, and select again based on face select */
+ for(eed= em->edges.first; eed; eed= eed->next) EM_select_edge(eed, 0);
+
+ for(efa= em->faces.first; efa; efa= efa->next)
+ if(efa->f & SELECT) EM_select_face(efa, 1);
+ }
+}
+
+/* paranoia check, actually only for entering editmode. rule:
+- vertex hidden, always means edge is hidden too
+- edge hidden, always means face is hidden too
+- face hidden, dont change anything
+*/
+void EM_hide_reset(EditMesh *em)
+{
+ EditEdge *eed;
+ EditFace *efa;
+
+ for(eed= em->edges.first; eed; eed= eed->next)
+ if(eed->v1->h || eed->v2->h) eed->h |= 1;
+
+ for(efa= em->faces.first; efa; efa= efa->next)
+ if((efa->e1->h & 1) || (efa->e2->h & 1) || (efa->e3->h & 1) || (efa->e4 && (efa->e4->h & 1)))
+ efa->h= 1;
+
+}
+
+void EM_data_interp_from_verts(EditMesh *em, EditVert *v1, EditVert *v2, EditVert *eve, float fac)
+{
+ void *src[2];
+ float w[2];
+
+ if (v1->data && v2->data) {
+ src[0]= v1->data;
+ src[1]= v2->data;
+ w[0] = 1.0f-fac;
+ w[1] = fac;
+
+ CustomData_em_interp(&em->vdata, src, w, NULL, 2, eve->data);
+ }
+}
+
+void EM_data_interp_from_faces(EditMesh *em, EditFace *efa1, EditFace *efa2, EditFace *efan, int i1, int i2, int i3, int i4)
+{
+ float w[2][4][4];
+ void *src[2];
+ int count = (efa2)? 2: 1;
+
+ if (efa1->data) {
+ /* set weights for copying from corners directly to other corners */
+ memset(w, 0, sizeof(w));
+
+ w[i1/4][0][i1%4]= 1.0f;
+ w[i2/4][1][i2%4]= 1.0f;
+ w[i3/4][2][i3%4]= 1.0f;
+ if (i4 != -1)
+ w[i4/4][3][i4%4]= 1.0f;
+
+ src[0]= efa1->data;
+ src[1]= (efa2)? efa2->data: NULL;
+
+ CustomData_em_interp(&em->fdata, src, NULL, (float*)w, count, efan->data);
+ }
+}
+
+EditFace *EM_face_from_faces(EditMesh *em, EditFace *efa1, EditFace *efa2, int i1, int i2, int i3, int i4)
+{
+ EditFace *efan;
+ EditVert **v[2];
+
+ v[0]= &efa1->v1;
+ v[1]= (efa2)? &efa2->v1: NULL;
+
+ efan= addfacelist(em, v[i1/4][i1%4], v[i2/4][i2%4], v[i3/4][i3%4],
+ (i4 == -1)? 0: v[i4/4][i4%4], efa1, NULL);
+
+ EM_data_interp_from_faces(em, efa1, efa2, efan, i1, i2, i3, i4);
+
+ return efan;
+}
+
+static void update_data_blocks(EditMesh *em, CustomData *olddata, CustomData *data)
+{
+ EditFace *efa;
+ EditVert *eve;
+ void *block;
+
+ if (data == &em->vdata) {
+ for(eve= em->verts.first; eve; eve= eve->next) {
+ block = NULL;
+ CustomData_em_set_default(data, &block);
+ CustomData_em_copy_data(olddata, data, eve->data, &block);
+ CustomData_em_free_block(olddata, &eve->data);
+ eve->data= block;
+ }
+ }
+ else if (data == &em->fdata) {
+ for(efa= em->faces.first; efa; efa= efa->next) {
+ block = NULL;
+ CustomData_em_set_default(data, &block);
+ CustomData_em_copy_data(olddata, data, efa->data, &block);
+ CustomData_em_free_block(olddata, &efa->data);
+ efa->data= block;
+ }
+ }
+}
+
+void EM_add_data_layer(EditMesh *em, CustomData *data, int type)
+{
+ CustomData olddata;
+
+ olddata= *data;
+ olddata.layers= (olddata.layers)? MEM_dupallocN(olddata.layers): NULL;
+ CustomData_add_layer(data, type, CD_CALLOC, NULL, 0);
+
+ update_data_blocks(em, &olddata, data);
+ if (olddata.layers) MEM_freeN(olddata.layers);
+}
+
+void EM_free_data_layer(EditMesh *em, CustomData *data, int type)
+{
+ CustomData olddata;
+
+ olddata= *data;
+ olddata.layers= (olddata.layers)? MEM_dupallocN(olddata.layers): NULL;
+ CustomData_free_layer_active(data, type, 0);
+
+ update_data_blocks(em, &olddata, data);
+ if (olddata.layers) MEM_freeN(olddata.layers);
+}
+
+/* ******** EXTRUDE ********* */
+
+static void add_normal_aligned(float *nor, float *add)
+{
+ if( INPR(nor, add) < -0.9999f)
+ VecSubf(nor, nor, add);
+ else
+ VecAddf(nor, nor, add);
+}
+
+static void set_edge_directions_f2(EditMesh *em, int val)
+{
+ EditFace *efa;
+ int do_all= 1;
+
+ /* edge directions are used for extrude, to detect direction of edges that make new faces */
+ /* we have set 'f2' flags in edges that need to get a direction set (e.g. get new face) */
+ /* the val argument differs... so we need it as arg */
+
+ for(efa= em->faces.first; efa; efa= efa->next) {
+ if(efa->f & SELECT) {
+ do_all= 0;
+ if(efa->e1->f2<val) {
+ if(efa->e1->v1 == efa->v1) efa->e1->dir= 0;
+ else efa->e1->dir= 1;
+ }
+ if(efa->e2->f2<val) {
+ if(efa->e2->v1 == efa->v2) efa->e2->dir= 0;
+ else efa->e2->dir= 1;
+ }
+ if(efa->e3->f2<val) {
+ if(efa->e3->v1 == efa->v3) efa->e3->dir= 0;
+ else efa->e3->dir= 1;
+ }
+ if(efa->e4 && efa->e4->f2<val) {
+ if(efa->e4->v1 == efa->v4) efa->e4->dir= 0;
+ else efa->e4->dir= 1;
+ }
+ }
+ }
+ /* ok, no faces done... then we at least set it for exterior edges */
+ if(do_all) {
+ for(efa= em->faces.first; efa; efa= efa->next) {
+ if(efa->e1->v1 == efa->v1) efa->e1->dir= 0;
+ else efa->e1->dir= 1;
+ if(efa->e2->v1 == efa->v2) efa->e2->dir= 0;
+ else efa->e2->dir= 1;
+ if(efa->e3->v1 == efa->v3) efa->e3->dir= 0;
+ else efa->e3->dir= 1;
+ if(efa->e4) {
+ if(efa->e4->v1 == efa->v4) efa->e4->dir= 0;
+ else efa->e4->dir= 1;
+ }
+ }
+ }
+}
+
+/* individual face extrude */
+/* will use vertex normals for extrusion directions, so *nor is unaffected */
+short extrudeflag_face_indiv(EditMesh *em, short flag, float *nor)
+{
+ EditVert *eve, *v1, *v2, *v3, *v4;
+ EditEdge *eed;
+ EditFace *efa, *nextfa;
+
+ if(G.obedit==0 || get_mesh(G.obedit)==0) return 0;
+
+ /* selected edges with 1 or more selected face become faces */
+ /* selected faces each makes new faces */
+ /* always remove old faces, keeps volumes manifold */
+ /* select the new extrusion, deselect old */
+
+ /* step 1; init, count faces in edges */
+ recalc_editnormals(em);
+
+ for(eve= em->verts.first; eve; eve= eve->next) eve->f1= 0; // new select flag
+
+ for(eed= em->edges.first; eed; eed= eed->next) {
+ eed->f2= 0; // amount of unselected faces
+ }
+ for(efa= em->faces.first; efa; efa= efa->next) {
+ if(efa->f & SELECT);
+ else {
+ efa->e1->f2++;
+ efa->e2->f2++;
+ efa->e3->f2++;
+ if(efa->e4) efa->e4->f2++;
+ }
+ }
+
+ /* step 2: make new faces from faces */
+ for(efa= em->faces.last; efa; efa= efa->prev) {
+ if(efa->f & SELECT) {
+ v1= addvertlist(em, efa->v1->co, efa->v1);
+ v2= addvertlist(em, efa->v2->co, efa->v2);
+ v3= addvertlist(em, efa->v3->co, efa->v3);
+
+ v1->f1= v2->f1= v3->f1= 1;
+ VECCOPY(v1->no, efa->n);
+ VECCOPY(v2->no, efa->n);
+ VECCOPY(v3->no, efa->n);
+ if(efa->v4) {
+ v4= addvertlist(em, efa->v4->co, efa->v4);
+ v4->f1= 1;
+ VECCOPY(v4->no, efa->n);
+ }
+ else v4= NULL;
+
+ /* side faces, clockwise */
+ addfacelist(em, efa->v2, v2, v1, efa->v1, efa, NULL);
+ addfacelist(em, efa->v3, v3, v2, efa->v2, efa, NULL);
+ if(efa->v4) {
+ addfacelist(em, efa->v4, v4, v3, efa->v3, efa, NULL);
+ addfacelist(em, efa->v1, v1, v4, efa->v4, efa, NULL);
+ }
+ else {
+ addfacelist(em, efa->v1, v1, v3, efa->v3, efa, NULL);
+ }
+ /* top face */
+ addfacelist(em, v1, v2, v3, v4, efa, NULL);
+ }
+ }
+
+ /* step 3: remove old faces */
+ efa= em->faces.first;
+ while(efa) {
+ nextfa= efa->next;
+ if(efa->f & SELECT) {
+ BLI_remlink(&em->faces, efa);
+ free_editface(em, efa);
+ }
+ efa= nextfa;
+ }
+
+ /* step 4: redo selection */
+ EM_clear_flag_all(em, SELECT);
+
+ for(eve= em->verts.first; eve; eve= eve->next) {
+ if(eve->f1) eve->f |= SELECT;
+ }
+
+ EM_select_flush(em);
+
+ return 'n';
+}
+
+
+/* extrudes individual edges */
+/* nor is filled with constraint vector */
+short extrudeflag_edges_indiv(EditMesh *em, short flag, float *nor)
+{
+ EditVert *eve;
+ EditEdge *eed;
+ EditFace *efa;
+
+ for(eve= em->verts.first; eve; eve= eve->next) eve->tmp.v = NULL;
+ for(eed= em->edges.first; eed; eed= eed->next) {
+ eed->tmp.f = NULL;
+ eed->f2= ((eed->f & flag)!=0);
+ }
+
+ set_edge_directions_f2(em, 2);
+
+ /* sample for next loop */
+ for(efa= em->faces.first; efa; efa= efa->next) {
+ efa->e1->tmp.f = efa;
+ efa->e2->tmp.f = efa;
+ efa->e3->tmp.f = efa;
+ if(efa->e4) efa->e4->tmp.f = efa;
+ }
+ /* make the faces */
+ for(eed= em->edges.first; eed; eed= eed->next) {
+ if(eed->f & flag) {
+ if(eed->v1->tmp.v == NULL)
+ eed->v1->tmp.v = addvertlist(em, eed->v1->co, eed->v1);
+ if(eed->v2->tmp.v == NULL)
+ eed->v2->tmp.v = addvertlist(em, eed->v2->co, eed->v2);
+
+ if(eed->dir==1)
+ addfacelist(em, eed->v1, eed->v2,
+ eed->v2->tmp.v, eed->v1->tmp.v,
+ eed->tmp.f, NULL);
+ else
+ addfacelist(em, eed->v2, eed->v1,
+ eed->v1->tmp.v, eed->v2->tmp.v,
+ eed->tmp.f, NULL);
+
+ /* for transform */
+ if(eed->tmp.f) {
+ efa = eed->tmp.f;
+ if (efa->f & SELECT) add_normal_aligned(nor, efa->n);
+ }
+ }
+ }
+ Normalize(nor);
+
+ /* set correct selection */
+ EM_clear_flag_all(em, SELECT);
+ for(eve= em->verts.last; eve; eve= eve->prev) {
+ if(eve->tmp.v) {
+ eve->tmp.v->f |= flag;
+ }
+ }
+
+ for(eed= em->edges.first; eed; eed= eed->next) {
+ if(eed->v1->f & eed->v2->f & flag) eed->f |= flag;
+ }
+
+ if(nor[0]==0.0 && nor[1]==0.0 && nor[2]==0.0) return 'g'; // g is grab
+ return 'n'; // n is for normal constraint
+}
+
+/* extrudes individual vertices */
+short extrudeflag_verts_indiv(EditMesh *em, short flag, float *nor)
+{
+ EditVert *eve;
+
+ /* make the edges */
+ for(eve= em->verts.first; eve; eve= eve->next) {
+ if(eve->f & flag) {
+ eve->tmp.v = addvertlist(em, eve->co, eve);
+ addedgelist(em, eve, eve->tmp.v, NULL);
+ }
+ else eve->tmp.v = NULL;
+ }
+
+ /* set correct selection */
+ EM_clear_flag_all(em, SELECT);
+
+ for(eve= em->verts.last; eve; eve= eve->prev)
+ if (eve->tmp.v)
+ eve->tmp.v->f |= flag;
+
+ return 'g'; // g is grab
+}
+
+
+/* this is actually a recode of extrudeflag(), using proper edge/face select */
+/* hurms, doesnt use 'flag' yet, but its not called by primitive making stuff anyway */
+static short extrudeflag_edge(EditMesh *em, short flag, float *nor)
+{
+ /* all select edges/faces: extrude */
+ /* old select is cleared, in new ones it is set */
+ EditVert *eve, *nextve;
+ EditEdge *eed, *nexted;
+ EditFace *efa, *nextfa, *efan;
+ short del_old= 0;
+ ModifierData *md;
+
+ if(G.obedit==0 || get_mesh(G.obedit)==0) return 0;
+
+ md = G.obedit->modifiers.first;
+
+ /* selected edges with 0 or 1 selected face become faces */
+ /* selected faces generate new faces */
+
+ /* if *one* selected face has edge with unselected face; remove old selected faces */
+
+ /* if selected edge is not used anymore; remove */
+ /* if selected vertex is not used anymore: remove */
+
+ /* select the new extrusion, deselect old */
+
+
+ /* step 1; init, count faces in edges */
+ recalc_editnormals(em);
+
+ for(eve= em->verts.first; eve; eve= eve->next) {
+ eve->tmp.v = NULL;
+ eve->f1= 0;
+ }
+
+ for(eed= em->edges.first; eed; eed= eed->next) {
+ eed->f1= 0; // amount of unselected faces
+ eed->f2= 0; // amount of selected faces
+ if(eed->f & SELECT) {
+ eed->v1->f1= 1; // we call this 'selected vertex' now
+ eed->v2->f1= 1;
+ }
+ eed->tmp.f = NULL; // here we tuck face pointer, as sample
+ }
+ for(efa= em->faces.first; efa; efa= efa->next) {
+ if(efa->f & SELECT) {
+ efa->e1->f2++;
+ efa->e2->f2++;
+ efa->e3->f2++;
+ if(efa->e4) efa->e4->f2++;
+
+ // sample for next loop
+ efa->e1->tmp.f = efa;
+ efa->e2->tmp.f = efa;
+ efa->e3->tmp.f = efa;
+ if(efa->e4) efa->e4->tmp.f = efa;
+ }
+ else {
+ efa->e1->f1++;
+ efa->e2->f1++;
+ efa->e3->f1++;
+ if(efa->e4) efa->e4->f1++;
+ }
+ }
+
+ /* If a mirror modifier with clipping is on, we need to adjust some
+ * of the cases above to handle edges on the line of symmetry.
+ */
+ for (; md; md=md->next) {
+ if (md->type==eModifierType_Mirror) {
+ MirrorModifierData *mmd = (MirrorModifierData*) md;
+
+ if(mmd->flag & MOD_MIR_CLIPPING) {
+ float mtx[4][4];
+ if (mmd->mirror_ob) {
+ float imtx[4][4];
+ Mat4Invert(imtx, mmd->mirror_ob->obmat);
+ Mat4MulMat4(mtx, G.obedit->obmat, imtx);
+ }
+
+ for (eed= em->edges.first; eed; eed= eed->next) {
+ if(eed->f2 == 1) {
+ float co1[3], co2[3];
+
+ VecCopyf(co1, eed->v1->co);
+ VecCopyf(co2, eed->v2->co);
+
+ if (mmd->mirror_ob) {
+ VecMat4MulVecfl(co1, mtx, co1);
+ VecMat4MulVecfl(co2, mtx, co2);
+ }
+
+ if (mmd->flag & MOD_MIR_AXIS_X)
+ if ( (fabs(co1[0]) < mmd->tolerance) &&
+ (fabs(co2[0]) < mmd->tolerance) )
+ ++eed->f2;
+
+ if (mmd->flag & MOD_MIR_AXIS_Y)
+ if ( (fabs(co1[1]) < mmd->tolerance) &&
+ (fabs(co2[1]) < mmd->tolerance) )
+ ++eed->f2;
+
+ if (mmd->flag & MOD_MIR_AXIS_Z)
+ if ( (fabs(co1[2]) < mmd->tolerance) &&
+ (fabs(co2[2]) < mmd->tolerance) )
+ ++eed->f2;
+ }
+ }
+ }
+ }
+ }
+
+ set_edge_directions_f2(em, 2);
+
+ /* step 1.5: if *one* selected face has edge with unselected face; remove old selected faces */
+ for(efa= em->faces.last; efa; efa= efa->prev) {
+ if(efa->f & SELECT) {
+ if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1 || (efa->e4 && efa->e4->f1)) {
+ del_old= 1;
+ break;
+ }
+ }
+ }
+
+ /* step 2: make new faces from edges */
+ for(eed= em->edges.last; eed; eed= eed->prev) {
+ if(eed->f & SELECT) {
+ if(eed->f2<2) {
+ if(eed->v1->tmp.v == NULL)
+ eed->v1->tmp.v = addvertlist(em, eed->v1->co, eed->v1);
+ if(eed->v2->tmp.v == NULL)
+ eed->v2->tmp.v = addvertlist(em, eed->v2->co, eed->v2);
+
+ /* if del_old, the preferred normal direction is exact
+ * opposite as for keep old faces
+ */
+ if(eed->dir!=del_old)
+ addfacelist(em, eed->v1, eed->v2,
+ eed->v2->tmp.v, eed->v1->tmp.v,
+ eed->tmp.f, NULL);
+ else
+ addfacelist(em, eed->v2, eed->v1,
+ eed->v1->tmp.v, eed->v2->tmp.v,
+ eed->tmp.f, NULL);
+ }
+ }
+ }
+
+ /* step 3: make new faces from faces */
+ for(efa= em->faces.last; efa; efa= efa->prev) {
+ if(efa->f & SELECT) {
+ if (efa->v1->tmp.v == NULL)
+ efa->v1->tmp.v = addvertlist(em, efa->v1->co, efa->v1);
+ if (efa->v2->tmp.v ==NULL)
+ efa->v2->tmp.v = addvertlist(em, efa->v2->co, efa->v2);
+ if (efa->v3->tmp.v ==NULL)
+ efa->v3->tmp.v = addvertlist(em, efa->v3->co, efa->v3);
+ if (efa->v4 && (efa->v4->tmp.v == NULL))
+ efa->v4->tmp.v = addvertlist(em, efa->v4->co, efa->v4);
+
+ if(del_old==0) { // keep old faces means flipping normal
+ if(efa->v4)
+ efan = addfacelist(em, efa->v4->tmp.v, efa->v3->tmp.v,
+ efa->v2->tmp.v, efa->v1->tmp.v, efa, efa);
+ else
+ efan = addfacelist(em, efa->v3->tmp.v, efa->v2->tmp.v,
+ efa->v1->tmp.v, NULL, efa, efa);
+ }
+ else {
+ if(efa->v4)
+ efan = addfacelist(em, efa->v1->tmp.v, efa->v2->tmp.v,
+ efa->v3->tmp.v, efa->v4->tmp.v, efa, efa);
+ else
+ efan = addfacelist(em, efa->v1->tmp.v, efa->v2->tmp.v,
+ efa->v3->tmp.v, NULL, efa, efa);
+ }
+
+ if (em->act_face == efa) {
+ em->act_face = efan;
+ }
+
+ /* for transform */
+ add_normal_aligned(nor, efa->n);
+ }
+ }
+
+ if(del_old) {
+
+ /* step 4: remove old faces, if del_old */
+ efa= em->faces.first;
+ while(efa) {
+ nextfa= efa->next;
+ if(efa->f & SELECT) {
+ BLI_remlink(&em->faces, efa);
+ free_editface(em, efa);
+ }
+ efa= nextfa;
+ }
+
+
+ /* step 5: remove selected unused edges */
+ /* start tagging again */
+ for(eed= em->edges.first; eed; eed= eed->next) eed->f1=0;
+ for(efa= em->faces.first; efa; efa= efa->next) {
+ efa->e1->f1= 1;
+ efa->e2->f1= 1;
+ efa->e3->f1= 1;
+ if(efa->e4) efa->e4->f1= 1;
+ }
+ /* remove */
+ eed= em->edges.first;
+ while(eed) {
+ nexted= eed->next;
+ if(eed->f & SELECT) {
+ if(eed->f1==0) {
+ remedge(em, eed);
+ free_editedge(em, eed);
+ }
+ }
+ eed= nexted;
+ }
+
+ /* step 6: remove selected unused vertices */
+ for(eed= em->edges.first; eed; eed= eed->next)
+ eed->v1->f1= eed->v2->f1= 0;
+
+ eve= em->verts.first;
+ while(eve) {
+ nextve= eve->next;
+ if(eve->f1) {
+ // hack... but we need it for step 7, redoing selection
+ if(eve->tmp.v) eve->tmp.v->tmp.v= eve->tmp.v;
+
+ BLI_remlink(&em->verts, eve);
+ free_editvert(em, eve);
+ }
+ eve= nextve;
+ }
+ }
+
+ Normalize(nor); // translation normal grab
+
+ /* step 7: redo selection */
+ EM_clear_flag_all(em, SELECT);
+
+ for(eve= em->verts.first; eve; eve= eve->next) {
+ if(eve->tmp.v) {
+ eve->tmp.v->f |= SELECT;
+ }
+ }
+
+ EM_select_flush(em);
+
+ if(nor[0]==0.0 && nor[1]==0.0 && nor[2]==0.0) return 'g'; // grab
+ return 'n'; // normal constraint
+}
+
+short extrudeflag_vert(EditMesh *em, short flag, float *nor)
+{
+ /* all verts/edges/faces with (f & 'flag'): extrude */
+ /* from old verts, 'flag' is cleared, in new ones it is set */
+ EditVert *eve, *v1, *v2, *v3, *v4, *nextve;
+ EditEdge *eed, *e1, *e2, *e3, *e4, *nexted;
+ EditFace *efa, *efa2, *nextvl;
+ short sel=0, del_old= 0, is_face_sel=0;
+ ModifierData *md;
+
+ if(G.obedit==0 || get_mesh(G.obedit)==0) return 0;
+
+ md = G.obedit->modifiers.first;
+
+ /* clear vert flag f1, we use this to detect a loose selected vertice */
+ eve= em->verts.first;
+ while(eve) {
+ if(eve->f & flag) eve->f1= 1;
+ else eve->f1= 0;
+ eve= eve->next;
+ }
+ /* clear edges counter flag, if selected we set it at 1 */
+ eed= em->edges.first;
+ while(eed) {
+ if( (eed->v1->f & flag) && (eed->v2->f & flag) ) {
+ eed->f2= 1;
+ eed->v1->f1= 0;
+ eed->v2->f1= 0;
+ }
+ else eed->f2= 0;
+
+ eed->f1= 1; /* this indicates it is an 'old' edge (in this routine we make new ones) */
+ eed->tmp.f = NULL; /* used as sample */
+
+ eed= eed->next;
+ }
+
+ /* we set a flag in all selected faces, and increase the associated edge counters */
+
+ efa= em->faces.first;
+ while(efa) {
+ efa->f1= 0;
+
+ if(faceselectedAND(efa, flag)) {
+ e1= efa->e1;
+ e2= efa->e2;
+ e3= efa->e3;
+ e4= efa->e4;
+
+ if(e1->f2 < 3) e1->f2++;
+ if(e2->f2 < 3) e2->f2++;
+ if(e3->f2 < 3) e3->f2++;
+ if(e4 && e4->f2 < 3) e4->f2++;
+
+ efa->f1= 1;
+ is_face_sel= 1; // for del_old
+ }
+ else if(faceselectedOR(efa, flag)) {
+ e1= efa->e1;
+ e2= efa->e2;
+ e3= efa->e3;
+ e4= efa->e4;
+
+ if( (e1->v1->f & flag) && (e1->v2->f & flag) ) e1->f1= 2;
+ if( (e2->v1->f & flag) && (e2->v2->f & flag) ) e2->f1= 2;
+ if( (e3->v1->f & flag) && (e3->v2->f & flag) ) e3->f1= 2;
+ if( e4 && (e4->v1->f & flag) && (e4->v2->f & flag) ) e4->f1= 2;
+ }
+
+ // sample for next loop
+ efa->e1->tmp.f = efa;
+ efa->e2->tmp.f = efa;
+ efa->e3->tmp.f = efa;
+ if(efa->e4) efa->e4->tmp.f = efa;
+
+ efa= efa->next;
+ }
+
+ set_edge_directions_f2(em, 3);
+
+ /* the current state now is:
+ eve->f1==1: loose selected vertex
+
+ eed->f2==0 : edge is not selected, no extrude
+ eed->f2==1 : edge selected, is not part of a face, extrude
+ eed->f2==2 : edge selected, is part of 1 face, extrude
+ eed->f2==3 : edge selected, is part of more faces, no extrude
+
+ eed->f1==0: new edge
+ eed->f1==1: edge selected, is part of selected face, when eed->f==3: remove
+ eed->f1==2: edge selected, part of a partially selected face
+
+ efa->f1==1 : duplicate this face
+ */
+
+ /* If a mirror modifier with clipping is on, we need to adjust some
+ * of the cases above to handle edges on the line of symmetry.
+ */
+ for (; md; md=md->next) {
+ if (md->type==eModifierType_Mirror) {
+ MirrorModifierData *mmd = (MirrorModifierData*) md;
+
+ if(mmd->flag & MOD_MIR_CLIPPING) {
+ float mtx[4][4];
+ if (mmd->mirror_ob) {
+ float imtx[4][4];
+ Mat4Invert(imtx, mmd->mirror_ob->obmat);
+ Mat4MulMat4(mtx, G.obedit->obmat, imtx);
+ }
+
+ for (eed= em->edges.first; eed; eed= eed->next) {
+ if(eed->f2 == 2) {
+ float co1[3], co2[3];
+
+ VecCopyf(co1, eed->v1->co);
+ VecCopyf(co2, eed->v2->co);
+
+ if (mmd->mirror_ob) {
+ VecMat4MulVecfl(co1, mtx, co1);
+ VecMat4MulVecfl(co2, mtx, co2);
+ }
+
+ if (mmd->flag & MOD_MIR_AXIS_X)
+ if ( (fabs(co1[0]) < mmd->tolerance) &&
+ (fabs(co2[0]) < mmd->tolerance) )
+ ++eed->f2;
+
+ if (mmd->flag & MOD_MIR_AXIS_Y)
+ if ( (fabs(co1[1]) < mmd->tolerance) &&
+ (fabs(co2[1]) < mmd->tolerance) )
+ ++eed->f2;
+ if (mmd->flag & MOD_MIR_AXIS_Z)
+ if ( (fabs(co1[2]) < mmd->tolerance) &&
+ (fabs(co2[2]) < mmd->tolerance) )
+ ++eed->f2;
+ }
+ }
+ }
+ }
+ }
+
+ /* copy all selected vertices, */
+ /* write pointer to new vert in old struct at eve->tmp.v */
+ eve= em->verts.last;
+ while(eve) {
+ eve->f &= ~128; /* clear, for later test for loose verts */
+ if(eve->f & flag) {
+ sel= 1;
+ v1= addvertlist(em, 0, NULL);
+
+ VECCOPY(v1->co, eve->co);
+ v1->f= eve->f;
+ eve->f-= flag;
+ eve->tmp.v = v1;
+ }
+ else eve->tmp.v = 0;
+ eve= eve->prev;
+ }
+
+ if(sel==0) return 0;
+
+ /* all edges with eed->f2==1 or eed->f2==2 become faces */
+
+ /* if del_old==1 then extrude is in partial geometry, to keep it manifold.
+ verts with f1==0 and (eve->f & 128)==0) are removed
+ edges with eed->f2>2 are removed
+ faces with efa->f1 are removed
+ if del_old==0 the extrude creates a volume.
+ */
+
+ /* find if we delete old faces */
+ if(is_face_sel) {
+ for(eed= em->edges.first; eed; eed= eed->next) {
+ if( (eed->f2==1 || eed->f2==2) ) {
+ if(eed->f1==2) {
+ del_old= 1;
+ break;
+ }
+ }
+ }
+ }
+
+ eed= em->edges.last;
+ while(eed) {
+ nexted= eed->prev;
+ if( eed->f2<3) {
+ eed->v1->f |= 128; /* = no loose vert! */
+ eed->v2->f |= 128;
+ }
+ if( (eed->f2==1 || eed->f2==2) ) {
+
+ /* if del_old, the preferred normal direction is exact opposite as for keep old faces */
+ if(eed->dir != del_old)
+ efa2 = addfacelist(em, eed->v1, eed->v2,
+ eed->v2->tmp.v, eed->v1->tmp.v,
+ eed->tmp.f, NULL);
+ else
+ efa2 = addfacelist(em, eed->v2, eed->v1,
+ eed->v1->tmp.v, eed->v2->tmp.v,
+ eed->tmp.f, NULL);
+
+ /* Needs smarter adaption of existing creases.
+ * If addedgelist is used, make sure seams are set to 0 on these
+ * new edges, since we do not want to add any seams on extrusion.
+ */
+ efa2->e1->crease= eed->crease;
+ efa2->e2->crease= eed->crease;
+ efa2->e3->crease= eed->crease;
+ if(efa2->e4) efa2->e4->crease= eed->crease;
+ }
+
+ eed= nexted;
+ }
+ if(del_old) {
+ eed= em->edges.first;
+ while(eed) {
+ nexted= eed->next;
+ if(eed->f2==3 && eed->f1==1) {
+ remedge(em, eed);
+ free_editedge(em, eed);
+ }
+ eed= nexted;
+ }
+ }
+ /* duplicate faces, if necessary remove old ones */
+ efa= em->faces.first;
+ while(efa) {
+ nextvl= efa->next;
+ if(efa->f1 & 1) {
+
+ v1 = efa->v1->tmp.v;
+ v2 = efa->v2->tmp.v;
+ v3 = efa->v3->tmp.v;
+ if(efa->v4)
+ v4 = efa->v4->tmp.v;
+ else
+ v4= 0;
+
+ /* hmm .. not sure about edges here */
+ if(del_old==0) // if we keep old, we flip normal
+ efa2= addfacelist(em, v3, v2, v1, v4, efa, efa);
+ else
+ efa2= addfacelist(em, v1, v2, v3, v4, efa, efa);
+
+ /* for transform */
+ add_normal_aligned(nor, efa->n);
+
+ if(del_old) {
+ BLI_remlink(&em->faces, efa);
+ free_editface(em, efa);
+ }
+ }
+ efa= nextvl;
+ }
+
+ Normalize(nor); // for grab
+
+ /* for all vertices with eve->tmp.v!=0
+ if eve->f1==1: make edge
+ if flag!=128 : if del_old==1: remove
+ */
+ eve= em->verts.last;
+ while(eve) {
+ nextve= eve->prev;
+ if(eve->tmp.v) {
+ if(eve->f1==1) addedgelist(em, eve, eve->tmp.v, NULL);
+ else if( (eve->f & 128)==0) {
+ if(del_old) {
+ BLI_remlink(&em->verts,eve);
+ free_editvert(em, eve);
+ eve= NULL;
+ }
+ }
+ }
+ if(eve) {
+ eve->f &= ~128;
+ }
+ eve= nextve;
+ }
+ // since its vertex select mode now, it also deselects higher order
+ EM_selectmode_flush(em);
+
+ if(nor[0]==0.0 && nor[1]==0.0 && nor[2]==0.0) return 'g'; // g is grab, for correct undo print
+ return 'n';
+}
+
+/* generic extrude */
+short extrudeflag(EditMesh *em, short flag, float *nor)
+{
+ if(em->selectmode & SCE_SELECT_VERTEX)
+ return extrudeflag_vert(em, flag, nor);
+ else
+ return extrudeflag_edge(em, flag, nor);
+
+}
+
+void rotateflag(EditMesh *em, short flag, float *cent, float rotmat[][3])
+{
+ /* all verts with (flag & 'flag') rotate */
+ EditVert *eve;
+
+ eve= em->verts.first;
+ while(eve) {
+ if(eve->f & flag) {
+ eve->co[0]-=cent[0];
+ eve->co[1]-=cent[1];
+ eve->co[2]-=cent[2];
+ Mat3MulVecfl(rotmat,eve->co);
+ eve->co[0]+=cent[0];
+ eve->co[1]+=cent[1];
+ eve->co[2]+=cent[2];
+ }
+ eve= eve->next;
+ }
+}
+
+void translateflag(EditMesh *em, short flag, float *vec)
+{
+ /* all verts with (flag & 'flag') translate */
+ EditVert *eve;
+
+ eve= em->verts.first;
+ while(eve) {
+ if(eve->f & flag) {
+ eve->co[0]+=vec[0];
+ eve->co[1]+=vec[1];
+ eve->co[2]+=vec[2];
+ }
+ eve= eve->next;
+ }
+}
+
+/* helper call for below */
+static EditVert *adduplicate_vertex(EditMesh *em, EditVert *eve, int flag)
+{
+ /* FIXME: copy deformation weight from eve ok here? */
+ EditVert *v1= addvertlist(em, eve->co, eve);
+
+ v1->f= eve->f;
+ eve->f-= flag;
+ eve->f|= 128;
+
+ eve->tmp.v = v1;
+
+ return v1;
+}
+
+/* old selection has flag 128 set, and flag 'flag' cleared
+new selection has flag 'flag' set */
+void adduplicateflag(EditMesh *em, int flag)
+{
+ EditVert *eve, *v1, *v2, *v3, *v4;
+ EditEdge *eed, *newed;
+ EditFace *efa, *newfa, *act_efa = EM_get_actFace(em, 0);
+
+ EM_clear_flag_all(em, 128);
+ EM_selectmode_set(em); // paranoia check, selection now is consistant
+
+ /* vertices first */
+ for(eve= em->verts.last; eve; eve= eve->prev) {
+
+ if(eve->f & flag)
+ adduplicate_vertex(em, eve, flag);
+ else
+ eve->tmp.v = NULL;
+ }
+
+ /* copy edges, note that vertex selection can be independent of edge */
+ for(eed= em->edges.last; eed; eed= eed->prev) {
+ if( eed->f & flag ) {
+ v1 = eed->v1->tmp.v;
+ if(v1==NULL) v1= adduplicate_vertex(em, eed->v1, flag);
+ v2 = eed->v2->tmp.v;
+ if(v2==NULL) v2= adduplicate_vertex(em, eed->v2, flag);
+
+ newed= addedgelist(em, v1, v2, eed);
+
+ newed->f= eed->f;
+ eed->f -= flag;
+ eed->f |= 128;
+ }
+ }
+
+ /* then duplicate faces, again create new vertices if needed */
+ for(efa= em->faces.last; efa; efa= efa->prev) {
+ if(efa->f & flag) {
+ v1 = efa->v1->tmp.v;
+ if(v1==NULL) v1= adduplicate_vertex(em, efa->v1, flag);
+ v2 = efa->v2->tmp.v;
+ if(v2==NULL) v2= adduplicate_vertex(em, efa->v2, flag);
+ v3 = efa->v3->tmp.v;
+ if(v3==NULL) v3= adduplicate_vertex(em, efa->v3, flag);
+ if(efa->v4) {
+ v4 = efa->v4->tmp.v;
+ if(v4==NULL) v4= adduplicate_vertex(em, efa->v4, flag);
+ }
+ else v4= NULL;
+
+ newfa= addfacelist(em, v1, v2, v3, v4, efa, efa);
+
+ if (efa==act_efa) {
+ EM_set_actFace(em, newfa);
+ }
+
+ newfa->f= efa->f;
+ efa->f -= flag;
+ efa->f |= 128;
+ }
+ }
+
+ EM_fgon_flags(em); // redo flags and indices for fgons
+}
+
+void delfaceflag(EditMesh *em, int flag)
+{
+ /* delete all faces with 'flag', including loose edges and loose vertices */
+ /* this is maybe a bit weird, but this function is used for 'split' and 'separate' */
+ /* in remaining vertices/edges 'flag' is cleared */
+ EditVert *eve,*nextve;
+ EditEdge *eed, *nexted;
+ EditFace *efa,*nextvl;
+
+ /* to detect loose edges, we put f2 flag on 1 */
+ for(eed= em->edges.first; eed; eed= eed->next) {
+ if(eed->f & flag) eed->f2= 1;
+ else eed->f2= 0;
+ }
+
+ /* delete faces */
+ efa= em->faces.first;
+ while(efa) {
+ nextvl= efa->next;
+ if(efa->f & flag) {
+
+ efa->e1->f2= 1;
+ efa->e2->f2= 1;
+ efa->e3->f2= 1;
+ if(efa->e4) {
+ efa->e4->f2= 1;
+ }
+
+ BLI_remlink(&em->faces, efa);
+ free_editface(em, efa);
+ }
+ efa= nextvl;
+ }
+
+ /* all remaining faces: make sure we keep the edges */
+ for(efa= em->faces.first; efa; efa= efa->next) {
+ efa->e1->f2= 0;
+ efa->e2->f2= 0;
+ efa->e3->f2= 0;
+ if(efa->e4) {
+ efa->e4->f2= 0;
+ }
+ }
+
+ /* remove tagged edges, and clear remaining ones */
+ eed= em->edges.first;
+ while(eed) {
+ nexted= eed->next;
+
+ if(eed->f2==1) {
+ remedge(em, eed);
+ free_editedge(em, eed);
+ }
+ else {
+ eed->f &= ~flag;
+ eed->v1->f &= ~flag;
+ eed->v2->f &= ~flag;
+ }
+ eed= nexted;
+ }
+
+ /* vertices with 'flag' now are the loose ones, and will be removed */
+ eve= em->verts.first;
+ while(eve) {
+ nextve= eve->next;
+ if(eve->f & flag) {
+ BLI_remlink(&em->verts, eve);
+ free_editvert(em, eve);
+ }
+ eve= nextve;
+ }
+
+}
+
+/* ********************* */
+#if 0
+static int check_vnormal_flip(float *n, float *vnorm)
+{
+ float inp;
+
+ inp= n[0]*vnorm[0]+n[1]*vnorm[1]+n[2]*vnorm[2];
+
+ /* angles 90 degrees: dont flip */
+ if(inp> -0.000001) return 0;
+
+ return 1;
+}
+#endif
+
+void flipface(EditMesh *em, EditFace *efa)
+{
+ if(efa->v4) {
+ SWAP(EditVert *, efa->v2, efa->v4);
+ SWAP(EditEdge *, efa->e1, efa->e4);
+ SWAP(EditEdge *, efa->e2, efa->e3);
+ EM_data_interp_from_faces(em, efa, NULL, efa, 0, 3, 2, 1);
+ }
+ else {
+ SWAP(EditVert *, efa->v2, efa->v3);
+ SWAP(EditEdge *, efa->e1, efa->e3);
+ efa->e2->dir= 1-efa->e2->dir;
+ EM_data_interp_from_faces(em, efa, NULL, efa, 0, 2, 1, 3);
+ }
+
+ if(efa->v4) CalcNormFloat4(efa->v1->co, efa->v2->co, efa->v3->co, efa->v4->co, efa->n);
+ else CalcNormFloat(efa->v1->co, efa->v2->co, efa->v3->co, efa->n);
+}
+
+
+void flip_editnormals(EditMesh *em)
+{
+ EditFace *efa;
+
+ efa= em->faces.first;
+ while(efa) {
+ if( efa->f & SELECT ){
+ flipface(em, efa);
+ }
+ efa= efa->next;
+ }
+
+ /* update vertex normals too */
+ recalc_editnormals(em);
+
+}
+
+/* does face centers too */
+void recalc_editnormals(EditMesh *em)
+{
+ EditFace *efa;
+ EditVert *eve;
+
+ for(eve= em->verts.first; eve; eve=eve->next) {
+ eve->no[0] = eve->no[1] = eve->no[2] = 0.0;
+ }
+
+ for(efa= em->faces.first; efa; efa=efa->next) {
+ if(efa->v4) {
+ CalcNormFloat4(efa->v1->co, efa->v2->co, efa->v3->co, efa->v4->co, efa->n);
+ CalcCent4f(efa->cent, efa->v1->co, efa->v2->co, efa->v3->co, efa->v4->co);
+ VecAddf(efa->v4->no, efa->v4->no, efa->n);
+ }
+ else {
+ CalcNormFloat(efa->v1->co, efa->v2->co, efa->v3->co, efa->n);
+ CalcCent3f(efa->cent, efa->v1->co, efa->v2->co, efa->v3->co);
+ }
+ VecAddf(efa->v1->no, efa->v1->no, efa->n);
+ VecAddf(efa->v2->no, efa->v2->no, efa->n);
+ VecAddf(efa->v3->no, efa->v3->no, efa->n);
+ }
+
+ /* following Mesh convention; we use vertex coordinate itself for normal in this case */
+ for(eve= em->verts.first; eve; eve=eve->next) {
+ if (Normalize(eve->no)==0.0) {
+ VECCOPY(eve->no, eve->co);
+ Normalize(eve->no);
+ }
+ }
+}
+
+int compareface(EditFace *vl1, EditFace *vl2)
+{
+ EditVert *v1, *v2, *v3, *v4;
+
+ if(vl1->v4 && vl2->v4) {
+ v1= vl2->v1;
+ v2= vl2->v2;
+ v3= vl2->v3;
+ v4= vl2->v4;
+
+ if(vl1->v1==v1 || vl1->v2==v1 || vl1->v3==v1 || vl1->v4==v1) {
+ if(vl1->v1==v2 || vl1->v2==v2 || vl1->v3==v2 || vl1->v4==v2) {
+ if(vl1->v1==v3 || vl1->v2==v3 || vl1->v3==v3 || vl1->v4==v3) {
+ if(vl1->v1==v4 || vl1->v2==v4 || vl1->v3==v4 || vl1->v4==v4) {
+ return 1;
+ }
+ }
+ }
+ }
+ }
+ else if(vl1->v4==0 && vl2->v4==0) {
+ v1= vl2->v1;
+ v2= vl2->v2;
+ v3= vl2->v3;
+
+ if(vl1->v1==v1 || vl1->v2==v1 || vl1->v3==v1) {
+ if(vl1->v1==v2 || vl1->v2==v2 || vl1->v3==v2) {
+ if(vl1->v1==v3 || vl1->v2==v3 || vl1->v3==v3) {
+ return 1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* checks for existance, not tria overlapping inside quad */
+EditFace *exist_face(EditMesh *em, EditVert *v1, EditVert *v2, EditVert *v3, EditVert *v4)
+{
+ EditFace *efa, efatest;
+
+ efatest.v1= v1;
+ efatest.v2= v2;
+ efatest.v3= v3;
+ efatest.v4= v4;
+
+ efa= em->faces.first;
+ while(efa) {
+ if(compareface(&efatest, efa)) return efa;
+ efa= efa->next;
+ }
+ return NULL;
+}
+
+/* evaluate if entire quad is a proper convex quad */
+int convex(float *v1, float *v2, float *v3, float *v4)
+{
+ float nor[3], nor1[3], nor2[3], vec[4][2];
+
+ /* define projection, do both trias apart, quad is undefined! */
+ CalcNormFloat(v1, v2, v3, nor1);
+ CalcNormFloat(v1, v3, v4, nor2);
+ nor[0]= ABS(nor1[0]) + ABS(nor2[0]);
+ nor[1]= ABS(nor1[1]) + ABS(nor2[1]);
+ nor[2]= ABS(nor1[2]) + ABS(nor2[2]);
+
+ if(nor[2] >= nor[0] && nor[2] >= nor[1]) {
+ vec[0][0]= v1[0]; vec[0][1]= v1[1];
+ vec[1][0]= v2[0]; vec[1][1]= v2[1];
+ vec[2][0]= v3[0]; vec[2][1]= v3[1];
+ vec[3][0]= v4[0]; vec[3][1]= v4[1];
+ }
+ else if(nor[1] >= nor[0] && nor[1]>= nor[2]) {
+ vec[0][0]= v1[0]; vec[0][1]= v1[2];
+ vec[1][0]= v2[0]; vec[1][1]= v2[2];
+ vec[2][0]= v3[0]; vec[2][1]= v3[2];
+ vec[3][0]= v4[0]; vec[3][1]= v4[2];
+ }
+ else {
+ vec[0][0]= v1[1]; vec[0][1]= v1[2];
+ vec[1][0]= v2[1]; vec[1][1]= v2[2];
+ vec[2][0]= v3[1]; vec[2][1]= v3[2];
+ vec[3][0]= v4[1]; vec[3][1]= v4[2];
+ }
+
+ /* linetests, the 2 diagonals have to instersect to be convex */
+ if( IsectLL2Df(vec[0], vec[2], vec[1], vec[3]) > 0 ) return 1;
+ return 0;
+}
+
+
+/* ********************* Fake Polgon support (FGon) ***************** */
+
+
+/* results in:
+ - faces having ->fgonf flag set (also for draw)
+ - edges having ->fgoni index set (for select)
+*/
+
+float EM_face_area(EditFace *efa)
+{
+ if(efa->v4) return AreaQ3Dfl(efa->v1->co, efa->v2->co, efa->v3->co, efa->v4->co);
+ else return AreaT3Dfl(efa->v1->co, efa->v2->co, efa->v3->co);
+}
+
+float EM_face_perimeter(EditFace *efa)
+{
+ if(efa->v4) return
+ VecLenf(efa->v1->co, efa->v2->co)+
+ VecLenf(efa->v2->co, efa->v3->co)+
+ VecLenf(efa->v3->co, efa->v4->co)+
+ VecLenf(efa->v4->co, efa->v1->co);
+
+ else return
+ VecLenf(efa->v1->co, efa->v2->co)+
+ VecLenf(efa->v2->co, efa->v3->co)+
+ VecLenf(efa->v3->co, efa->v1->co);
+}
+
+void EM_fgon_flags(EditMesh *em)
+{
+ EditFace *efa, *efan, *efamax;
+ EditEdge *eed;
+ ListBase listb={NULL, NULL};
+ float size, maxsize;
+ short done, curindex= 1;
+
+ // for each face with fgon edge AND not fgon flag set
+ for(eed= em->edges.first; eed; eed= eed->next) eed->fgoni= 0; // index
+ for(efa= em->faces.first; efa; efa= efa->next) efa->fgonf= 0; // flag
+
+ // for speed & simplicity, put fgon face candidates in new listbase
+ efa= em->faces.first;
+ while(efa) {
+ efan= efa->next;
+ if( (efa->e1->h & EM_FGON) || (efa->e2->h & EM_FGON) ||
+ (efa->e3->h & EM_FGON) || (efa->e4 && (efa->e4->h & EM_FGON)) ) {
+ BLI_remlink(&em->faces, efa);
+ BLI_addtail(&listb, efa);
+ }
+ efa= efan;
+ }
+
+ // find an undone face with fgon edge
+ for(efa= listb.first; efa; efa= efa->next) {
+ if(efa->fgonf==0) {
+
+ // init this face
+ efa->fgonf= EM_FGON;
+ if(efa->e1->h & EM_FGON) efa->e1->fgoni= curindex;
+ if(efa->e2->h & EM_FGON) efa->e2->fgoni= curindex;
+ if(efa->e3->h & EM_FGON) efa->e3->fgoni= curindex;
+ if(efa->e4 && (efa->e4->h & EM_FGON)) efa->e4->fgoni= curindex;
+
+ // we search for largest face, to give facedot drawing rights
+ maxsize= EM_face_area(efa);
+ efamax= efa;
+
+ // now flush curendex over edges and set faceflags
+ done= 1;
+ while(done==1) {
+ done= 0;
+
+ for(efan= listb.first; efan; efan= efan->next) {
+ if(efan->fgonf==0) {
+ // if one if its edges has index set, do other too
+ if( (efan->e1->fgoni==curindex) || (efan->e2->fgoni==curindex) ||
+ (efan->e3->fgoni==curindex) || (efan->e4 && (efan->e4->fgoni==curindex)) ) {
+
+ efan->fgonf= EM_FGON;
+ if(efan->e1->h & EM_FGON) efan->e1->fgoni= curindex;
+ if(efan->e2->h & EM_FGON) efan->e2->fgoni= curindex;
+ if(efan->e3->h & EM_FGON) efan->e3->fgoni= curindex;
+ if(efan->e4 && (efan->e4->h & EM_FGON)) efan->e4->fgoni= curindex;
+
+ size= EM_face_area(efan);
+ if(size>maxsize) {
+ efamax= efan;
+ maxsize= size;
+ }
+ done= 1;
+ }
+ }
+ }
+ }
+
+ efamax->fgonf |= EM_FGON_DRAW;
+ curindex++;
+
+ }
+ }
+
+ // put fgon face candidates back in listbase
+ efa= listb.first;
+ while(efa) {
+ efan= efa->next;
+ BLI_remlink(&listb, efa);
+ BLI_addtail(&em->faces, efa);
+ efa= efan;
+ }
+
+ // remove fgon flags when edge not in fgon (anymore)
+ for(eed= em->edges.first; eed; eed= eed->next) {
+ if(eed->fgoni==0) eed->h &= ~EM_FGON;
+ }
+
+}
+
+/* editmesh vertmap, copied from intern.mesh.c
+ * if do_face_idx_array is 0 it means we need to run it as well as freeing
+ * */
+
+UvVertMap *make_uv_vert_map_EM(EditMesh *em, int selected, int do_face_idx_array, float *limit)
+{
+ EditVert *ev;
+ EditFace *efa;
+ int totverts;
+
+ /* vars from original func */
+ UvVertMap *vmap;
+ UvMapVert *buf;
+ MTFace *tf;
+ unsigned int a;
+ int i, totuv, nverts;
+
+ if (do_face_idx_array)
+ EM_init_index_arrays(em, 0, 0, 1);
+
+ /* we need the vert */
+ for (ev= em->verts.first, totverts=0; ev; ev= ev->next, totverts++) {
+ ev->tmp.l = totverts;
+ }
+
+ totuv = 0;
+
+ /* generate UvMapVert array */
+ for (efa= em->faces.first; efa; efa= efa->next)
+ if(!selected || ((!efa->h) && (efa->f & SELECT)))
+ totuv += (efa->v4)? 4: 3;
+
+ if(totuv==0) {
+ if (do_face_idx_array)
+ EM_free_index_arrays();
+ return NULL;
+ }
+ vmap= (UvVertMap*)MEM_callocN(sizeof(*vmap), "UvVertMap");
+ if (!vmap) {
+ if (do_face_idx_array)
+ EM_free_index_arrays();
+ return NULL;
+ }
+
+ vmap->vert= (UvMapVert**)MEM_callocN(sizeof(*vmap->vert)*totverts, "UvMapVert*");
+ buf= vmap->buf= (UvMapVert*)MEM_callocN(sizeof(*vmap->buf)*totuv, "UvMapVert");
+
+ if (!vmap->vert || !vmap->buf) {
+ free_uv_vert_map(vmap);
+ if (do_face_idx_array)
+ EM_free_index_arrays();
+ return NULL;
+ }
+
+ for (a=0, efa= em->faces.first; efa; a++, efa= efa->next) {
+ if(!selected || ((!efa->h) && (efa->f & SELECT))) {
+ nverts= (efa->v4)? 4: 3;
+
+ for(i=0; i<nverts; i++) {
+ buf->tfindex= i;
+ buf->f= a;
+ buf->separate = 0;
+
+ buf->next= vmap->vert[(*(&efa->v1 + i))->tmp.l];
+ vmap->vert[(*(&efa->v1 + i))->tmp.l]= buf;
+
+ buf++;
+ }
+ }
+ }
+
+ /* sort individual uvs for each vert */
+ for(a=0, ev=em->verts.first; ev; a++, ev= ev->next) {
+ UvMapVert *newvlist= NULL, *vlist=vmap->vert[a];
+ UvMapVert *iterv, *v, *lastv, *next;
+ float *uv, *uv2, uvdiff[2];
+
+ while(vlist) {
+ v= vlist;
+ vlist= vlist->next;
+ v->next= newvlist;
+ newvlist= v;
+
+ efa = EM_get_face_for_index(v->f);
+ tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
+ uv = tf->uv[v->tfindex];
+
+ lastv= NULL;
+ iterv= vlist;
+
+ while(iterv) {
+ next= iterv->next;
+ efa = EM_get_face_for_index(iterv->f);
+ tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE);
+ uv2 = tf->uv[iterv->tfindex];
+
+ Vec2Subf(uvdiff, uv2, uv);
+
+ if(fabs(uv[0]-uv2[0]) < limit[0] && fabs(uv[1]-uv2[1]) < limit[1]) {
+ if(lastv) lastv->next= next;
+ else vlist= next;
+ iterv->next= newvlist;
+ newvlist= iterv;
+ }
+ else
+ lastv=iterv;
+
+ iterv= next;
+ }
+
+ newvlist->separate = 1;
+ }
+
+ vmap->vert[a]= newvlist;
+ }
+
+ if (do_face_idx_array)
+ EM_free_index_arrays();
+
+ return vmap;
+}
+
+UvMapVert *get_uv_map_vert_EM(UvVertMap *vmap, unsigned int v)
+{
+ return vmap->vert[v];
+}
+
+void free_uv_vert_map_EM(UvVertMap *vmap)
+{
+ if (vmap) {
+ if (vmap->vert) MEM_freeN(vmap->vert);
+ if (vmap->buf) MEM_freeN(vmap->buf);
+ MEM_freeN(vmap);
+ }
+}
+